Nginx请求日志采集
Nginx 请求日志记录了服务器接收到的 HTTP 请求的详细信息,用于监控、分析和调试。日志中包括诸如请求的来源 IP 地址、请求方法、响应状态码、处理时间等信息。
日志的作用:
- 监控与调试:Nginx 日志确实帮助系统管理员监控服务器运行状态,识别问题源头,如请求失败、500 内部服务器错误等,并进行相应的调试。
- 性能优化:通过分析日志中的处理时间和响应大小,可以发现可能的性能瓶颈,例如慢查询或资源瓶颈,进而对服务器配置进行优化。
- 安全分析:日志可以用于安全分析,通过检测异常的访问模式,如大量的 404 错误、403 禁止访问或 429 太多请求等,可以帮助识别潜在的恶意活动或攻击。
- 用户行为分析:通过跟踪用户的访问模式,如访问时间、频率、页面跳转等,可以更好地理解用户行为,进而优化网站内容和提升用户体验。
触发条件:
- 所有 HTTP 请求:默认情况下,Nginx 会记录所有接收到的 HTTP 请求。
- 特定事件或条件:可以通过 Nginx 的配置文件自定义日志记录的条件,例如只记录出现错误状态码的请求或特定的请求方法(如 POST、PUT)。
1.配置请求日志
1.1 日志来源
- 默认日志路径: /var/log/nginx/access.log
- 默认配置文件路径: /etc/nginx/nginx.conf
- 修改日志路径和格式: 提供示例配置(通过变更配置文件内容修改日志路径,请求日志格式)
# 主配置文件 /etc/nginx/nginx.conf
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
# Gzip 压缩设置
gzip on;
gzip_disable "msie6";
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
# 虚拟主机配置文件示例 /etc/nginx/sites-enabled/example.com.conf
sserver {
listen 80;
server_name example.com www.example.com;
access_log /var/log/nginx/example_access.log custom;
}
1.2 配置测试和重载
- 测试配置: sudo nginx -t
- 重载配置: sudo systemctl reload nginx
- 重新启动: sudo systemctl restart nginx
1.3 字段参考 Module ngx_http_log_module
字段名 | 字段用例 | 正则表达式 | 字段解释 | 是否默认 | |
1 | remote_addr | 192.168.1.1 | \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} | 客户端的 IP 地址。 | Y |
2 | remote_user | john | \S* | 客户端的用户名,如果用户是通过 HTTP 基本认证登录的。 | Y |
3 | time_local | 02/Jul/2024:06:30:20 +0000 | \d{2}/\w{3}/\d{4}:\d{2}:\d{2}:\d{2} [+\-]\d{4} | 通用日志格式的本地时间,格式为 [day/month/year:hour:minute:second timezone]。 | Y |
4 | method | GET | \S+ | HTTP方法 | Y |
5 | path | /index.html | \S+ | URL路径 | Y |
6 | protocol | HTTP | \S+ | 协议 | Y |
7 | protocol_version | 1.1 | \S+/\d\.\d | 协议版本 | Y |
8 | status | 200 | \d{3} | 响应状态码。 | Y |
9 | body_bytes_sent | 1234 | \d+ | 只表示发送给客户端的响应体的字节数,不包括响应头部的大小。 | Y |
10 | http_referer | https://example.com/previous-page | (?:https?://)?(?:[-\w]+\.)+[-\w]+(?:/\S*)? | 请求来源的页面 URL。 | Y |
11 | http_user_agent | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 | .+ | 客户端使用的浏览器或用户代理信息。 | Y |
12 | gzip_ratio | 80 /"-" | /d+\S | gzip_ratio 表示响应被压缩的比率。只有在启用了 Gzip 压缩时,gzip_ratio 变量才会存在。它显示了压缩后数据的大小与原始数据大小的比率。 | N |
13 | bytes_sent | 1234 | \d+ | 代表发送给客户端的总字节数,包括响应头部和响应体的大小。 | N |
14 | connection | 1 | /d+ | 连接序列号 | N |
15 | connection_requests | 1 | /d+ | connection_requests 表示在当前连接上处理的请求数量。每当有新请求进入相同的连接时,该计数器会增加。 | N |
16 | msec | 1625213420.123 | \d+\.\d+ | 记录日志时的时间(以秒为单位,精确到毫秒 | N |
17 | pipe | p/. | /S | 如果请求是流水线化的,则为 p,否则为 . | N |
18 | request_length | 1311 | /d+ | 请求长度(包括请求行、头部和请求体) | N |
19 | request_time | 0.123 | \d+\.\d+ | 请求处理时间,以秒为单位,精确到毫秒;记录从从客户端读取到的第一个字节到发送给客户端的最后一个字节后记录日志的时间。 | N |
20 | time_iso8601 | 2024-07-02T06:30:20+00:00 | \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+\-]\d{2}:\d{2} | ISO 8601 标准格式的本地时间。 | N |
21 | http_x_forwarded_for | 192.168.1.1, 10.0.0.1 | (?:\d{1,3}\.){3}\d{1,3}(?:,\s*(?:\d{1,3}\.){3}\d{1,3}) | 代理服务器用来记录原始客户端 IP 地址的 HTTP 头信息。 | N |
22 | upstream_response_time | 0.045 | \d+\.\d+ | 处理请求时,上游服务器的响应时间,单位为秒。 | N |
- 日志采集概述
2.1 access日志格式样例
日志样本:
111.42.240.186 - - [20/May/2024:00:55:46 +0800] "POST /myfiles?cmd=count HTTP/1.1" 200 42 "-" "Mozilla/5.0"
日志字段解释
- IP地址:111.42.240.186,表示发起请求的客户端 IP 地址。
- 认证状态:"- -" 表示请求未经过身份验证。
- 时间戳:[20/May/2024:00:55:46 +0800],记录了请求处理的时间和时区。
- 请求详情:"POST /myfiles?cmd=count HTTP/1.1" 展示了请求使用的 HTTP 方法、请求的资源路径以及查询字符串。
- 状态码:200,表示请求成功,服务器返回了所请求的资源。
- 响应大小:42,响应正文的字节数。
- 引用页:"-",表示请求没有 HTTP 引用页或未记录。
- 用户代理:"Mozilla/5.0",说明了客户端使用的浏览器类型。
日志解释:这条日志表示在2024年5月20日,一个未认证的用户使用 Mozilla 浏览器通过 POST 方法成功请求了服务器上的 /myfiles 资源,并且请求中包含了一个查询参数 cmd=count。服务器成功处理了该请求,并返回了一个200 OK的状态码,响应正文大小为42字节。请求没有明确的引用页。
2.1 access日志采集方式
Vector配置
transforms:
nginx_parsed:
inputs: [ "nginx_logs" ]
type: "remap"
source: |
if is_json(string!(.message)){
.message= parse_json!(.message)
.message=.message.log
}
.id ="测试id"
.topic ="测试topic"
.category="网关"
.agent_version = "1.1"
.type = "errorlog"
.raw_data =.message
.log_level = "INFO"
parsed_data, err = parse_nginx_log(.message, "error")
if err != null{
parsed_data, err = parse_nginx_log(.message, "combined")
.type = "accesslog"
. |= parsed_data
.timestamp = parsed_data.timestamp
.log_at = format_timestamp!(parse_timestamp!(.timestamp, "%+"), "%Y-%m-%d %H:%M:%S")
.topic="nginx_access"
.source = {
"ip": string!(.client),
"host": .host,
"product": "Nginx",
"program": "nginx",
"region": "China"
}
.strings.request=.request
.strings.http_referer=.referer
.numbers.status_code=.status
.numbers.response_size=.size
}else{
.topic="nginx_error"
. |= parsed_data
.timestamp = parsed_data.timestamp
.log_at = format_timestamp!(parse_timestamp!(.timestamp, "%+"), "%Y-%m-%d %H:%M:%S")
.source = {
"ip": string!(.client),
"host": .host,
"product": "Nginx",
"program": "nginx",
"region": "China"
}
.strings.request=.request
parsed_request=parse_regex!(.request, r'^\s*(?P<method>\S+)\s*(?P<path>\S+)\s*(?P<protocol>\w+*)\/(?P<protocol_version>\S+)')
.parsed_request=parsed_request
if parsed_request !=null{
.strings = merge(.strings,.parsed_request)
}
.strings.http_referer=.referer
.strings.upstream=.upstream
.strings.server=.server
.numbers.pid=.pid
.numbers.tid=.tid
.numbers.cid=.cid
.log_level = parsed_data.severity
}
filter_err_logs:
inputs: ["nginx_parsed"]
type: "filter"
condition: .type == "errorlog"
filter_access_logs:
inputs: ["nginx_parsed"]
type: "filter"
condition: .type == "accesslog"
2.3 access日志格式样例解析结果
{
"agent": "Mozilla/5.0",
"agent_version": "1.1",
"category": "网关",
"client": "111.42.240.181",
"file": "access.log",
"host": "VM-4-7-ubuntu",
"id": "测试id",
"log_at": "2024-05-19 16:55:46",
"log_level": "INFO",
"message": "111.42.240.186 - - [20/May/2024:00:55:46 +0800] \"POST /myfiles?cmd=count HTTP/1.1\" 200 42 \"-\" \"Mozilla/5.0\"",
"numbers": {
"response_size": 42,
"status_code": 200
},
"raw_data": "111.42.240.186 - - [20/May/2024:00:55:46 +0800] \"POST /myfiles?cmd=count HTTP/1.1\" 200 42 \"-\" \"Mozilla/5.0\"",
"referer": "-",
"request": "POST /myfiles?cmd=count HTTP/1.1",
"size": 42,
"source": {
"host": "VM-4-7-ubuntu",
"ip": "111.42.240.186",
"product": "Nginx",
"program": "nginx",
"region": "China"
},
"status": 200,
"strings": {
"http_referer": "-",
"method": "POST",
"path": "/myfiles?cmd=count",
"protocol": "HTTP",
"protocol_version": "1.1",
"request": "POST /myfiles?cmd=count HTTP/1.1",
"server": null,
"upstream": null
},
"timestamp": "2024-05-19T16:55:46Z",
"topic": "nginx_access",
"type": "accesslog"
}
- 注意点
3.1 nginx 默认位置没有日志文件处理方法
查找Nginx配置文件,使用 find 命令在整个系统中搜索 nginx.conf 文件:
bash:
sudo find / -name "nginx.conf"
找到配置文件后,使用文本编辑器(如 vim)打开它进行编辑:
bash:
sudo vim /etc/nginx/nginx.conf
在配置文件中,查找 access_log 和 error_log 的设置。确保它们指向有效的路径,并且这些路径存在:
nginx:
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;
确认日志目录存在:
检查日志文件指定的目录是否存在。如果不存在,需要创建它们:
bash
sudo mkdir -p /var/log/nginx
设置正确的权限:
确保 Nginx 进程(通常是由 www-data 用户运行)有权限写入日志文件的目录。
bash
sudo chown -R www-data:www-data /var/log/nginx
sudo chmod -R 755 /var/log/nginx
测试配置文件:
在修改配置文件后,使用以下命令测试配置文件是否有语法错误。
bash
sudo nginx -t
重载或重启 Nginx:
如果配置测试通过,重载或重启 Nginx 以应用更改。
bash
sudo systemctl restart nginx
3.2 一条日志既不符合access log格式 也不符合error log 提取
只保留原数据massage字段其他字段为空存入数据库
3.3 docker容器部署的nginx处理:
3.3.1. 读取docker nginx logs输出 :参考Docker logs | Vector documentation
sources:
docker_logs:
type: "docker_logs"
include_containers: ["nginx"]
3.3.2. 读取docker容器文件 :
sources:
container_logs:
type: "file"
# 包含要监控的日志文件的路径,可以使用通配符
include: ["/path/to/container/logs/*.log"]
# 从文件末尾开始读取,只捕获新日志
read_from: "end"
3.4 某些 Nginx 配置可能不包括所有字段。例如,gzip_ratio 和 upstream_response_time 字段仅在启用特定模块或配置时才会出现。确认 Nginx 配置是否启用了相关模块,并根据实际日志内容调整解析规则。参考:Module ngx_http_log_module
在配置文件中,查找 gzip_ratio 和 upstream_response_time 的设置是否启用:
nginx:
# 主配置文件 /etc/nginx/nginx.conf
#只有在 Nginx 配置中启用了 Gzip 压缩时,$gzip_ratio 字段才会出现在日志中。如果配置了 upstream 和反向代理,$upstream_response_time 字段才会被记录。
http {
# 设置日志格式,包含 gzip_ratio 和 upstream_response_time 字段
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'$gzip_ratio $upstream_response_time';
# 设置访问日志路径和格式
access_log /var/log/nginx/access.log main;
# 启用 Gzip 压缩
gzip on;
gzip_disable "msie6";
gzip_proxied any;
gzip_min_length 1000;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# 反向代理配置,记录 upstream_response_time
upstream backend {
server backend1.example.com;
server backend2.example.com;
}
server {
listen 80;
server_name example.com www.example.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 记录响应时间
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
}
# 设置独立的访问日志文件
access_log /var/log/nginx/example_access.log main;
}
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
3.5 不同环境中的兼容性:Nginx 的日志配置和格式可能在不同版本中有所变化。确认使用的 Nginx 版本,并根据文档或实际情况调整配置和解析规则。
1. 确认 Nginx 版本
通过以下命令确认当前使用的 Nginx 版本:
nginx -v
输出类似如下:
nginx version: nginx/1.18.0
2. 查阅 Nginx 官方文档
根据确认的版本号,查阅相应版本的 Nginx 官方文档。可以访问 Nginx 官方文档 来查找不同版本的文档。
3. 示例:日志配置在不同版本中的变化
假设你发现 $request_time 字段在旧版本中未包含或格式有变化,而在较新版本中已经被支持或格式有改动:
Nginx 1.16.0 日志配置:
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
}
Nginx 1.18.0 日志配置:
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'$request_time';
access_log /var/log/nginx/access.log main;
}
4. 调整解析规则
如果你的环境使用的是 Nginx 1.16.0,并且你希望解析 $request_time,那么你需要在升级到 Nginx 1.18.0 后,调整解析规则以支持新的日志格式。可以在配置文件中添加新的字段或调整现有字段的顺序和格式。
5. 验证配置兼容性
在做出任何配置更改后,建议重新加载 Nginx 配置并测试日志输出,以确保所有新字段都被正确记录和解析:
nginx -s reload
tail -f /var/log/nginx/access.log
4. 日志字段分析用途
remote_addr
- 用途:追踪客户端的IP地址,识别访问者地理位置和访问源。
- 分析:如果发现某个IP地址频繁请求不存在的页面,可能表明有爬虫或扫描器在活动。
remote_user
- 用途:记录认证的客户端用户名。
- 分析:监控特定用户的活动,有助于识别滥用或异常行为。
time_local
- 用途:记录请求的本地时间。
- 分析:通过分析请求时间,可以发现访问高峰时段,为服务器负载管理提供参考。
method
- 用途:记录请求使用的HTTP方法。
- 分析:GET请求通常用于数据检索,而POST和其他方法可能用于数据提交,分析这些数据有助于了解用户行为模式。
path
- 用途:记录请求的资源路径。
- 分析:热门或异常路径的识别可以帮助优化资源分配和安全策略。
protocol
- 用途:记录使用的协议名称。
- 分析:确保使用安全协议,如HTTPS,有助于保护数据传输安全。
status
- 用途:记录HTTP响应状态码。
- 分析:高频率的4xx或5xx状态码可能表明用户体验问题或服务器端错误。
body_bytes_sent
- 用途:记录发送给客户端的响应正文的字节数。
- 分析:正文大小的统计有助于评估服务器的数据传输效率。
http_referer
- 用途:记录导致本次请求的引用页面URL。
- 分析:分析用户是如何找到网站的,评估营销活动的效果或识别潜在的恶意引用。
http_user_agent
- 用途:记录客户端浏览器和操作系统信息。
- 分析:用户代理的分析有助于理解用户群体的浏览器使用情况,优化网页兼容性。
bytes_sent
- 用途:记录发送给客户端的总字节数。
- 分析:监控带宽使用,为带宽规划和成本控制提供数据支持。
connection_requests
- 用途:记录单个连接中的请求数量。
- 分析:评估连接复用情况,识别长连接或慢客户端问题。
msec
- 用途:记录请求处理的毫秒数。
- 分析:毫秒级的响应时间分析有助于识别慢请求,优化服务器性能。
request_length
- 用途:记录请求的总长度。
- 分析:长请求可能影响服务器性能,需要特别关注。
request_time
- 用途:记录从接收到请求到发送响应的总时间。
- 分析:长时间请求的分析有助于发现性能瓶颈。
upstream_response_time
- 用途:记录后端服务的响应时间。
- 分析:如果上游响应时间长,可能表明需要优化后端服务或增加服务器资源。
5. 参考
- Nginx 官方文档: 用于理解 Nginx 配置和日志处理。