03 . Nginx日志配置及日志切割

时间:2022-07-25
本文章向大家介绍03 . Nginx日志配置及日志切割,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Nginx日志

日志对于统计排错来说是非常有利的,Nginx日志主要分为两种: access_log(访问日志)和error_log(错误日志),通过访问日志可以得到用户的IP地址、浏览器的信息,请求的处理时间等信息。错误日志记录了访问出错的信息,可以帮助我们定位错误的原因。

设置Access_log日志

访问日志主要记录客户端的请求。客户端向Nginx服务器发起的每一次请求都记录在这里。客户端IP,浏览器信息,referer,请求处理时间,请求URL等都可以在访问日志中得到。当然具体要记录哪些信息,你可以通过log_format指令定义。

语法
access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]]; # 设置访问日志
access_log off; # 关闭访问日志

# path 指定日志的存放位置。
# format 指定日志的格式。默认使用预定义的combined。
# buffer 用来指定日志写入时的缓存大小。默认是64k。
# gzip 日志写入前先进行压缩。压缩率可以指定,从1到9数值越大压缩比越高,同时压缩的速度也越慢。默认是1。
# flush 设置缓存的有效时间。如果超过flush指定的时间,缓存中的内容将被清空。
# if 条件判断。如果指定的条件计算为0或空字符串,那么该请求不会写入日志。
# 另外,还有一个特殊的值off。如果指定了该值,当前作用域下的所有的请求日志都被关闭。
作用域

可以应用access_log指令的作用域分别有http,server,location,limit_except,也就是说,这几个作用域使用该命令,Nginx会报错

基本用法

access_log /usr/local/nginx/logs/b_test/access.log;

该例子指定日志的写入路径为/usr/local/nginx/logs/b_test/access.log,日志格式使用默认的combined

log_format指令
# Nginx预定义了名为combined日志格式,如果没有明确指定日志格式默认使用该格式:
# 如果想使用自定义格式,可以使用log_format自定义

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

# 语法:
# log_format name [escape=default|json] string ...;
        
name:               # 表示格式名称,在access_log指令中引用
string:             # 要定义的日志格式内容,该参数可以有多个,参数中可以使用Nginx变量
log_format:         # 有默认的无需设置的combined日志格式,相当于apache的combined格式,日志格式如下.
escape:				# 设置变量中的字符编码方式是json还是default,默认是default
log_format combined '$remote_addr - $remote_user [$time_local] '
                    ' "$request" $status $body_bytes_sent '
                    ' "$http_referer" "$http_user_agent" ';

#如果Nginx位于负载均衡器,squit,nginx反向代理之后,web服务器无法直接获取到客户端真实的IP地址。
#而此时$remote_addr 获取的是反向代理的IP地址.
#反向代理服务器在转发请求的http头信息中,可以增加X-Forwarded-For信息,用来记录客户端IP地址和客户端请求的IP服务器地址.日志格式如下:

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

下面是log_format指令常用的一些变量

变量

含义

$bytes_sent

发送给客户端的总字节数

$body_bytes_sent

发送给客户端的字节数,不包括响应头的大小

$connection

连接序列号

$connection_requests

当前通过连接发出的请求数量

$msec

日志写入时间,单位为秒,精度是毫秒

$pipe

如果请求是通过http流水线发送,则其值为"p",否则为“."

$request_length

请求长度(包括请求行,请求头和请求体)

$request_time

请求处理时长,单位为秒,精度为毫秒,从读入客户端的第一个字节开始,直到把最后一个字符发送张客户端进行日志写入为止

$status

响应状态码

$time_iso8601

标准格式的本地时间,形如“2017-05-24T18:31:27+08:00”

$time_local

通用日志格式下的本地时间,如"24/May/2017:18:31:27 +0800"

$http_referer

请求的referer地址。

$http_user_agent

客户端浏览器信息。

$remote_addr

客户端IP

$http_x_forwarded_for

当前端有代理服务器时,设置web节点记录客户端地址的配置,此参数生效的前提是代理服务器也要进行相关的x_forwarded_for设置。

$request

完整的原始请求行,如 "GET / HTTP/1.1"

$remote_user

客户端用户名称,针对启用了用户认证的请求

$request_uri

完整的请求地址,如 "https://daojia.com/"

Nginx日志分析

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '  
 '$status $body_bytes_sent "$http_referer" '  
 '"$http_user_agent" "$http_x_forwarded_for"';  
  
日志条目:  
117.107.143.155 - - [01/Nov/2019:16:29:39 +0800] "GET /favicon.ico HTTP/1.1" 404 555   
"http://39.108.140.0/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.17 Safari/537.36" "-"  

# Nov,是Novemenber(11)月的缩写  
# Feb是February(2)月的缩写  

设置error_log

错误日志在Nginx中通过error_log指令实现的,该指令记录服务器和请求处理过程中的错误信息

