4542

在nginx中收集请求日志,异步归总到消息队列(nsq)

乐果   发表于   2018 年 07 月 24 日 标签:lua

最近公司的服务器经常因为磁盘被日志文件写爆,磁盘不可用后导致接口请求报错,为此经常要定期删除服务器日志以腾出磁盘空间。有没有更好的办法解决磁盘容量太小,日志保存不能太多、时间不能太长的麻烦呢?我试想过,将接口日志收收集后转储到公司内部服务器,做离线存储方案,这样有三个好处:

1、海量接口日志不用再占用线上服务器的有限磁盘空间,节省费用;

2、在负载均衡多节点的部署架构下,避免日志文件的分散导致的查阅不方便;

3、归总转储离线后,日志可以更长时间的存储,本地集中查阅高效,为进一步分析提供了可能性。 比如:收集日志后,可以统计每日访问的pu、pv,可以分析每个接口请求频次;但业务操作存在bug时,可以借助日志分析问题原因……

但如何转储呢?在网上查阅了一些资料,有人利用kafka消息队列+lua,集中存储nginx请求日志到kafka中。

最近刚好在研究nsq,借此做一个基于nsq+lua的nginx日志转储方案,如下!

一、资源下载:

依赖:

https://github.com/openresty/lua-cjson/archive/2.1.0.6.tar.gz

https://github.com/openresty/lua-resty-core/archive/v0.1.13.tar.gz

lua-nsq:

https://github.com/rainingmaster/lua-resty-nsq

注意将Makefile文件里修改lua相关参数 :

#LUA_INCLUDE_DIR ?= $(PREFIX)/include
#LUA_LIB_DIR ?=     $(PREFIX)/lib/lua/$(LUA_VERSION)

LUA_INCLUDE_DIR ?= $(PREFIX)/include/luajit-2.0
LUA_LIB_DIR ?=     $(PREFIX)/lib/lua/5.1

二、nginx站点server里配置如下:

body_filter_by_lua '
    local resp_body = string.sub(ngx.arg[1], 1, 1000)
    ngx.ctx.buffered = (ngx.ctx.buffered or"") .. resp_body
';
log_by_lua_file /data/service/tengine/conf/lua/log.lua;

三、log.lua脚步如下:

-- 引入lua所有api
local cjson = require "cjson"
-- 定义json便于日志数据整理收集
local log_json = {}
log_json["uri"]=ngx.var.uri
log_json["args"]=ngx.var.args
log_json["host"]=ngx.var.host
log_json["request_body"]=ngx.var.request_body
log_json["remote_addr"] = ngx.var.remote_addr
log_json["remote_user"] = ngx.var.remote_user
log_json["time_local"] = ngx.var.time_local
log_json["status"] = ngx.var.status
log_json["body_bytes_sent"] = ngx.var.body_bytes_sent
log_json["http_referer"] = ngx.var.http_referer
log_json["http_user_agent"] = ngx.var.http_user_agent
log_json["http_x_forwarded_for"] = ngx.var.http_x_forwarded_for
log_json["upstream_response_time"] = ngx.var.upstream_response_time
log_json["request_time"] = ngx.var.request_time
log_json["resq_body"] = ngx.ctx.buffered
-- 转换json为字符串
local message = cjson.encode(log_json);
local producer = require "resty.nsq.producer"

-- 字符串分隔方法
function string:split(sep)
        local sep, fields = sep or ":", {}
        local pattern = string.format("([^%s]+)", sep)
        self:gsub(pattern, function (c) fields[#fields + 1] = c end)
        return fields
end

local handler
handler = function(premature)
    if premature then
        return
    end
    local prod = producer:new()
    local poolSlice = {"127.0.0.1:4150","127.0.0.1:4152"}
    local connString = poolSlice[math.random(1,2)]
    local connSlice = connString:split(":")
    local ok, err = prod:connect(connSlice[1], connSlice[2])
    if not ok then
        ngx.say("failed to connect: ", err)
        return
    end
    ok, err = prod:pub(topic, message)
    if not ok then
        ngx.say("failed to pub: ", err)
        return
    end

    ok, err = prod:close()
    if not ok then
        ngx.say("failed to close: ", err)
        return
    end
end

local delay = 0
ok, err = ngx.timer.at(delay,handler)
if not ok then
    ngx.log(ngx.ERR,"failed to create the timer:",err)
    return
end

注意,要在nginx.conf的http配置里,指定lua扩展路径地址:

lua_package_path "/usr/local/lib/lua/5.1/?.lua;;";

乐果   发表于   2018 年 07 月 24 日 标签:lua

0

文章评论