nginx配置访问日志
条评论对接调式排查问题时,查询日志是非常关键的,nginx作为负载/反向代理,冲在第一线可以收集到很多访问/接入信息。熟悉和了解nginx的日志模块,对排查,分析,统计都有帮助。
nginx的日志相关指令
log_format
- 用于设置日志的格式
语法:
log_format name string
name
:格式名称string
:定义的格式
举例:
log_format access '$http_x_forwarded_for $remote_addr [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent" "$request_body" "$sent_http_last_modified" "$remote_user" ';
当日志文件中记录的值为”-“时,表示为空
access为自定义的日志格式的名称,使用时引用即可:
access_log /var/log/nginx/access.log access;
具体设置的参数说明:
参数 | 说明 | 示例 |
---|---|---|
$args | #请求中的参数值 | |
$query_string | #同 $args | |
$arg_NAME | #GET请求中NAME的值 | |
$is_args | #如果请求中有参数,值为”?”,否则为空字符串 | |
$uri | #请求中的当前URI(不带请求参数,参数位于$args),可以不同于浏览器传递的$request_uri的值,它可以通过内部重定向,或者使用index指令进行修改,$uri不包含主机名,如”/foo/bar.html”。 | |
$document_uri | #同 $uri | |
$document_root | #当前请求的文档根目录或别名 | |
$host | #优先级:HTTP请求行的主机名>”HOST”请求头字段>符合请求的服务器名.请求中的主机头字段,如果请求中的主机头不可用,则为服务器处理请求的服务器名称 | |
$hostname | #主机名 | |
$https | #如果开启了SSL安全模式,值为”on”,否则为空字符串。 | |
$binary_remote_addr | #客户端地址的二进制形式,固定长度为4个字节 | |
$body_bytes_sent | #传输给客户端的字节数,响应头不计算在内;这个变量和Apache的mod_log_config模块中的”%B”参数保持兼容 | |
$bytes_sent | #传输给客户端的字节数 | |
$connection | #TCP连接的序列号 | |
$connection_requests | #TCP连接当前的请求数量 | |
$content_length | #”Content-Length” 请求头字段 | |
$content_type | #”Content-Type” 请求头字段 | |
$cookie_name | #cookie名称 | |
$limit_rate | #用于设置响应的速度限制 | |
$msec | #当前的Unix时间戳 | |
$nginx_version | #nginx版本 | |
$pid | #工作进程的PID | |
$pipe | #如果请求来自管道通信,值为”p”,否则为”.” | |
$proxy_protocol_addr | #获取代理访问服务器的客户端地址,如果是直接访问,该值为空字符串 | |
$realpath_root | #当前请求的文档根目录或别名的真实路径,会将所有符号连接转换为真实路径 | |
$remote_addr | #客户端地址 | |
$remote_port | #客户端端口 | |
$remote_user | #用于HTTP基础认证服务的用户名 | |
$request | #代表客户端的请求地址 | |
$request_body | #客户端的请求主体:此变量可在location中使用,将请求主体通过proxy_pass,fastcgi_pass,uwsgi_pass和scgi_pass传递给下一级的代理服务器 | |
$request_body_file | #将客户端请求主体保存在临时文件中。文件处理结束后,此文件需删除。如果需要之一开启此功能,需要设置client_body_in_file_only。如果将次文件传 递给后端的代理服务器,需要禁用request body,即设置proxy_pass_request_body off,fastcgi_pass_request_body off,uwsgi_pass_request_body off,or scgi_pass_request_body off | |
$request_completion | #如果请求成功,值为”OK”,如果请求未完成或者请求不是一个范围请求的最后一部分,则为空 | |
$request_filename | #当前连接请求的文件路径,由root或alias指令与URI请求生成 | |
$request_length | #请求的长度 (包括请求的地址,http请求头和请求主体) | |
$request_method | #HTTP请求方法,通常为”GET”或”POST” | |
$request_time | #处理客户端请求使用的时间,单位为秒,精度毫秒; 从读入客户端的第一个字节开始,直到把最后一个字符发送给客户端后进行日志写入为止。 | |
$request_uri | #这个变量等于包含一些客户端请求参数的原始URI,它无法修改,请查看$uri更改或重写URI,不包含主机名,例如:”/cnphp/test.php?arg=freemouse” | |
$scheme | #请求使用的Web协议,”http” 或 “https” | |
$server_addr | #服务器端地址,需要注意的是:为了避免访问linux系统内核,应将ip地址提前设置在配置文件中 | |
$server_name | #服务器名 | |
$server_port | #服务器端口 | |
$server_protocol | #服务器的HTTP版本,通常为 “HTTP/1.0” 或 “HTTP/1.1” | |
$status | #HTTP响应代码 | |
$time_iso8601 | #服务器时间的ISO 8610格式 | |
$time_local | #服务器时间(LOG Format 格式) | |
$cookie_NAME | #客户端请求Header头中的cookie变量,前缀”$cookie_”加上cookie名称的变量,该变量的值即为cookie名称的值 | |
$http_NAME | #匹配任意请求头字段;变量名中的后半部分NAME可以替换成任意请求头字段,如在配置文件中需要获取http请求头:”Accept-Language”,$http_accept_language即可 | |
$http_cookie | ||
$http_host | #请求地址,即浏览器中你输入的地址(IP或域名) | |
$http_referer | #url跳转来源,用来记录从那个页面链接访问过来的 | |
$http_user_agent | #用户终端浏览器等信息 | |
$http_x_forwarded_for | ||
$sent_http_NAME | #可以设置任意http响应头字段;变量名中的后半部分NAME可以替换成任意响应头字段,如需要设置响应头Content-length,$sent_http_content_length即可 | |
$sent_http_cache_control | ||
$sent_http_connection | ||
$sent_http_content_type | ||
$sent_http_keep_alive | ||
$sent_http_last_modified | ||
$sent_http_location | ||
$sent_http_transfer_encoding |
常用的配置:
参数 说明 示例 $remote_addr 客户端地址 211.28.65.253 $remote_user 客户端用户名称 — $time_local 访问时间和时区 18/Jul/2012:17:00:01 +0800 $request 请求的URI和HTTP协议 “GET /article-10000.html HTTP/1.1” $http_host 请求地址,即浏览器中你输入的地址(IP或域名) www.it300.com 192.168.100.100 $status HTTP请求状态 200 $upstream_status upstream状态 200 $body_bytes_sent 发送给客户端文件内容大小 1547 $http_referer url跳转来源 https://www.baidu.com/ $http_user_agent 用户终端浏览器等信息 Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; SV1; GTB7.0; .NET4. $ssl_protocol SSL协议版本 TLSv1 $ssl_cipher 交换数据中的算法 RC4-SHA $upstream_addr 后台upstream的地址,即真正提供服务的主机地址 10.10.10.100:80 $request_time 整个请求的总时间 0.002 $upstream_response_time 请求过程中,upstream响应时间 0.002 $request_body post请求的参数 —
注意点:
- 如何设置nginx中remote_addr和x_forwarded_for参数
remote_addr
: remote_addr代表客户端的IP,但它的值不是由客户端提供的,而是服务端根据客户端的ip指定的,当你的浏览器访问某个网站时,假设中间没有任何代理,那么网站的web服务器(Nginx,Apache等)就会把remote_addr设为你的机器IP,如果你用了某个代理,那么你的浏览器会先访问这个代理,然后再由这个代理转发到网站,这样web服务器就会把remote_addr设为这台代理机器的IP
x_forwarded_for
:通常web服务器放在反向代理的后面,当使用了代理时,web服务器就不知道你的真实IP了,为了避免这个情况,代理服务器通常会增加一个x_forwarded_for
的头信息,把连接它的客户端IP(即你的上网机器IP)加到这个头信息里,这样就能保证网站的web服务器能获取到真实IP.
在server中设置x_forwarded_for
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
access_log
- 用来指定日志文件的存放路径、格式
语法:
- access_log path [format [buffer=size [flush=time]]];
- access_log path format gzip[=level] [buffer=size] [flush=time];
- access_log syslog:server=address[,parameter=value] [format];
- access_log off;
默认值: access_log logs/access.log combined;
配置段: http, server, location, if in location, limit_except
gzip压缩等级。
buffer设置内存缓存区大小。
flush保存在缓存区中的最长时间。
不记录日志:access_log off;
使用默认combined格式记录日志:access_log logs/access.log 或 access_log logs/access.log combined;
open_log_file_cache
语法:
- open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];
- open_log_file_cache off;
默认值: open_log_file_cache off;
配置段: http, server, location
对于每一条日志记录,都将是先打开文件,再写入日志,然后关闭。可以使用open_log_file_cache来设置日志文件缓存(默认是off),格式如下:
参数注释如下:
- max:设置缓存中的最大文件描述符数量,如果缓存被占满,采用LRU算法将描述符关闭。
- inactive:设置存活时间,默认是10s
- min_uses:设置在inactive时间段内,日志文件最少使用多少次后,该日志文件描述符记入缓存中,默认是1次
- valid:设置检查频率,默认60s
- off:禁用缓存
实例:
open_log_file_cache max=1000 inactive=20s valid=1m min_uses=2;
log_not_found
- 是否在error_log中记录不存在的错误。默认是。
语法:
- log_not_found on | off;
默认值: log_not_found on;
配置段: http, server, location
log_subrequest
- 是否在access_log中记录子请求的访问日志。默认不记录。
语法:
- log_subrequest on | off;
默认值: log_subrequest off;
配置段: http, server, location
rewrite_log
- 启用时将在error log中记录notice级别的重写日志。
由ngx_http_rewrite_module模块提供的。用来记录重写日志的。对于调试重写规则建议开启。 Nginx重写规则指南
语法:
rewrite_log on | off;
默认值: rewrite_log off;
配置段: http, server, location, if
error_log
- 配置错误日志。
语法:
- error_log file | stderr | syslog:server=address[,parameter=value] [debug | info | notice | warn | error | crit | alert | emerg];
默认值: error_log logs/error.log error;
配置段: main, http, server, location
日志统计分析
> 看到某大师分享的关于手动分析的日志的方式,方法不错,简单记录下
Nginx 日志能够记录的变量还有很多没出现在默认配置中:
比如:
请求数据大小:$request_length
返回数据大小:$bytes_sent
请求耗时:$request_time
所用连接序号:$connection
当前连接发生请求数:$connection_requests
Nginx 的默认格式不可计算统计,需要想办法转换成可统计计算的格式,比如用控制字符 ^A (Mac 下 ctrl+v ctrl+a 打出)分割每个字段。
log_format 的格式可以变成这样:
log_format new '$remote_addr^A$http_x_forwarded_for^A$host^A$time_local^A$status^A'
'$request_time^A$request_length^A$bytes_sent^A$http_referer^A$request^A$http_user_agent';
这样之后就通过常见的 Linux 命令行工具进行分析了:
查找访问频率最高的 URL 和次数:
cat access.log | awk -F ‘^A’ ‘{print $10}’ | sort | uniq -c
查找当前日志文件 500 错误的访问:
cat access.log | awk -F ‘^A’ ‘{if($5 == 500) print $0}’
查找当前日志文件 500 错误的数量:
cat access.log | awk -F ‘^A’ ‘{if($5 == 500) print $0}’ | wc -l查找某一分钟内 500 错误访问的数量:
cat access.log | awk -F ‘^A’ ‘{if($5 == 500) print $0}’ | grep ’09:00’ | wc-l
查找耗时超过 1s 的慢请求:
tail -f access.log | awk -F ‘^A’ ‘{if($6>1) print $0}’
假如只想查看某些位:
tail -f access.log | awk -F ‘^A’ ‘{if($6>1) print $3″|”$4}’
查找 502 错误最多的 URL:
cat access.log | awk -F ‘^A’ ‘{if($5==502) print $11}’ | sort | uniq -c
查找 200 空白页
cat access.log | awk -F ‘^A’ ‘{if($5==200 && $8 < 100) print $3″|”$4″|”$11″|”$6}’
查看实时日志数据流
tail -f access.log | cat -e
或者
tail -f access.log | tr ‘^A’ ‘|’
按这个思路可以做很多其他分析,比如 UA 最多的访问;访问频率最高的 IP;请求耗时分析;请求返回包大小分析;等等。
切割日志
Nginx 的日志都是写在一个文件当中的,不会自动地进行切割,如果访问量很大的话,将导致日志文件容量非常大,不便于管理和造成Nginx 日志写入效率低下等问题。所以,往往需要要对access_log、error_log日志进行切割。
切割日志一般利用USR1信号让nginx产生新的日志。
- 实例:
1 | #!/bin/bash |
1、分析:
将上面的脚本放到crontab中,每小时执行一次(0 ),这样每小时会把当前日志重命名成一个新文件;然后发送USR1这个信号让Nginx 重新生成一个新的日志。(相当于备份日志)
将前7天的日志删除;
2、说明:
在没有执行kill -USR1 $pid之前,即便已经对文件执行了mv命令而改变了文件名称,nginx还是会向新命名的文件”*access.log.2016032623”照常写入日志数据的。原因在于:linux系统中,内核是根据文件描述符来找文件的。
3、logrotates:
使用系统自带的logrotates,也可以实现nginx的日志分割,查看其bash源码,发现也是发送USR1这个信号。
nginx的日志相关总结先到这里。