Nginx利用访问日志、错误日志和请求跟踪进行调试和故障排除

TrumanWong
6/21/2024
TrumanWong

简介

日志记录是了解应用的基础。使用 NGINX 可有效控制对您和您的应用有意义的日志信息。NGINX允许您根据不同的上下文将日志以不同的格式拆分到不同的文件中,并更改错误日志的日志级别,以更深入地了解当前状况。NGINX 支持对接 Syslog,天生就具备了将日志以流的形式输出给集中式日志服务器的能力。NGINX支持为请求发起全链路跟踪。本文将介绍访问和错误日志,基于 Syslog协议的流式传输,以及利用 NGINX 所生成的请求标识符的实现端到端请求跟踪。

配置访问日志

问题

需要配置访问日志格式,以将内置变量添加到请求日志中。

解决方案

配置访问日志格式:

...
load_module "modules/ngx_http_geoip_module.so";
load_module "modules/ngx_stream_geoip_module.so";
...
http {
    # 下载链接 https://www.miyuru.lk/geoiplegacy
    geoip_country /etc/nginx/Maxmind/Maxmind-Country.dat; # the country IP data
    geoip_city /etc/nginx/Maxmind/Maxmind-City.dat; # the city IP data
    geoip_proxy 0.0.0.0/0;
    geoip_proxy_recursive on;
    
    log_format geoproxy
        '[$time_local] $remote_addr '
        '$realip_remote_addr $remote_user '
        '$proxy_protocol_server_addr $proxy_protocol_server_port '
        '$request_method $server_protocol '
        '$scheme $server_name $uri $status '
        '$request_time $body_bytes_sent '
        '$geoip_city_country_code3 $geoip_region '
        '"$geoip_city" $http_x_forwarded_for '
        '$upstream_status $upstream_response_time '
        '"$http_referer" "$http_user_agent"';
    # ...
}

该日志格式配置被命名为 geoproxy,使用了大量内置变量来演示 NGINX 日志记录功能。该配置可输出发起请求时服务器上的本地时间、打开连接的 IP 地址,以及客户端的 IP 地址,因为 NGINX 根据 geoip_proxyreal_header 指令知道这些信息。

server 下的 listen 指令使用了 proxy_protocol 参数时,带 $proxy_protocol_server_前缀的变量可用于从 PROXY 协议头中获取服务端相关信息。$remote_user 表示通过 Basic认证的用户名;后面还包含请求方法、协议版本和协议类型,例如 HTTPHTTPS。当然也记录了匹配的 server 上下文名称、请求 URI 和响应状态码。

记录的统计信息包括以毫秒为单位的处理时间以及发送给客户端的消息体大小。此外,有关国家、地区和城市的信息也被记录在内。X-Forwarded-For HTTP 请求头可表明请求是否来自其它代理服务器转发。upstream 模块支持一些用于显示上游服务器响应状态和上游请求响应时间的内置变量。最后,有关客户端请求来源和客户端所用浏览器的信息也被记录在内。

log_format 指令仅在 HTTP 上下文中有效。可选参数 escape 可指定字符串转义类型,可选值有:defaultjsonnonenone 表示禁用转义字符。default 时,字符 "、\ 以及其他值小于 32 或大于 126 的字符用 \xXX 来转义。如果找不到变量值,则使用连字符-替代。对于json 转义,JSON 字符串中所有不允许的字符都将被转义,如:字符 "\\"\\ 来表示,值小于 32 的字符用 \n\r\t\b\f\u00XX 来表示。

此日志配置的日志条目如下所示:

[21/Jun/2024:05:59:18 +0000] 172.28.0.1 172.28.0.1 - - - POST HTTP/1.1 https serverapi.trumanwl.com /api/curl-convert 200 0.003 478 TWN 00 "Taichung" 211.75.74.223 200 0.002 "https://trumanwl.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"

如要使用此日志格式,则使用 access_log 指令,并将日志文件路径和格式名称geoproxy 用作参数:

server {
	access_log /var/log/nginx/access.log geoproxy;
    # ...
}

access_log 指令使用日志文件路径和格式名称作为参数。该指令在许多上下文中都有效,可根据上下文采用不同的日志路径和/或日志格式。bufferflushgzip 等参数可配置日志写入日志文件的频率以及文件是否被压缩。还可通过 if 指令设置输出条件,如果条件值为 0 或空字符串,则访问将不被记录。

详解

