openresty 日志输出的处理

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

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,造成整个系统崩溃掉。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/390216.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

谁是赢家_赢家的真正作品是股东

谁是赢家As I wrote in the article “5 Skills to Look For When Hiring Remote Talent,” remote work is a fast emerging segment of the labor market. Today roughly eight million Americans work remotely full-time. And among the most commonly held jobs include m…

博客园代码黑色主题高亮设置

参考链接&#xff1a; https://segmentfault.com/a/1190000013001367 先发链接&#xff0c;有空实践后会整理。我的GitHub地址&#xff1a;https://github.com/heizemingjun我的博客园地址&#xff1a;http://www.cnblogs.com/chenmingjun我的蚂蚁笔记博客地址&#xff1a;http…

Matplotlib课程–学习Python数据可视化

Learn the basics of Matplotlib in this crash course tutorial. Matplotlib is an amazing data visualization library for Python. You will also learn how to apply Matplotlib to real-world problems.在此速成班教程中学习Matplotlib的基础知识。 Matplotlib是一个很棒…

Android 开发使用 Gradle 配置构建库模块的工作方式

Android 开发过程中&#xff0c;我们不可避免地需要引入其他人的工作成果。减少重复“造轮子”的时间&#xff0c;投入到更有意义的核心任务当中。Android 库模块在结构上与 Android 应用模块相同。提供构建应用所需的一切内容&#xff0c;包括源代码&#xff08;src&#xff0…

vue 组件库发布_如何创建和发布Vue组件库

vue 组件库发布Component libraries are all the rage these days. They make it easy to maintain a consistent look and feel across an application. 如今&#xff0c;组件库风行一时。 它们使在整个应用程序中保持一致的外观和感觉变得容易。 Ive used a variety of diff…

angular

<input type"file" id"one-input" accept"image/*" file-model"images" οnchange"angular.element(this).scope().img_upload(this.files)"/>转载于:https://www.cnblogs.com/loweringye/p/8441437.html

Java网络编程 — Netty入门

认识Netty Netty简介 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty is a NIO client server framework which enables quick and easy development o…

har文件分析http_如何使用HAR文件分析一段时间内的性能

har文件分析httpWhen I consider the performance of a website, several things come to mind. I think about looking at the requests of a page, understanding what resources are being loaded, and how long these resources take to be available to users.当我考虑网站…

第一阶段:前端开发_Mysql——表与表之间的关系

2018-06-26 表与表之间的关系 一、一对多关系&#xff1a; 常见实例&#xff1a;分类和商品&#xff0c;部门和员工一对多建表原则&#xff1a;在从表&#xff08;多方&#xff09;创建一个字段&#xff0c;字段作为外键指向主表&#xff08;一方&#xff09;的一方      …

按钮提交在url后添加字段_在输入字段上定向单击“清除”按钮(X)

按钮提交在url后添加字段jQuery makes it easy to get your project up and running. Though its fallen out of favor in recent years, its still worth learning the basics, especially if you want quick access to its powerful methods.jQuery使您可以轻松启动和运行项目…

429. N 叉树的层序遍历

429. N 叉树的层序遍历 给定一个 N 叉树&#xff0c;返回其节点值的层序遍历。&#xff08;即从左到右&#xff0c;逐层遍历&#xff09;。 树的序列化输入是用层序遍历&#xff0c;每组子节点都由 null 值分隔&#xff08;参见示例&#xff09;。 - 示例 1&#xff1a;输入…

javascript如何阻止事件冒泡和默认行为

阻止冒泡&#xff1a; 冒泡简单的举例来说&#xff0c;儿子知道了一个秘密消息&#xff0c;它告诉了爸爸&#xff0c;爸爸知道了又告诉了爷爷&#xff0c;一级级传递从而以引起事件的混乱&#xff0c;而阻止冒泡就是不让儿子告诉爸爸&#xff0c;爸爸自然不会告诉爷爷。下面的d…

89. Gray Code - LeetCode

为什么80%的码农都做不了架构师&#xff1f;>>> Question 89. Gray Code Solution 思路&#xff1a; n 0 0 n 1 0 1 n 2 00 01 10 11 n 3 000 001 010 011 100 101 110 111 Java实现&#xff1a; public List<Integer> grayCode(int n) {List&…

400. 第 N 位数字

400. 第 N 位数字 在无限的整数序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, …中找到第 n 位数字。 注意&#xff1a;n 是正数且在 32 位整数范围内&#xff08;n < 231&#xff09;。 示例 1&#xff1a; 输入&#xff1a;3 输出&#xff1a;3 示例 2&#xff1a; 输入&…

1.初识Linux

1.Linux 区分大小写 2.shell命令行-bash 进入终端->[stulocalhost~]$ (其中,Stu为登录用户名&#xff0c;localhost为登录主机名&#xff0c;’~’ 表示当前用户正处在stu用户的家目录中, 普通用户的提示符以$结尾&#xff0c;而根用户以’#’结尾) 3.Linux中所谓的命令(…

这份NLP研究进展汇总请收好,GitHub连续3天最火的都是它

最近&#xff0c;有一份自然语言处理 (NLP) 进展合辑&#xff0c;一发布就受到了同性交友网站用户的疯狂标星&#xff0c;已经连续3天高居GitHub热门榜首位。 合集里面包括&#xff0c;20多种NLP任务前赴后继的研究成果&#xff0c;以及用到的数据集。 这是来自爱尔兰的Sebasti…

基于模型的嵌入式开发流程_如何使用基于模型的测试来改善工作流程

基于模型的嵌入式开发流程Unit testing is not enough – so lets start using model-based testing to improve our workflows.单元测试还不够–因此&#xff0c;让我们开始使用基于模型的测试来改善我们的工作流程。 Software testing is an important phase in building a …

166. 分数到小数

166. 分数到小数 给定两个整数&#xff0c;分别表示分数的分子 numerator 和分母 denominator&#xff0c;以 字符串形式返回小数 。 如果小数部分为循环小数&#xff0c;则将循环的部分括在括号内。 如果存在多个答案&#xff0c;只需返回 任意一个 。 对于所有给定的输入…

最近用.NET实现DHT爬虫,全.NET实现

最近用.NET实现DHT爬虫&#xff0c;全.NET实现&#xff0c;大家可以加我QQ交流下 309159808 转载于:https://www.cnblogs.com/oshoh/p/9236186.html

C++贪吃蛇

动画链接 GitHub链接&#xff1a;https://github.com/yanpeng1314/Snake 1 #include "Snake.h"2 3 int iScore 0;4 int iGrade 1;5 6 //蛇头蛇尾初始位置7 int x_head 1, y_head 3;8 int x_tail 1, y_tail 1;9 10 //地图坐标11 int i_Map 1, j_Map 1;12 13 /…