语法

配置错误日志文件的路径和日志级别

error_log file [level];
Default:    
error_log logs/error.log error;

# 第一个参数指定日志的写入位置。

# 第二个参数指定日志的级别。level可以是debug, info, notice, warn, error, crit, alert,emerg中的任意值。可以看到其取值范围是按紧急程度从低到高排列的。只有日志的错误级别等于或高于level指定的值才会写入错误日志中。默认值是error。
基本用法

error_log /var/logs/nginx/nginx-error.log

# 它可以配置在:main, http, mail, stream, server, location作用域。

# 例子中指定了错误日志的路径为:/var/logs/nginx/nginx-error.log,日志级别使用默认的error。

Open_log_file_cache

每一条日志记录的写入都是先打开文件再写入记录,然后关闭日志文件。如果你的日志文件路径中使用了变量,如access_log /var/logs/$host/nginx-access.log,为提高性能,可以使用open_log_file_cache指令设置日志文件描述符的缓存。

语法
open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];

#对于每一条日志记录,都将是先打开文件,再写入日志,然后关闭访问量大的时候IO会变多,会造成速度变慢.
#此时可以使用open_log_file_cache来设置日志文件缓存(默认是off),先将日志写到内存中,等待时机然后写入硬盘,格式如下

# max: 设置缓存中的最大文件描述符数量,如果缓存被占满,采用LRU(least recently used最近最少使用)算法将描述符关闭
# inactive: 设置存活时间,默认是10s
# min_uses: 设置在inactive时间段内,日志文件最少使用多少次后,该日志文件描述符记入缓存中,默认是1次
# valid: 设置检查频率,默认60s
# off: 不使用缓存

#open_log_file_cache max=1000 inactive=20s valid=1m min_uses=2;
基本用法

open_log_file_cache max=1000 inactive=20s valid=1m min_uses=2;

# 它可以配置在http、server、location作用域中。

# 例子中,设置缓存最多缓存1000个日志文件描述符,20s内如果缓存中的日志文件描述符至少被被访问2次,才不会被缓存关闭。每隔1分钟检查缓存中的文件描述符的文件名是否还存在

Nginx日志轮转

默认nginx不会自动切割日志,当日志文件越来越大时,不再浪费磁盘空间,nginx的性能也会降低。可以使用Linux的logrotate来解决这个问题。

logrotate可以自动对日志进行切割,压缩和删除。而且自动化处理,不需要人为操作,使用非常方便。

rpm -ql nginx |grep log
/etc/logrotate.d/nginx
/var/log/nginx
#轮转规则文件
vim /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
missingok
rotate 52
compress
delaycompress
notifempty
create 640 nginx adm
sharedscripts
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
#-USR1:发送1信号

# 手动运行脚本分割日志
sudo /usr/sbin/logrotate -f /etc/logrotate.d/nginx
logrotate参数说明
# 配置		   # 说明
daily			# 指定转储周期为每天
weekly			# 指定转储周期为每周
monthly			# 指定转储周期为每月
rotate count	# 指定日志文件删除之前转储的次数,0 指没有备份,5 指保留5 个备份
compress		# 通过gzip 压缩转储以后的日志
nocompress		# 不做gzip压缩处理
create mode owner group	# 轮转时指定创建新文件的属性,如create 0777 nobody nobody
nocreate		# 不建立新的日志文件
delaycompress	# 和compress 一起使用时,转储的日志文件到下一次转储时才压缩
nodelaycompress	# 覆盖 delaycompress 选项,转储同时压缩
missingok		# 如果日志丢失,不报错继续滚动下一个日志
ifempty			# 即使日志文件为空文件也做轮转,这个是logrotate的缺省选项
notifempty		# 当日志文件为空时,不进行轮转
mail address	# 把转储的日志文件发送到指定的E-mail 地址
olddir directory # 转储后的日志文件放入指定的目录,必须和当前日志文件在同一个文件系统
noolddir		# 转储后的日志文件和当前日志文件放在同一个目录下
sharedscripts	# 运行postrotate脚本,作用是在所有日志都轮转后统一执行一次脚本。如果没有配置这个,那么每个日志轮转后都会执行一次脚本
prerotate		# 在logrotate转储之前需要执行的指令,例如修改文件的属性等动作;必须独立成行
postrotate		# 在logrotate转储之后需要执行的指令,例如重新启动 (kill -HUP) 某个服务!必须独立成行
dateext			# 使用当期日期作为命名格式
dateformat .%s	# 配合dateext使用,紧跟在下一行出现,定义文件切割后的文件名,必须配合dateext使用,只支持 %Y %m %d %s 这四个参数
size(minsize) log-size	# 当日志文件到达指定的大小时才转储,log-size能指定bytes(缺省)及KB (sizek)或MB(sizem),例如 size 100M
添加定时任务
sudo crontab -u root -e
# rotate nginx log erery day
0 0 * * * /usr/sbin/logrotate -f /etc/logrotate.d/nginx

