最近出了个故障,有个接口的请求居然出现了长达几十秒的处理时间,由于日志缺乏,网络故障也解除了,就没法再重现这个故障了。为了可以在下次出现问题的时候能追查到问题,所以需要添加一些追踪日志。
添加这些追踪日志,我希望能够达到如下几点:

1、只有请求超过一定时间才记录,不然请求太多,系统扛不住

2、添加的代码可以尽量的少

3、对接口的影响尽量小,比如不影响实际时延,甚至记录日志时出现了错误,也不影响系统正常运行

openresty这套工具,可以在nginx处理请求的每一个阶段介入,编写代码进行逻辑处理。其可介入的流程如下图:

log Phase这个阶段,就是openresty能处理的最后阶段。到这个阶段的时候,实际上请求的响应已经发送给客户端了。所以使用 log_by_lua (知乎真特么蛋疼啊,左右下划线就自动斜体,还没提供转义功能)

log Phase这个阶段,就是openresty能处理的最后阶段。到这个阶段的时候,实际上请求的响应已经发送给客户端了。另外我也测试过了,即使在这个阶段发生了错误,如 io 错误,也不会影响接口的正常响应,所以使用 log_by_lua 很是符合需求。

好处不止如此, log_by_lua是一个请求的最后处理阶段,那么只要请求正常进行,比如会走到这一步,因此,在这一步,我们就知道了这个请求的耗时了。另外,则是我们的代码里有不少的 ngx.exit ,如果是在业务逻辑处理的时候就记录日志,那么每个出现 ngx.exit 的地方,都需要插入写日志到硬盘的操作,大大增加了代码量。

写日志到硬盘的这一步操作,可以在 log_by_lua 这个阶段来完成,剩下的另一个问题就是每一步记录的日志如何传递到 log_by_lua 这一阶段来了。

我处理的方式是使用ngx.ctx, 每一个请求,都会有自己独立的 ngx.ctx, 这个 ngx.ctx 会贯穿整个请求的始终,简单的log函数如下:

logger.lua
--------------------------
local _M = {}function _M.log(format, ...)if ngx.ctx.log_slot == nil thenngx.ctx.log_slot = {}endarg = {...}local logstr = ""if arg == nil thenlogstr = formatelselogstr = string.format(format, unpack(arg))endlogstr = logstr .. "\t" .. ngx.now()table.insert(ngx.ctx.log_slot, logstr)
endreturn _M

到了 log_by_lua 阶段要把追踪日志写入到硬盘里,处理代码如下:

log_slot.lua
---------------------
local request_time = ngx.var.request_time
if request_time < 1 thenreturn  --- 小于1秒的请求不记录
end
local slot = ngx.ctx.log_slot
if slot == nil or type(slot) ~= "table" thenreturn
end
local logs = table.concat(slot, "\n")
local f = assert(io.open("/logs/trace", "a"))
f:write(logs .. "\n")
f:close()

log_by_lua 可以用在 http 模块,也可以用在server模块,也能直接精确到location模块,即只到某个请求。所以你可以在nginx.conf 里的http里添加:

http{log_by_lua_file '/code/log_slot.lua';
}

也可以在server的配置里添加:

server {log_by_lua_file '/code/log_slot.lua';
}

更能直接在某个接口里添加:

/v1/test {content_by_lua_file '/code/v1/test.lua';log_by_lua_file '/code/log_slot.lua';
}

http里添加,则对所有的server; server里添加,则只针对此server;location里添加,就只针对这个接口。

但是,比较坑爹的是,log_by_lua 不像 access log,可以多层级使用。log_by_lua 在某层使用了之后,上层的 log_by_lua 就对此一层无效了。比如 /v1/test 接口添加了 log_by_lua, 那么 http 或者 server 里添加的 log_by_lua 在接受/v1/test接口的请求时都不会被用到。

正是因为这个坑,浪费了我不少的时间来解决。我们的系统里,http 模块是配置了 log_by_lua 的,用来做接口监控,监控返回的错误码,处理的时延等。如果我在 /v1/test 里添加了只针对 /v1/test 的追踪日志,那么接口监控就无法正常运行了。

不过天无绝人之路,我想到了一个处理方法如下:

monitor_log.lua
---------------------
local _M = {}function _M.monitor_log()local f = _M.api_monitor_log_funcif f == nil thenf, err = loadfile("/code/monitor.lua")if f == nil thenngx.log(ngx.ERR, "/code/monitor.lua, ", err)--- 如果不存在接口监控,直接给一个空函数f = function() endend_M.api_monitor_log_func = fendlocal status, err = pcall(f)if not status thenngx.log(ngx.ERR, "run api monitor /code/monitor.lua failed", err)end
endreturn _M

修改log_slot.lua代码如下:

local logger = require "code.monitor"
local request_time = ngx.var.request_time
logger.monitor_log()
if request_time < 1 thenreturn  --- 小于1秒的请求不记录
end
local slot = ngx.ctx.log_slot
if slot == nil or type(slot) ~= "table" thenreturn
end
local logs = table.concat(slot, "\n")
local f = assert(io.open("/logs/trace", "a"))
f:write(logs .. "\n")
f:close()

