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 # 发送给客户端的字节数,不包括响应头的大小,首页多大就是多大