NGINX 中的log模块允许您为许多不同的场景配置日志格式,以根据需要记录到不同日志文件中。在实际运用中,为不同上下文配置不同的日志格式非常有用,您可以在日志中使用不同的模块和内置变量,或者集中提供所有必要信息。日志也能记录以JSON 格式和 XML 格式创建的字符串。这些日志将帮助您了解流量模式、客户端使用情况和客户端来源等。此外,访问日志还可以帮助您发现与上游服务器或特定 URI 相关的响应延迟和问题。访问日志还可用于在测试环境中解析和回放流量模式,以模仿真实的用户交互。日志在应用故障排除、调试或市场分析中发挥着至关重要的作用。

配置错误日志

问题

需要配置错误日志,以更深入地了解 NGINX 服务器问题。

解决方案

使用 error_log 指令定义日志路径和日志级别:

error_log /var/log/nginx/error.log warn;

在配置 error_log 指令时,路径是必选的,但日志级别是可选的,默认为 error。除 if语句以外,该指令在所有上下文中都有效。日志级别包括 debug(调试)、info(信息)、notice(注意)、warn(警告)、error(错误)、crit(严重)、alert(警报)和 emerg(紧急)。上述日志级别的排列顺序也是严重程度从低到高的顺序。debug日志级别仅在 NGINX 配置了 --with-debug 标记时可用。

详解

当配置文件无法正常工作时,应首先查看错误日志。错误日志也是定位由 FastCGI 等应用服务器所产生的错误的利器。还可以使用错误日志来调试 worker 进程连接、内存分配、客户端 IP 和服务器。错误日志虽然不支持自定义日志格式,但也遵循特定格式 —— 先列出日期,随后列出日志级别和具体消息。

转发日志到 Syslog

问题

需要将日志转发到 Syslog 侦听器,以便将日志聚合到集中式日志服务中。

解决方案

使用 error_logaccess_log 指令将日志发送到 Syslog 侦听器:

error_log syslog:server=10.0.1.42 debug;
access_log syslog:server=10.0.1.42,tag=nginx,severity=info geoproxy;

error_logaccess_log 指令的 syslog 参数后面紧跟冒号和一些参数选项。这些选项包括必要的日志服务标识,如:需要连接的 IP 地址、DNS 名称或 Unix socket,以及 facilityseveritytagnohostname 等可选标记。server 选项在指定指 IP 地址或 DNS 名称时设置端口号,默认为 UDP 514facility 选项是指日志消息的 facility值,取 Syslog RFC 标准定义的 23 个值之一;默认值为 local7tag 选项表示消息的标记,默认值为 nginxseverity 选项默认为 info,表示所发送消息的日志级别。nohostname 标记禁止将 hostname 字段添加到 syslog 消息头中,不取值。

详解

Syslog 是一种在单台服务器或服务器集群上发送和收集日志消息的标准协议。在多个主机上运行相同服务的多个实例时,将日志发送到集中位置有助于调试。这被称作聚合日志。聚合日志允许您在一个地方查看日志,无需切换服务器,并通过时间戳将日志文件合并在一起。常见的日志聚合堆栈有 ElasticsearchLogstashKibana,统称为 ELK 堆栈。NGINX 可使用 access_logerror_log 指令,轻松将这些日志传输到 Syslog 侦听器。

请求跟踪

问题

需要将 NGINX 日志与应用日志相关联,以了解请求的端到端处理过程。

解决方案

使用request的唯一识别变量,并将其传递给应用以进行记录:

log_format trace '$remote_addr - $remote_user [$time_local] '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "$http_user_agent" '
    '"$http_x_forwarded_for" $request_id';
upstream backend {
    server 10.0.0.42;
}
server {
	listen 80;

	# 将 X-Request-ID 标头添加到对客户端的响应中
	add_header X-Request-ID $request_id;
	location / {
		proxy_pass http://backend;
		# 将 X-Request-ID 标头发送到应用端
		proxy_set_header X-Request-ID $request_id;
		access_log /var/log/nginx/access_trace.log trace;
	}
}

在本示例配置中,配置了名为 tracelog_format,并在日志中使用了 $request_id 变量。还在发起上游请求时,通过使用 proxy_set_header 指令将请求 ID 添加到请求头里,将此 $request_id 变量传递给了上游应用。此外,还通过使用 add_header 指令,在响应头中设置请求 ID,将请求 ID 传回客户端。

详解

该功能在 NGINX Plus R10NGINX 1.11.0 版本中可用,$request_id 提供了一个随机生成的 32 位十六进制字符串,这些字符可以用来唯一性标识请求。通过将此标识符传递给客户端和应用,您可以将日志与请求相关联。在前端客户端,你可通过响应头收到该请求唯一性标识字符串,并且可以利用它在日志中搜索与请求相关联的条目。您还需要在应用日志中捕获和记录此请求唯一性标头,从而在日志之间建立真正的端到端关联。基于这项功能,NGINX 让跨应用栈跟踪请求变为可能。