nginx日志管理

nginx是一个高性能的HTTP和反向代理服务器,以其轻量级、资源占用少、并发能力强等优点被广泛使用。

在使用过程中,我们常常希望能够将接到的请求记录到日志以便统计分析或问题排查,自然少不了做日志管理。本人近期刚刚开始学习nginx的使用,总结遇到的日志管理方面的问题整理如下(本文使用nginx-1.12.1版本):

自定义日志格式

在nginx 的日志目录logs下,默认有以下3个文件:

  • access.log:记录请求日志
  • error.log:记录错误日志,例如启动时端口被占用,请求的资源不存在等。
  • nginx.pid:记录启动时nginx的进程id

nginx的日志配置是在conf/nginx.conf文件中,初始内容如下(省略部分被注释的内容):

#user  nobody;#运行worker process的用户
worker_processes  1;#worker 进程数据,通常可以设置为cpu核数

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;#进程pid文件


events {
    worker_connections  1024;#每个worker process能处理的最大连接数
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #nginx默认access日志格式
    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #默认access日志路径
    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;#监听端口
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        #默认请求匹配路径
        location / {
            root   html;#请求的根目录是html,即从html目录查找资源
            index  index.html index.htm;#默认首页
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

从配置文件中我们可以看出,access的默认日志格式为:

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"';
    

各字段含义如下:

字段 含义 示例
remote_addr 客户端地址 211.28.65.253
remote_user Auth Basic Module验证的用户名 test
time_local 访问时间和时区 24/Oct/2017:19:47:17 +0800
request 请求的URI和HTTP协议 GET /index.html HTTP/1.1
status HTTP请求状态 200
body_bytes_sent 发送给客户端文件内容大小 612
http_referer url跳转来源 https://www.baidu.com
http_user_agent 用户终端浏览器等信息 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
http_x_forwarded_for 简称XFF头,它代表客户端,也就是HTTP的请求端真实的IP,只有在通过了HTTP 代理或者负载均衡服务器时才会添加该项,第一个值为真实的ip 10.21.74.53, 10.29.83.46

利用这些内置的变量,我们就可以进行日志格式的自定义,我们甚至可以将日志格式变为json的,只需修改配置如下:

log_format json_log escape=json '{"timestamp": "$time_local", '
    '"remote_addr": "$remote_addr", '
    '"remotr_user": "$remote_user", '
    '"referer": "$http_referer", '
    '"request": "$request", '
    '"status": $status, '
    '"bytes":$body_bytes_sent, '
    '"agent": "$http_user_agent", '
    '"x_forwarded": "$http_x_forwarded_for", '
    '"upstr_addr": "$upstream_addr",'
    '"upstr_host": "$upstream_http_host", '
    '"request_body": "$request_body", '
    '"ups_resp_time": "$upstream_response_time"}';

注意要在配置中加上escape=json

记录post请求内容

默认情况下,nginx只能响应GET方式的静态资源请求,如果发送POST方式的静态资源请求,则会得到如下信息:

<html>
    <head>
        <title>405 Not Allowed</title>
    </head>
    <body bgcolor="white">
        <center>
            <h1>405 Not Allowed</h1>
        </center>
        <hr>
        <center>nginx/1.12.1</center>
    </body>
</html>

有时,我们希望能够使用POST方式来请求静态资源,同时发送一些参数,比如我最近就遇到了这种需求,希望通过POST请求来记录请求日志,包括请求中的参数。

关于405的问题,网上给出的解决方法主要是两种:
(1)将405重定向到200
(2)修改源代码

个人觉得这两种方式都不是很好,最终查到了另一种更优雅的方式:nginx+Lua。

具体做法如下:
(1)下载LuaJIT并安装,本人安装的为2.0.5版本,下载地址:http://luajit.org/download.html
安装完成后,设置环境变量(编辑~/.bashrc):

export LUAJIT_LIB=/home/work/local/LuaJIT-2.0.5/usr/local/lib
export LUAJIT_INC=/home/work/local/LuaJIT-2.0.5/usr/local/include/luajit-2.0

其中变量指向安装目录中的相应资源。
(2)下载lua-nginx-module,并解压,无需安装,本人使用的是0.10.10版本。下载地址:https://github.com/openresty/lua-nginx-module/releases
(3)安装nginx:

./configure –prefix=安装目录 –with-pcre=pcre源码路径 –add-module=lua-nginx-modul解压后路径
make -j2
make install

额外补充一点,pcre是安装nginx依赖的另一个包,因为与主题无关,因此没有专门交代。

(4)修改nginx.conf配置文件

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" "$request_body" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
location / {
            lua_need_request_body on;
            content_by_lua 'local s = ngx.var.request_body';
            root   html;
            index  index.html index.htm;
        }

注意:我们使用到另一个内置变量:request_body,该变量的值就是客户发送的POST请求的请求体。

各位看官看到这里,难免会问:既然nginx已经给我们提供了request_body变量,那我们直接用不就行了吗?干嘛还要扯出Lua呢?

这个问题是这样:nginx中读取POST数据必须要调用ngx_http_read_client_request_body()函数,而默认情况下,这个函数是不会被调用的。(莫名其妙???so am i)

那么我们怎么能够调用这个函数呢?这就要借助于ngx_lua解决,只要在输出log之前读一遍request_body,就可以调用该函数,配置如上面所示。

日志分割

nginx默认不会对日志做分割,随着时间的积累,access.log和error.log将会越来越大,非常不便于我们查看日志。

在linux下进行日志分割的一个常用工具是logrotatelogrotate功能非常强大,除了支持日志按照一定规则分割,还支持对历史日志进行压缩,过期日志删除等常见操作,可谓是日志管理的瑞士军刀。

logrotate的使用非常简单,只需写一个配置文件,基本格式如下:

/home/work/local/nginx-1.12.1/logs/access.log {
    daily #日志分割的周期,共有daily,weekly,monthly,yearly几种
    rotate 7 #保留多少份历史日志,为7表示保留1周的历史日志
    nocompress  #是否将历史日志进行压缩
    missingok  # 如果遇到文件无法找到,继续执行,忽略错误
    dateext  #以日期作为历史文件结尾
    dateformat .%Y-%m-%d。#与dateext配合使用,设定后缀格式
    sharedscripts #在所有的日志文件都轮转完毕后统一执行一次脚本
    postrotate # 指定日志轮滚完成后执行的脚本
        kill -USR1 `cat /home/work/local/nginx-1.12.1/logs/nginx.pid`
    endscript
}

需要注意的是:kill -USR11的作用是给nginx进程发送一个指令,让nginx重新加载配置,目的是更新日志文件句柄,使新的日志输出到新的日志文件中。

logrotate只负责对日志的管理,定时执行还要靠crontab。有关crontab的配置和使用网上很多,这里就不再赘述了。

在crontab定时任务中配置:

0 0 * * * /usr/sbin/logrotate -f /home/work/local/nginx-1.12.1/conf/access.cron

这样,系统就会在每天0点0分执行日志轮滚操作。logrotate -f的作用是让轮滚操作强制执行。

如果定时任务是在非root账号下执行,在运行过程中可能会报如下错误:

error: error creating state file /var/lib/logrotate/status: Permission denied

这是由于logrotate默认会将执行记录写入到/var/lib/logrotate/status文件中,而非root账号不具有该文件的写权限。

解决该问题,可以通过在执行logrotate命令时指定-s参数,重新指定一个具有权限的文件即可:

/usr/sbin/logrotate -s /home/work/local/logrotate.status

使用crontab + logrotate是linux下非常理想的日志定时管理方式。略有遗憾的是,logrotate不支持小时粒度的轮滚。当然,我们是可以设置定时任务每小时执行一次,但是文件后缀不支持设置小时,最多只能设置到天级别。如果要使用logrotate做日志小时粒度的分割并以小时作为日志文件后缀,还需要结合一些shell脚本来实现(主要逻辑是对分割后的文件做一次重命名)。

参考资料:

本文已迁移至我的博客:http://ipenge.com/33504.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容