接口日志捕获:API 网关、反向代理与应用层的采集方法
日志是做审计和异常检测的「原材料」。原材料质量不行,后面再好的分析算法也白搭。我在早期接手过一个安全项目,团队花了两周搭了一套炫酷的实时检测仪表盘,结果上线第一天就发现日志里缺了 30% 的请求——API 网关根本没配请求体日志。嗯,这次教训让我养成了一个习惯:先捋清楚日志到底在哪、怎么采、采到什么粒度。
4.1 日志来源的三层架构
从请求进来到处理结束,接口日志通常经过三层:
- API 网关层:对外统一入口,最容易拿到全局视图。
- 反向代理层:Nginx、Apache 这类,负责路由和负载均衡。
- 应用层:业务代码内部,最详细但也最分散。
下面这张图能帮你快速建立整体印象。我习惯先画一个「日志流向图」再动手配置,避免漏采。
4.2 API 网关日志捕获
API 网关是所有流量的「咽喉」。在这个位置捕日志,能同时看到入站和出站数据,还能顺便记录认证结果、限流信息。我个人习惯优先配置网关层,因为它最难后补——很多网关只保存最近几小时日志,过期就没了。
4.2.1 关键配置项
| 字段 | 说明 | 推荐值 |
|---|---|---|
| 请求方法 + URI | 唯一标识接口 | 开启 |
| 请求头(关键) | Authorization、User-Agent、X-Forwarded-For | 确保 Header 不被截断 |
| 请求体 | POST / PUT 的 payload | 只对敏感接口开启,注意脱敏 |
| 响应状态码 | HTTP 200 / 4xx / 5xx | 必须记录 |
| 响应耗时 | 单位毫秒 | 需开启 upstream_latency |
注意:千万不要在网关层明文记录密码、信用卡号、 等敏感字段。我曾经看到过一个网关配置,直接把整个请求体 JSON 原样写进日志——这是审计事故,不是审计素材。
4.2.2 Kong 网关配置示例
# 启用 file-log 插件
curl -X POST http://localhost:8001/services/your-service/plugins \
--data "name=file-log" \
--data "config.path=/var/log/kong/access.log" \
--data "config.reopen=true" \
--data "config.custom_fields_by_lua=request_body,response_body"
# 注意:custom_fields_by_lua 需要谨慎启用,大量请求体日志会拉爆磁盘
建议对日志做滚动切割:按天或按小时分文件,保留最近 7 天。我习惯用 logrotate 统一管理,避免磁盘写满导致网关夯住。
4.3 反向代理日志(Nginx / Apache)
很多团队没有独立 API 网关,直接靠 Nginx 或 Apache 做入口。这时候反向代理日志就是最外层的审计证据。说实话,Nginx 的日志格式可以做得非常灵活,但默认的 combined 格式远不够用。
4.3.1 Nginx 自定义日志格式
http {
log_format sensitive '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time '
'"$http_x_forwarded_for"';
access_log /var/log/nginx/api_access.log sensitive;
}
这里 `$request_time` 和 `$upstream_response_time` 是我必加的字段。比较两者能快速判断瓶颈在哪——请求时间远大于上游时间?说明 Nginx 自己在排队。反过来?那就是后端慢了。
4.3.2 Apache 日志格式
<IfModule log_config_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\"
\"%{User-agent}i\" %D %{X-Forwarded-For}i" extended
CustomLog /var/log/apache2/api_access.log extended
</IfModule>
Apache 里 `%D` 是微秒级耗时,但注意它不包含到客户端的网络传输时间。我踩过这个坑:有一次日志显示后端很慢,结果查了半天发现是客户端带宽不够,传输占了大部分时间。
关键点:反向代理层日志必须包含「客户端真实 IP」。很多请求经过 CDN 或七层负载均衡后,$remote_addr 变成了内网网关地址。务必开启 X-Forwarded-For 或 X-Real-IP 解析。
4.4 应用层日志采集
应用层日志最细致,但也是最容易「各自为政」的层。我记得有一家客户,每个微服务用自己的日志框架、格式、字段命名都不统一,最后做关联分析时简直要命。
4.4.1 结构化日志(推荐)
别再打 `[INFO] 下单成功` 这种纯文本了。用 JSON 格式,日志本身就是可解析的数据。
// Java (Logback + logstash-encoder)
{
"timestamp": "2025-02-18T10:30:15.123+08:00",
"level": "INFO",
"service": "order-service",
"traceId": "abc123xyz",
"spanId": "span001",
"userId": 10086,
"requestId": "req-007",
"message": "下单成功",
"cost_ms": 45
}
traceId 和 spanId 一定要带。没有调用链的日志,碰到跨服务异常时只能傻眼。我习惯在网关层就注入 traceId,一路透传到所有下游服务。
4.4.2 采集方式对比
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Filebeat → Elasticsearch | 轻量、部署简单 | 丢失敏感字段无告警 | 中小规模 |
| Fluentd → Kafka → Logstash | 高吞吐、可缓冲 | 运维复杂度高 | 大规模集群 |
| 直接 SDK(如 log4j2 SocketAppender) | 实时性最好 | 应用层耦合重 | 低延迟监控场景 |
应用层日志最容易犯的错误:把异常堆栈截断。Java 的 StackTrace 可能长达几百行,默认日志框架只保留前面几行。你想想看,真正的根因在第 50 行,你只采到前 20 行,等于白记。务必检查 appender 的 maxLength 或 queueSize。
4.4.3 避坑指南:日志与业务同步
我曾经在审计一个支付接口时发现,应用层日志比数据库里实际记录多了一条。原因很简单——日志先打印,数据库后提交,然后事务回滚了。日志里永远多了一些「幽灵请求」。现在我的做法是:关键业务日志在事务提交后才写,或者在日志里加上事务状态字段。
4.5 三层日志的统一性要求
三层日志各自独立采集,但最终要能关联在一起。最重要的三个统一元素:
- 统一时间戳格式:全部用 UTC+ISO8601,别有的用timestamp毫秒,有的用datetime
- 统一请求标识:requestId 从网关透传到应用,反向代理层也记这个 ID
- 统一敏感字段脱敏规则:三家都按同一套正则替换,不然分析时有的脱了有的没脱,容易误报
我推荐在架构早期就制定一份《日志采集规范文档》,包含字段列表、格式样例、脱敏策略。不要等到出安全事件后再补,那时候很多日志已经被覆盖了。