摘要:我處理的方式是使用每一個(gè)請求,都會(huì)有自己獨(dú)立的這個(gè)會(huì)貫穿整個(gè)請求的始終,簡單的函數(shù)如下到了階段要把追蹤日志寫入到硬盤里,處理代碼如下小于秒的請求不記錄可以用在模塊,也可以用在模塊,也能直接精確到模塊,即只到某個(gè)請求。
最近出了個(gè)故障,有個(gè)接口的請求居然出現(xiàn)了長達(dá)幾十秒的處理時(shí)間,由于日志缺乏,網(wǎng)絡(luò)故障也解除了,就沒法再重現(xiàn)這個(gè)故障了。為了可以在下次出現(xiàn)問題的時(shí)候能追查到問題,所以需要添加一些追蹤日志。
添加這些追蹤日志,我希望能夠達(dá)到如下幾點(diǎn):
1、只有請求超過一定時(shí)間才記錄,不然請求太多,系統(tǒng)扛不住
2、添加的代碼可以盡量的少
3、對接口的影響盡量小,比如不影響實(shí)際時(shí)延,甚至記錄日志時(shí)出現(xiàn)了錯(cuò)誤,也不影響系統(tǒng)正常運(yùn)行
openresty這套工具,可以在nginx處理請求的每一個(gè)階段介入,編寫代碼進(jìn)行邏輯處理。其可介入的流程如下圖:
log Phase這個(gè)階段,就是openresty能處理的最后階段。到這個(gè)階段的時(shí)候,實(shí)際上請求的響應(yīng)已經(jīng)發(fā)送給客戶端了。所以使用 log_by_lua (知乎真特么蛋疼啊,左右下劃線就自動(dòng)斜體,還沒提供轉(zhuǎn)義功能)
log Phase這個(gè)階段,就是openresty能處理的最后階段。到這個(gè)階段的時(shí)候,實(shí)際上請求的響應(yīng)已經(jīng)發(fā)送給客戶端了。另外我也測試過了,即使在這個(gè)階段發(fā)生了錯(cuò)誤,如 io 錯(cuò)誤,也不會(huì)影響接口的正常響應(yīng),所以使用 log_by_lua 很是符合需求。
好處不止如此, log_by_lua是一個(gè)請求的最后處理階段,那么只要請求正常進(jìn)行,比如會(huì)走到這一步,因此,在這一步,我們就知道了這個(gè)請求的耗時(shí)了。另外,則是我們的代碼里有不少的 ngx.exit ,如果是在業(yè)務(wù)邏輯處理的時(shí)候就記錄日志,那么每個(gè)出現(xiàn) ngx.exit 的地方,都需要插入寫日志到硬盤的操作,大大增加了代碼量。
寫日志到硬盤的這一步操作,可以在 log_by_lua 這個(gè)階段來完成,剩下的另一個(gè)問題就是每一步記錄的日志如何傳遞到 log_by_lua 這一階段來了。
我處理的方式是使用ngx.ctx, 每一個(gè)請求,都會(huì)有自己獨(dú)立的 ngx.ctx, 這個(gè) ngx.ctx 會(huì)貫穿整個(gè)請求的始終,簡單的log函數(shù)如下:
logger.lua -------------------------- local _M = {} function _M.log(format, ...) if ngx.ctx.log_slot == nil then ngx.ctx.log_slot = {} end arg = {...} local logstr = "" if arg == nil then logstr = format else logstr = string.format(format, unpack(arg)) end logstr = logstr .. " " .. ngx.now() table.insert(ngx.ctx.log_slot, logstr) end return _M
到了 log_by_lua 階段要把追蹤日志寫入到硬盤里,處理代碼如下:
log_slot.lua --------------------- local request_time = ngx.var.request_time if request_time < 1 then return --- 小于1秒的請求不記錄 end local slot = ngx.ctx.log_slot if slot == nil or type(slot) ~= "table" then return end local logs = table.concat(slot, " ") local f = assert(io.open("/logs/trace", "a")) f:write(logs .. " ") f:close()
log_by_lua 可以用在 http 模塊,也可以用在server模塊,也能直接精確到location模塊,即只到某個(gè)請求。所以你可以在nginx.conf 里的http里添加:
http{ log_by_lua_file "/code/log_slot.lua"; }
也可以在server的配置里添加:
server { log_by_lua_file "/code/log_slot.lua"; }
更能直接在某個(gè)接口里添加:
/v1/test { content_by_lua_file "/code/v1/test.lua"; log_by_lua_file "/code/log_slot.lua"; }
http里添加,則對所有的server; server里添加,則只針對此server;location里添加,就只針對這個(gè)接口。
但是,比較坑爹的是,log_by_lua 不像 access log,可以多層級(jí)使用。log_by_lua 在某層使用了之后,上層的 log_by_lua 就對此一層無效了。比如 /v1/test 接口添加了 log_by_lua, 那么 http 或者 server 里添加的 log_by_lua 在接受/v1/test接口的請求時(shí)都不會(huì)被用到。
正是因?yàn)檫@個(gè)坑,浪費(fèi)了我不少的時(shí)間來解決。我們的系統(tǒng)里,http 模塊是配置了 log_by_lua 的,用來做接口監(jiān)控,監(jiān)控返回的錯(cuò)誤碼,處理的時(shí)延等。如果我在 /v1/test 里添加了只針對 /v1/test 的追蹤日志,那么接口監(jiān)控就無法正常運(yùn)行了。
不過天無絕人之路,我想到了一個(gè)處理方法如下:
monitor_log.lua --------------------- local _M = {} function _M.monitor_log() local f = _M.api_monitor_log_func if f == nil then f, err = loadfile("/code/monitor.lua") if f == nil then ngx.log(ngx.ERR, "/code/monitor.lua, ", err) --- 如果不存在接口監(jiān)控,直接給一個(gè)空函數(shù) f = function() end end _M.api_monitor_log_func = f end local status, err = pcall(f) if not status then ngx.log(ngx.ERR, "run api monitor /code/monitor.lua failed", err) end end return _M
修改log_slot.lua代碼如下:
local logger = require "code.monitor" local request_time = ngx.var.request_time logger.monitor_log() if request_time < 1 then return --- 小于1秒的請求不記錄 end local slot = ngx.ctx.log_slot if slot == nil or type(slot) ~= "table" then return end local logs = table.concat(slot, " ") local f = assert(io.open("/logs/trace", "a")) f:write(logs .. " ") f:close()
如上,就可以進(jìn)行其他層級(jí)的 log_by_lua 代碼運(yùn)行了,皆大歡喜,問題解決了。
當(dāng)系統(tǒng)并發(fā)請求較低的時(shí)候,worker夠用,則使用 log_by_lua 可以說是毫無壞處。當(dāng)然,一旦 log_by_lua 出現(xiàn)了故障,如死循環(huán),則會(huì)長時(shí)間占用worker,造成整個(gè)系統(tǒng)崩潰掉。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/39987.html
摘要:現(xiàn)在用方式調(diào)用接口,中使用方式輸入內(nèi)容日志平臺(tái)網(wǎng)關(guān)層基于。日志平臺(tái)網(wǎng)關(guān)層基于到此為止,提取經(jīng)過網(wǎng)關(guān)的接口信息,并將其寫入日志文件就完成了,所有的接口日志都寫入了文件中。 背景介紹 1、問題現(xiàn)狀與嘗試 沒有做日志記錄的線上系統(tǒng),絕對是給系統(tǒng)運(yùn)維人員留下的坑。尤其是前后端分離的項(xiàng)目,后端的接口日志可以解決對接、測試和運(yùn)維時(shí)的很多問題。之前項(xiàng)目上發(fā)布的接口都是通過Oracle Service...
摘要:一看果然是在響應(yīng)發(fā)出后報(bào)的錯(cuò),但日志沒有反應(yīng)出報(bào)錯(cuò)的具體位置。而我期望的當(dāng)前請求直接終止,不應(yīng)該使用而是。自起,執(zhí)行成功返回,失敗則返回和錯(cuò)誤描述信息。 事由 我們基于Vanilla開發(fā)了一個(gè)類似于一個(gè)網(wǎng)關(guān)的流量分發(fā)服務(wù),在原來的業(yè)務(wù)線上對不同的業(yè)務(wù)使用不同的后端(PHP、Python、Lua...)進(jìn)行處理,最近在緊鑼密鼓的測試(當(dāng)然這里咱們主要看問題),在掃蕩日志的過程中發(fā)現(xiàn)有這樣...
摘要:用于方便地搭建能夠處理超高并發(fā)擴(kuò)展性極高的動(dòng)態(tài)應(yīng)用服務(wù)和動(dòng)態(tài)網(wǎng)關(guān)。安裝安裝依賴庫下載及安裝激活組件被用于構(gòu)建。大部組件默認(rèn)是激活的,也有部件不是。您需要通過以下選項(xiàng)在編譯的時(shí)候?qū)⑺鼈兏髯约せ睿汀? OpenResty簡介 OpenResty 是一個(gè)基于 Nginx 與 Lua 的高性能 Web 平臺(tái),其內(nèi)部集成了大量精良的 Lua 庫、第三方模塊以及大多數(shù)的依賴項(xiàng)。用于方便地搭建能夠處...
閱讀 746·2021-11-23 09:51
閱讀 2447·2021-10-11 11:10
閱讀 1319·2021-09-23 11:21
閱讀 1101·2021-09-10 10:50
閱讀 898·2019-08-30 15:54
閱讀 3337·2019-08-30 15:53
閱讀 3299·2019-08-30 15:53
閱讀 3196·2019-08-29 17:23