接口日志捕获:API 网关、反向代理与应用层的采集方法

日志是做审计和异常检测的「原材料」。原材料质量不行,后面再好的分析算法也白搭。我在早期接手过一个安全项目,团队花了两周搭了一套炫酷的实时检测仪表盘,结果上线第一天就发现日志里缺了 30% 的请求——API 网关根本没配请求体日志。嗯,这次教训让我养成了一个习惯:先捋清楚日志到底在哪、怎么采、采到什么粒度。

4.1 日志来源的三层架构

从请求进来到处理结束,接口日志通常经过三层:

  • API 网关层:对外统一入口,最容易拿到全局视图。
  • 反向代理层:Nginx、Apache 这类,负责路由和负载均衡。
  • 应用层:业务代码内部,最详细但也最分散。

下面这张图能帮你快速建立整体印象。我习惯先画一个「日志流向图」再动手配置,避免漏采。

接口日志三层捕获架构 API 网关层 Kong / APISIX / AWS API Gateway 反向代理层 Nginx / Apache / HAProxy 应用层 Spring Boot / Go / Node.js 日志收集 中心化存储 每层日志格式、字段、时效性不同 需要分层采集后做关联合并

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
  • 统一敏感字段脱敏规则:三家都按同一套正则替换,不然分析时有的脱了有的没脱,容易误报

我推荐在架构早期就制定一份《日志采集规范文档》,包含字段列表、格式样例、脱敏策略。不要等到出安全事件后再补,那时候很多日志已经被覆盖了。