# 如果要删除上面定时任务,运行如下命令
contab -r

# 如果任务没有正确执行,可以通过如下命令查看任务日志
vim /var/log/cron

# 也可以通过自己写脚本日志分割
#!/bin/bash
base_path='/usr/local/nginx/logs'               # 保存日志的目录
log_path=$(date -d yesterday +"%Y%m")           
day=$(date -d yesterday +"%d")                  # 日期作为日志的标识
mkdir -p $base_path/$log_path                   # 根据年月管理日志
mv $base_path/access.log $base_path/$log_path/access_$day.log
#echo $base_path/$log_path/access_$day.log
kill -USR1 `cat /usr/local/nginx/logs/nginx.pid`# 信号通知nginx重新开始记录日志

# Crontab执行任务
30 02 * * * /cron/nginx/log.sh //每天2时30分(建议在02-04点之间,系统负载小)

常见的日志分析命令案例

# 1.统计2019年11月1号的pv量
grep '01/Nov/2019' /var/log/nginx/access.log |wc -l
102

# 2.统计2019年11月1号访问最多的10个IP
grep '01/Nov/2019' /var/log/nginx/access.log |awk '{ips[$1]++ } END{for (i in ips){print i,ips[i]}}' |sort -k2 -rn |head -n 10
49.233.69.195 201
190.94.151.52 1
117.107.143.155 1

# 3.统计2019年11月1号访问大于100次的IP
grep '01/Nov/2019' /var/log/nginx/access.log |awk '{ips[$1]++ } END{for(i in ips){if (ips[i]>100) {print i,ips[i]}}}'
49.233.69.195 201

# 4.统计2019年11月1号访问最多的10个页面
grep '01/Nov/2019' /var/log/nginx/access.log |awk '{urls[$7]++ } END{for(i in urls){print urls[i],i}}'|sort -k1 -rn  |head -n 10
202 /
1 /zhou.html
1 /favicon.ico

# 5.统计2019年11月1号每个URL访问内容的总大小($body_bytes_sent)
grep '01/Nov/2019' /var/log/nginx/access.log |awk '{urls[$7]++; size[$7]+=$10} END{for (i in urls){print urls[i],size[i],i}}' |sort -k1 -rn |head -n10
202 123624 /
1555 /favicon.ico
14 /zhou.html


# 6.统计2019年11月1号每个IP访问状态码数量($status)
grep '01/Nov/2019' /var/log/nginx/access.log |awk '{ip_code[$1" "$9]++} END{for (i in ip_code){print i,ip_code[i]}}' |sort -k1 -rn |head -n 10
190.94.151.52 200 1
117.107.143.155 404 1
49.233.69.195 200 202

# 7.统计2019年11月1号 IP访问状态码为404及出现次数($status)    # 此方法有点问题,不是很正确,后期再完善
grep '01/Nov/2019' /var/log/nginx/access.log |awk '{if ($9="404"){ip_code[$1" "$9]++}} END{for(i in ip_code){print i,ip_code[i]}}'
117.107.143.155 404 1
190.94.151.52 404 1
49.233.69.195 404 202

# 8.统计前一分钟的pv量
date1=$(date -d '1 minute ago' +%d/%b/%Y:%H:%M); awk -v date=$date1 '$0 ~ date {i++} END{print i}' /var/log/nginx/access.log
100

# 9.统计两个时间点之间的访问状态码404分别是多少个
awk '$4>="[01/Nov/2019:16:29:38" && $4<="[01/Nov/2019:17:38:36" {if($9="404"){ip_code[$1" "$9]++}} END{for (i in ip_code){print i,ip_code[i]}}' /var/log/nginx/access.log    # 
117.107.143.155 404 1
190.94.151.52 404 1
47.92.24.137 404 1
80.82.70.187 404 1
49.233.69.195 404 404

# 10.统计2019年11月1号各种状态码数量
grep '01/Nov/2019' /var/log/nginx/access.log |awk '{code[$9]++} END{for(i in code){print i,code[i]}}'
200 405
404 3

grep '01/Nov/2019' /var/log/nginx/access.log |awk '{code[$9]++;total++} END{for(i in code) {printf i" "; printf code[i]"t";printf "%.2f",code[i]/total*100;print "%"}}'
200 405    99.26%
404 3    0.74%

# Nginx日志字段
$remote_addr        $1            # 远程客户端IP
$time_local         $4            # 访问时间
$request            $7            # HTTP请求(方法,http版本,URL地址)
$status             $9            # HTTP状态码
$body_bytes_sent    $10           # 发送给客户端的字节数,不包括响应头的大小,首页多大就是多大