nginx内置日志切割实战

DevOpSec
• 阅读 283

来源: DevOpSec公众号 作者: DevOpSec

背景

小明在维护nginx时通过自己写脚本切割日志,这里弊端有二:

一、日志可能丢失,在日志切割和新日志产生的一瞬间有日志的丢失。

二、维护成本高,由于业务量的增加nginx横向扩容了几台机器,小明很快就把nginx扩容上,但日志切割的定时任务忘记增加了。

在日常运维过程中我们希望日志自动的按天切割,而不是通过脚本加定时任务实现,增加了运维维护的复杂度,下面让我们看看怎么操作吧。

日志切割

我们知道nginx配置文件里是可以引用nginx的内置变量的,nginx的日志文件名是否可以把带有时间的变量拼接一起,随着时间的变化日志文件自动切割呢?

答案是可以的。下面看nginx内的的部分变量,我们通过time_iso8601变量分别实现http协议和tcp协议下nginx日志自动切割。

首先我们先看一下nginx内置常用变量,对我们定义打印日志内容有帮助。

1. nginx内置变量


变量名称 变量描述
$time_iso8601 本地ISO 8601时间
$arg_PARAMETER 客户端GET请求中PARAMETER 字段的值
$args 客户端请求中的参数
$binary_remote_addr 远程地址的二进制表示
$body_bytes_sent 已发送的消息体字节数
$content_length HTTP请求信息中content-length的字段
$content_type 请求信息中content-type字段
$cookie_COOKIE 客户端请求中COOKIE头域的值
$document_root 针对当前请求的根路径设置值
$document_uri 与$uri相同
$host 请求信息中的host头域,如果请求中没有Host行,则等于设置的服务器名
$http_HEADER HTTP请求信息里的HEADER地段
$http_host 与$host相同,但是如果请求信息中没有host行,则可能不同客户端cookie信息
$http_cookie 客户端cookie信息
$http_referer 客户端是从哪一个地址跳转过来的
$http_user_agent 客户端代理信息,也就是你客户端浏览器
$http_via 最后一个访问服务器的IP
$http_x_forwarded_for X-Forwarded-For 是一个扩展头, 用来表示HTTP 请求端真实IP
$is_args 如果有args的值,则等于”?”,否则为空
$limit_rate 对连接速率的限制
$nginx_version 当前Nginx的版本
$pid 当前Nginx服务器的进程的进程ID
$query_string 与$args相同
$remote_addr 客户端IP地址
$remote_port 客户端的端口
$remote_user 客户端的用户名,用于 auth basic module验证
$request 客户端请求
$request_body 客户端发送的报文体
$request_body_file 发送后端服务器的本地临时缓存文件的名称
$request_filename 当前请求的文件路径名,由root或alias指令与URI请求生成
$request_method 请求后端数据的方法,例如”GET”,”POST”
$request_uri 请求的URI,带参数,不包含主机名
$scheme 所用的协议,如http或者HTTPS,比如rewrite^(.+)$$scheme://mysite.name$redirect
$sent_http_cache_control 对应http请求头中的Cache-Control,需要打开chrome浏览器,右键检查,选中network,点中其中一个请求的资源
$sent_http_connection 对应http请求中的Connection
$sent_http_content_type 对应http请求中的Content-Type
$sent_last_modified 对应请求中的Last-Modified
$server_addr 服务端的地址
$server_name 请求到达的服务器名
$server_port 请求到达服务器端口号
$server_protocol 请求协议的版本号,HTTP1.0/HTTP1.1
$uri 请求的不带请求参数的URI,可能和最初的值有不同,比如经过重定向之类的
$request_time nginx接收到请求到发送给client端数据包耗时
$upstream_response_time nginx后端服务器的响应时间
$http_user_agent 用户客户端UA标识

下面开始做nginx日志切割的配置了,对于nginxtcphttp协议都适用。

2. 全局通用配置,http或者stream模块里设置

首先通过nginx关键字map和内置变量time_iso8601拿到年月日,当然还可以按小时分钟甚至是按秒切割,按日志大小和需求情况来定

http | stream { 
    #.....
    #设置变量 year month day hour minutes seconds
    map $time_iso8601 $year {
        default '0000';
        "~^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})" $1;
    }
    map $time_iso8601 $month {
        default '00';
        "~^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})" $2;
    }
    map $time_iso8601 $day {
        default '00';
        "~^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})" $3;
    }
    map $time_iso8601 $hour {
        default '00';
        "~^^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})" $4;
    }
    map $time_iso8601 $minutes {
        default '00';
        "~^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})" $5;
    }
    map $time_iso8601 $seconds {
        default '00';
        "~^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})" $6;
    }
}

定义日志格式,nginx日志json化,日志格式化后续方便处理,比如入kafkastorm等流式处理

