nginx按条件输出日志
之前写过一篇: nginx两种实用的自定义访问日志格式 里面介绍了两种自定义的日志格式,一种是对程序友好的json格式日志,另外一种是手动分析友好的日志格式。人肉分析日志时,如果不是排查应用相关的日志,关注的访问日志多数是指定条件的,如分析502、404等状态码的日志.error log比较简单,就不说了。下面就介绍一下按条件输出访问日志的一些做法。
nginx 输出日志用到的内置模块是ngx_http_log_module,我们先来看看输出access log的语法:
access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
可以看到access_log是支持if条件的,需要1.7以上版本的nginx,if条件为0或空时不输出日志,有了这个条件判断就可以做很多事了,下面介绍几种按条件输出日志的方式抛砖引玉。
一、按域名输出日志
这种情况主要是针对同一个虚拟主机配置了大量的域名情况,对于某一个域名要输出日志分析的情况。来看配置:
map $host $log_host {
~nixops.me 1;
~nixops1.me 1;
default 0;
}
server {
[…]
access_log /var/log/nginx/access.log main;
access_log /var/log/nginx/nixops.me.log main if=$log_host;
}
这样 /var/log/nginx/nixops.me.log 里面就只有 nixops.me和nixops1.me这两个域名的日志了。
二、按http状态码输出日志
输出不正常的http状态码到访问日志,如502、503、504、400等状态码,对于分析反向代理或者应用异常会有用。和上面的做法一样,来看配置:
map $status $log_status {
~^[50] 1;
404 1;
default 0;
}
server {
[…]
access_log /var/log/nginx/access.log main;
access_log /var/log/nginx/http_error.log main if=$log_status;
}
这样就输出502、503、504、404日志到/var/log/nginx/http_error.log
三、输出指定uri的日志
输出指定location的日志到访问日志,看配置:
map $uri $log_uri {
~*admin 1;
/api 1;
default 0;
}
server {
[…]
access_log /var/log/nginx/access.log main;
access_log /var/log/nginx/http_uri.log main if=$log_uri;
}
也可以换个方式:
server {
[…]
set $log_uri 0;
if ( $uri ~ ^/api ) {
set $log_uri 1;
}
access_log /var/log/nginx/access.log main;
access_log /var/log/nginx/http_uri.log main if=$log_uri;
}
这种不使用map的方式可以支持正则表达式。
四、输出指定user agent的日志
记录包含指定user agent的请求到访问日志,如搜索引擎的抓取日志:
map $http_user_agent $log_ua {
~*Googlebot 1;
~*Baiduspider 1;
default 0;
}
server {
[…]
access_log /var/log/nginx/access.log main;
access_log /var/log/nginx/http_ua.log main if=$log_ua;
}
这样就把baidu和google的爬虫抓取日志输出到http_ua.log里了。
五、记录指定cookie
记录指定包含指定cookie的请求日志:
map $http_cookie $log_cookie {
~PHPSESSION 1;
default "";
}
server {
[…]
access_log /var/log/nginx/access.log main;
access_log /var/log/nginx/http_cookie.log main if=$log_cookie;
}
聪明的你肯定已经发现,上面的方法都是用map来实现,map一个nginx内置的变量,并设置值0或1,通过if条件判断是否需要记录。 那我们看下通过这种方式,能设置或输出那些条件的日志,看看 nginx内置变量 就知道能输出那些自定义的内容:
$ancient_browser
$arg_
$args
$binary_remote_addr (ngx_http_core_module)
$binary_remote_addr (ngx_stream_core_module)
$body_bytes_sent
$bytes_received
$bytes_sent (ngx_http_core_module)
$bytes_sent (ngx_http_log_module)
$bytes_sent (ngx_stream_core_module)
$connection (ngx_http_core_module)
$connection (ngx_http_log_module)
$connection (ngx_stream_core_module)
$connection_requests (ngx_http_core_module)
$connection_requests (ngx_http_log_module)
$connections_active
$connections_reading
$connections_waiting
$connections_writing
$content_length
$content_type
$cookie_
$date_gmt
$date_local
$document_root
$document_uri
$fastcgi_path_info
$fastcgi_script_name
$geoip_area_code (ngx_http_geoip_module)
$geoip_area_code (ngx_stream_geoip_module)
$geoip_city (ngx_http_geoip_module)
$geoip_city (ngx_stream_geoip_module)
$geoip_city_continent_code (ngx_http_geoip_module)
$geoip_city_continent_code (ngx_stream_geoip_module)
$geoip_city_country_code (ngx_http_geoip_module)
$geoip_city_country_code (ngx_stream_geoip_module)
$geoip_city_country_code3 (ngx_http_geoip_module)
$geoip_city_country_code3 (ngx_stream_geoip_module)
$geoip_city_country_name (ngx_http_geoip_module)
$geoip_city_country_name (ngx_stream_geoip_module)
$geoip_country_code (ngx_http_geoip_module)
$geoip_country_code (ngx_stream_geoip_module)
$geoip_country_code3 (ngx_http_geoip_module)
$geoip_country_code3 (ngx_stream_geoip_module)
$geoip_country_name (ngx_http_geoip_module)
$geoip_country_name (ngx_stream_geoip_module)
$geoip_dma_code (ngx_http_geoip_module)
$geoip_dma_code (ngx_stream_geoip_module)
$geoip_latitude (ngx_http_geoip_module)
$geoip_latitude (ngx_stream_geoip_module)
$geoip_longitude (ngx_http_geoip_module)
$geoip_longitude (ngx_stream_geoip_module)
$geoip_org (ngx_http_geoip_module)
$geoip_org (ngx_stream_geoip_module)
$geoip_postal_code (ngx_http_geoip_module)
$geoip_postal_code (ngx_stream_geoip_module)
$geoip_region (ngx_http_geoip_module)
$geoip_region (ngx_stream_geoip_module)
$geoip_region_name (ngx_http_geoip_module)
$geoip_region_name (ngx_stream_geoip_module)
$gzip_ratio
$host
$hostname (ngx_http_core_module)
$hostname (ngx_stream_core_module)
$http2
$http_
$https
$invalid_referer
$is_args
$jwt_claim_
$jwt_header_
$limit_conn_status (ngx_http_limit_conn_module)
$limit_conn_status (ngx_stream_limit_conn_module)
$limit_rate
$limit_req_status
$memcached_key
$modern_browser
$msec (ngx_http_core_module)
$msec (ngx_http_log_module)
$msec (ngx_stream_core_module)
$msie
$nginx_version (ngx_http_core_module)
$nginx_version (ngx_stream_core_module)
$pid (ngx_http_core_module)
$pid (ngx_stream_core_module)
$pipe (ngx_http_core_module)
$pipe (ngx_http_log_module)
$protocol
$proxy_add_x_forwarded_for
$proxy_host
$proxy_port
$proxy_protocol_addr (ngx_http_core_module)
$proxy_protocol_addr (ngx_stream_core_module)
$proxy_protocol_port (ngx_http_core_module)
$proxy_protocol_port (ngx_stream_core_module)
$proxy_protocol_server_addr (ngx_http_core_module)
$proxy_protocol_server_addr (ngx_stream_core_module)
$proxy_protocol_server_port (ngx_http_core_module)
$proxy_protocol_server_port (ngx_stream_core_module)
$query_string
$realip_remote_addr (ngx_http_realip_module)
$realip_remote_addr (ngx_stream_realip_module)
$realip_remote_port (ngx_http_realip_module)
$realip_remote_port (ngx_stream_realip_module)
$realpath_root
$remote_addr (ngx_http_core_module)
$remote_addr (ngx_stream_core_module)
$remote_port (ngx_http_core_module)
$remote_port (ngx_stream_core_module)
$remote_user
$request
$request_body
$request_body_file
$request_completion
$request_filename
$request_id
$request_length (ngx_http_core_module)
$request_length (ngx_http_log_module)
$request_method
$request_time (ngx_http_core_module)
$request_time (ngx_http_log_module)
$request_uri
$scheme
$secure_link
$secure_link_expires
$sent_http_
$sent_trailer_
$server_addr (ngx_http_core_module)
$server_addr (ngx_stream_core_module)
$server_name
$server_port (ngx_http_core_module)
$server_port (ngx_stream_core_module)
$server_protocol
$session_log_binary_id
$session_log_id
$session_time
$slice_range
$spdy
$spdy_request_priority
$ssl_cipher (ngx_http_ssl_module)
$ssl_cipher (ngx_stream_ssl_module)
$ssl_ciphers (ngx_http_ssl_module)
$ssl_ciphers (ngx_stream_ssl_module)
$ssl_client_cert (ngx_http_ssl_module)
$ssl_client_cert (ngx_stream_ssl_module)
$ssl_client_escaped_cert
$ssl_client_fingerprint (ngx_http_ssl_module)
$ssl_client_fingerprint (ngx_stream_ssl_module)
$ssl_client_i_dn (ngx_http_ssl_module)
$ssl_client_i_dn (ngx_stream_ssl_module)
$ssl_client_i_dn_legacy
$ssl_client_raw_cert (ngx_http_ssl_module)
$ssl_client_raw_cert (ngx_stream_ssl_module)
$ssl_client_s_dn (ngx_http_ssl_module)
$ssl_client_s_dn (ngx_stream_ssl_module)
$ssl_client_s_dn_legacy
$ssl_client_serial (ngx_http_ssl_module)
$ssl_client_serial (ngx_stream_ssl_module)
$ssl_client_v_end (ngx_http_ssl_module)
$ssl_client_v_end (ngx_stream_ssl_module)
$ssl_client_v_remain (ngx_http_ssl_module)
$ssl_client_v_remain (ngx_stream_ssl_module)
$ssl_client_v_start (ngx_http_ssl_module)
$ssl_client_v_start (ngx_stream_ssl_module)
$ssl_client_verify (ngx_http_ssl_module)
$ssl_client_verify (ngx_stream_ssl_module)
$ssl_curves (ngx_http_ssl_module)
$ssl_curves (ngx_stream_ssl_module)
$ssl_early_data
$ssl_preread_alpn_protocols
$ssl_preread_protocol
$ssl_preread_server_name
$ssl_protocol (ngx_http_ssl_module)
$ssl_protocol (ngx_stream_ssl_module)
$ssl_server_name (ngx_http_ssl_module)
$ssl_server_name (ngx_stream_ssl_module)
$ssl_session_id (ngx_http_ssl_module)
$ssl_session_id (ngx_stream_ssl_module)
$ssl_session_reused (ngx_http_ssl_module)
$ssl_session_reused (ngx_stream_ssl_module)
$status (ngx_http_core_module)
$status (ngx_http_log_module)
$status (ngx_stream_core_module)
$tcpinfo_rtt
$tcpinfo_rttvar
$tcpinfo_snd_cwnd
$tcpinfo_rcv_space
$time_iso8601 (ngx_http_core_module)
$time_iso8601 (ngx_http_log_module)
$time_iso8601 (ngx_stream_core_module)
$time_local (ngx_http_core_module)
$time_local (ngx_http_log_module)
$time_local (ngx_stream_core_module)
$uid_got
$uid_reset
$uid_set
$upstream_addr (ngx_http_upstream_module)
$upstream_addr (ngx_stream_upstream_module)
$upstream_bytes_received (ngx_http_upstream_module)
$upstream_bytes_received (ngx_stream_upstream_module)
$upstream_bytes_sent (ngx_http_upstream_module)
$upstream_bytes_sent (ngx_stream_upstream_module)
$upstream_cache_status
$upstream_connect_time (ngx_http_upstream_module)
$upstream_connect_time (ngx_stream_upstream_module)
$upstream_cookie_
$upstream_first_byte_time
$upstream_header_time
$upstream_http_
$upstream_queue_time
$upstream_response_length
$upstream_response_time
$upstream_session_time
$upstream_status
$upstream_trailer_
$uri
这么多变量是不是晕了?不用怕,很多变量其实对于日志分析是没什么用的,常用的变量在nginx两种实用的自定义访问日志格式里面有介绍,过滤掉一下没用的,基本上以下变量就够用:
$host :访问域名
$uri :请求uri
$status :http状态码
$request_method :请求方法
$request_completion :请求是否完成
$upstream_addr :反向代理的upstream
$upstream_status :upstream响应值
$scheme :请求的协议
$http_referer :referer来源
$http_user_agent :浏览器的user agent
$http_cookie : 本地所有cookie,也可以打印指定cookie,如打印session: $http_session
$args 或$arg_ : 请求时的参数
如果有不常见的需求,可以参考官方Nginx里的内置变量进行配置输出。
六、多条件判断
上面例子1-5都是单条件的,也可以使用多条件,nginx没有类似if-else多条件判断语句。涉及到多条件判断的情况,有个通用的写法,先set多个条件的变量,在if判断:
map $http_user_agent $log_ua {
~*Googlebot 1;
~*Baiduspider 1;
default 0;
}
map $http_cookie $log_cookie {
~PHPSESSION 1;
default "";
}
server {
[…]
set $logging 0;
set $logtmp '';
if ( $log_ua = 0 ) {
set $logtest "${logtmp}A";
}
if ( $log_cookie = 0 ) {
set $logtmp "${logtmp}B";
}
if ( $logtmp = "AB" ) {
set $logging 1;
}
access_log /var/log/nginx/access.log main if=$logging;
}
nginx if条件句不为0或空时,nginx会认为是true,所以上面的配置可以精简一下写法:
map $http_user_agent $log_ua {
~*Googlebot 1;
~*Baiduspider 1;
default 0;
}
map $http_cookie $log_cookie {
~PHPSESSION 1;
default "";
}
server {
[…]
set $logging 0;
set $logtmp "$log_ua$log_cookie"
if ( $logtmp = "11" ) {
set $logging 1;
}
access_log /var/log/nginx/access.log main if=$logging;
}
不用if判断也是可以的,用map的方式:
map $http_user_agent $log_ua {
~*Googlebot 1;
~*Baiduspider 1;
default 0;
}
map $http_cookie $log_cookie {
~PHPSESSION 1;
default "";
}
map “$log_ua:$log_cookie” $logging {
“1:1” 1;
default 0;
}
server {
[…]
access_log /var/log/nginx/access.log main;
access_log /var/log/nginx/http_multi_conditions.log main if=$logging;
}
nginx按条件输出日志还是挺简单的,做法就是两种方式:
- 上面例子中使用map的方式
- 例子三中第二种写法,直接使用if的方式
参考文章:
https://www.bjornjohansen.com/exclude-requests-from-nginx-access-log
https://www.nginx.com/blog/sampling-requests-with-nginx-conditional-logging/