如上,就可以进行其他层级的 log_by_lua 代码运行了,皆大欢喜,问题解决了。
当系统并发请求较低的时候,worker够用,则使用 log_by_lua 可以说是毫无坏处。当然,一旦 log_by_lua 出现了故障,如死循环,则会长时间占用worker,造成整个系统崩溃掉。

openresty 日志输出的处理相关推荐

  1. Python之向日志输出中添加上下文信息

    除了传递给日志记录函数的参数(如msg)外,有时候我们还想在日志输出中包含一些额外的上下文信息.比如,在一个网络应用中,可能希望在日志中记录客户端的特定信息,如:远程客户端的IP地址和用户名.这里我们 ...

  2. python日志输出到屏幕,python日志写入文件

    python日志输出到屏幕,python日志写入文件 日志 日志是跟踪软件运行时所发生的事件的一种方法.软件开发者在代码中调用日志函数,表明发生了特定的事件.事件由描述性消息描述,该描述性消息可以可选 ...

  3. 基于单例模式的日志输出(C++)

    原文:https://blog.csdn.net/giser_mxd/article/details/75557410 在软件运行中,系统一般会采用一个持久化的日志系统来记录运行情况.本题你需要实现一 ...

  4. Srping Boot日志输出(转)

    说明:其实经过研究,在最新版本的Spring Boot中默认使用的是logback进行日志输出,其余的都没有引入.但是网上的教程说只要按照下面的文件列表引入对应的配置文件就会进行输出,这个没有实践过, ...

  5. iOS根据Debug和Release状态的变化来屏蔽日志输出

    今天在这里分享一个很实用的小技巧. 我们平时在开发应用的时候,经常会用到NSLog来调试我们的程序,而随着项目越来越大,这些用于调试的日志输出就会变得很难管理. 我们在发布正式版的时候一定要屏蔽掉所有 ...

  6. android tag 快捷_Android Studio快捷键生成TAG、Log.x日志输出介绍

    生成TAG logt+Tab键: private static final String TAG = "Extract"; 生成Log.d() logd+Tab键: Log.d(T ...

  7. 【原创】日志输出到串口设备导致的问题

    2019独角兽企业重金招聘Python工程师标准>>> 问题场景:       测试人员报告,业务 modb 作为 RabbitMQ 的消费者,消费消息的速度非常慢,慢到大约每秒 2 ...

  8. Mybatis指定日志输出实现

    10             Mybatis指定日志输出实现 在程序开发过程中,为了调试方便.了解程序的运行过程,进行必要的日志输出总是免不了的.对于使用Mybatis而言,我们常见的需求是希望可以在 ...

  9. FFMPEG 日志输出控制

    原文:https://blog.csdn.net/tea1896/article/details/48310579 1. FFMPEG 编码速度很慢,发现其中打印很多.因为打印是一个执行比较慢的动作, ...

最新文章

  1. 一款低延迟的分布式数据库同步系统--databus
  2. 关于被代理的bean的注入的问题
  3. JAVA不借助第三个变量实现两个变量交换的思考
  4. matlab潮流程序,IEEE33节点matlab潮流程序.doc
  5. 反编译apk文件教程(查看java代码篇)
  6. 域电脑不能显示桌面_学会这些电脑操作,工作更高效,摸鱼更舒适!
  7. PaddlePaddle训练营——公开课——AI核心技术掌握——第1章迈入现代人工智能的大门——多层感知机网络模型
  8. 数据库无法保存中文的解决
  9. Will not attempt to authenticate using SASL | dubbo项目启动特别慢,拉取 zookeeper 服务日志打印特别慢
  10. 初中位似图形作图_[如何画位似图形] 位似图形的画法及步骤
  11. rust矿洞绳子怎么爬下_rust矿洞绳子怎么爬下_打工小伙爬冰救人,每动一下都能听到冰面碎裂声...
  12. Tyvj-Begin P1029 Begin1 - Unit6 - 幼稚的把戏
  13. 转:zTree树控件入门之checkbox:如何动态设置节点的checkbox选择框启用与禁用状态(chkDisabled)...
  14. 百度前员工因内网发布“女优一览表”被辞退,自诉原因:想转岗鉴黄师.........
  15. python图片内容长度识别_python 图片中的表格识别
  16. 微信号、微信公众号、微信开放平台、微信商户、微信支付、移动应用申请流程
  17. AddressBook获取用户信息
  18. Qdata模块-python获取关键词百度指数
  19. 传感器响应时间与滤波器截止频率的关系
  20. RV1126笔记二十三:Nginx及cgi移植

热门文章

  1. fmax()函数以及C ++中的示例
  2. FreeRTOS--堆内存管理(二)
  3. linux网络编程Internet Socket地址,套接字,和函数
  4. linux之task_struct
  5. C++单例模式简单实现
  6. TCP第四次挥手为什么要等待2MSL
  7. 单向循环链表C语言实现
  8. 关于NOR FLASH地址左右移的问题
  9. 【精华文】C语言结构体特殊情况分析:结构体指针 / 基本数据类型指针,指向其他结构体
  10. Array | 74. Search a 2D Matrix