http | stream { 
# 日志格式
log_format   logjsonv1  escape=json '{'
        '"version": "logjsonv1", '
        '"hostname": "${hostname}", '
        '"remote_addr": "${remote_addr}", '
        '"remote_port": "${remote_port}", '
        '"proxy_protocol_addr": "${proxy_protocol_addr}", '
        '"http_x_forwarded_for": "${http_x_forwarded_for}", '
        '"upstream_addr": "${upstream_addr}", '
        '"time_iso8601": "${time_iso8601}", '
        '"request_method": "${request_method}", '
        '"scheme": "${scheme}", '
        '"server_name": "${host}", '
        '"server_port": "${server_port}", '
        '"uri": "${uri}", '
        '"args": "${args}", '
        '"status": "$status", '
        '"http_referer": "${http_referer}", '
        '"body_bytes_sent": "${body_bytes_sent}", '
        '"request_time": "${request_time}", '
        '"http_cookie": "${http_cookie}", '
        '"upstream_response_time": "${upstream_response_time}", '
        '"http_user_agent": "${http_user_agent}", '
        '"sent_http_location": "${sent_http_location}", '
        '"sent_http_content_type": "${sent_http_content_type}" '
'}';

    #记录日志,按天切割,也可以根据需要按小时切割
    access_log  logs/access_$year$month$day.log  logjsonv1;

    #.....
}

我们还可以简单的map以下比如:

 map $time_iso8601 $formatted_date {
        default                         'date-not-found';
        '~^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})'    $year-$month-$day;
    }
access_log  logs/access_$formatted_date.log  logjsonv1;

注意:可能还有人说还可以用if啊,如下:

 if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2})") {
                set $year $1;
                set $month $2;
                set $day $3;
                set $hour $4;
        }

ifhttp模块里是没有问题的,但在stream模块里不支持if语句,此方法就没有map方式通用。

以上就是本文重点tcphttp协议下怎样用nginx内置变量和命令做日志的自动切换,下面是map的简单介绍和三方工具logrotate实现的日志切割,感兴趣可以学习一下。

map介绍和三方工具logrotate实现日志切割

1. map用法

map 指令是由 ngx_http_map_modulengx_stream_map_module模块提供的,默认情况下安装 nginx 都会安装该模块。

map 的主要作用是创建自定义变量,通过使用 nginx 的内置变量,去匹配某些特定规则,如果匹配成功则设置某个值给自定义变量。 而这个自定义变量便可作于它用,比如日志切割。

map语法

http下:
Syntax:    map string $variable { ... }
Default:    —
Context:    http


stream下:
Syntax:    map string $variable { ... }
Default:    —
Context:    stream

例子: 默认mobile 变量默认值是0,当http_user_agent匹配上Opera Mini后其值被赋值为1

map $http_user_agent $mobile {
    default       0;
    "~Opera Mini" 1;
}

2. logrotate 切割nginx日志

安装logrotate

yum install logrotate

安装完成后,自动在/etc/cron.daily/下生成个logrotate脚本文件。


cd /etc/logrotate.d/

vim nginx 内容如下:

/path/nginx/logs/*log {
    su root www
    create 600 www www
    daily
    dateext
    rotate 10
    missingok
    notifempty
    compress
    sharedscripts
    olddir /path/nginx/logs/oldlogs  # 这个目录要事先创建好,并给相关权限
    postrotate
        kill -USR1 `cat /path/nginx/logs/nginx.pid`
    endscript
}

#设置定时任务

crontab -e

0 0 * * *  /usr/sbin/logrotate -f /etc/logrotate.d/nginx 

除了三方工具我们还可以写脚本实现日志切割,脚本方式大家可以尝试一下。

nginx内置工具实现有一个弊端没有压缩和日志保留时长的处理,这个大家也可以思考一下怎么处理,大家可以在评论区留言讨论。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
DevOpSec DevOpSec
3年前
nginx配置系列-日志切割
nginx配置系列日志切割背景nginx日志中我们希望日志能够每天或者每小时自动切割,nginx本身没有提供自动切割的机制,但是我们可以通过脚本或者稍加改造让其具备这种能力。下面让我们看看怎么操作吧。日志切割常见做法有四种,在我们做之前我们来学习一下nginx日志中常用的内置变量字段都是什么意思nginx内置变量
Wesley13 Wesley13
2年前
UIWebView长按保存图片和识别图片二维码的实现方案(使用缓存)
0x00需求:长按识别UIWebView中的二维码,如下图长按识别二维码0x01方案1:给UIWebView增加一个长按手势,激活长按手势时获取当前UIWebView的截图,分析是否包含二维码。核心代码:略优点:流程简单,可以快速实现。不足:无法实现保存UIWebView中图片,如果当前We
Wesley13 Wesley13
2年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Stella981 Stella981
2年前
Linux日志安全分析技巧
0x00前言我正在整理一个项目,收集和汇总了一些应急响应案例(不断更新中)。GitHub地址:https://github.com/Bypass007/EmergencyResponseNotes本文主要介绍Linux日志分析的技巧,更多详细信息请访问Github地址,欢迎Star。0x01日志简介Lin
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
python定时任务执行shell脚本切割Nginx日志-慎用
Python定时任务执行shell脚本切割Nginx日志(慎用)缘起我们有一个Nginx服务用来接收埋点上报数据,输出的日志文件比较大,Nginx没有自带日志分割组件,这样输出的日志文件就比较大,抽取日志就比较麻烦。网上切割日志的方式一般就两种:logra