3. 日志采集规范:字段标准、脱敏规则与存储周期

日志采集这块,说实话,很多团队一开始都不太重视。觉得“先把功能跑起来再说”。结果等到出了安全事件,想查日志的时候,发现要么字段不全,要么敏感信息明文暴露,要么日志已经被覆盖了。嗯,那时候再补就晚了。

我个人习惯是,在项目启动阶段就把日志规范定死。今天咱们就聊聊三个核心问题:字段该记什么、敏感信息怎么处理、日志存多久。

3.1 日志字段标准:RFC 5424 落地实践

RFC 5424 是 syslog 协议的标准。但说实话,API 日志不用完全照搬。我建议你把它当做一个“字段清单”来用,挑出对审计有用的部分。

下面这个表,是我在多个项目中总结出的“最小必要字段集”:

字段名 说明 示例值
timestamp 事件发生时间,精确到毫秒 2025-03-21T14:23:11.456Z
request_id 全局唯一请求追踪 ID req_a1b2c3d4-e5f6-7890
client_ip 客户端真实 IP(含代理透传) 203.0.113.42
http_method HTTP 方法 POST
uri_path 请求路径,不含 query /api/v2/orders
status_code HTTP 响应状态码 403
user_id 认证用户标识(如有) user_78901
app_id 调用方应用 ID app_android_3.2.1
duration_ms 请求处理耗时 127
我的习惯: 我会在日志里额外加一个 log_version 字段。版本号从 1 开始,每次字段结构有变更就递增。这样下游解析程序可以按版本兼容处理,不会因为字段增减而挂掉。

你可能会问:为什么不用 JSON 格式?其实 JSON 也可以,但 RFC 5424 的结构化数据部分(SD-ELEMENT)天然支持键值对。我个人更推荐用 JSON 格式输出,因为解析工具链更成熟。但字段名一定要统一,别今天用 user_id,明天用 userId,后天又变成 uid。

3.2 敏感信息脱敏规则

这是最容易出问题的地方。我曾经在一个客户的审计日志里,直接看到了用户的完整身份证号和银行卡号。说实话,当时后背一凉。这要是被监管查到,罚款是小事,声誉损失才是大事。

脱敏不是“删掉”,而是“保留格式但隐藏内容”。我常用的规则如下:

  • 手机号: 保留前 3 位和后 4 位,中间用 **** 替代。例如 138****5678
  • 身份证号: 保留前 6 位和后 4 位,中间用 ******** 替代。例如 110101********1234
  • 银行卡号: 保留前 4 位和后 4 位,中间用 **** **** 替代。例如 6222 **** **** 1234
  • 邮箱: 保留 @ 符号前的前 2 个字符和域名,其余用 *** 替代。例如 ab***@example.com
  • 密码/令牌: 完全脱敏,统一记录为 [REDACTED]
注意: 千万不要在日志里记录密码明文、支付 CVV、完整的 OAuth 令牌。我曾经见过一个团队把 access_ 完整打印到日志里,结果日志文件被误传到公开的 S3 桶……后果你自己想。

脱敏应该在日志输出层统一处理。我建议写一个 SensitiveDataMasker 工具类,在序列化日志之前调用。下面是一个简单的 Java 示例:

public class SensitiveDataMasker {
    private static final Pattern PHONE_PATTERN = 
        Pattern.compile("(1[3-9]\\d)\\d{4}(\\d{4})");
    
    public static String maskPhone(String raw) {
        if (raw == null || raw.length() != 11) return raw;
        return PHONE_PATTERN.matcher(raw).replaceAll("$1****$2");
    }
    
    public static String maskIdCard(String raw) {
        if (raw == null || raw.length() < 10) return raw;
        return raw.substring(0, 6) + "********" + 
               raw.substring(raw.length() - 4);
    }
    
    // 其他脱敏方法类似...
}
核心原则: 脱敏必须在日志输出层做,不能在业务代码里做。否则业务逻辑和日志逻辑耦合,后期维护成本极高。而且一旦漏掉一个业务方法,敏感信息就出去了。

3.3 日志存储周期策略

日志存多久?这个问题没有标准答案。但有几个硬性约束你必须考虑:

  • 合规要求: 等保 2.0 要求日志保存不少于 6 个月。金融行业(PCI DSS)要求至少 12 个月。某些监管机构甚至要求 3 年以上。
  • 存储成本: API 日志量通常很大。一个日均 1000 万次调用的系统,单日日志量可能达到 50-100GB。存一年就是几十 TB。
  • 查询性能: 日志量越大,查询越慢。超过 30 天的历史日志,查询响应时间会明显增加。

我建议采用“分层存储”策略。下面这张图展示了我的常用方案:

日志分层存储策略 热存储层(近 7 天) 存储介质:SSD / 高性能 ES 集群 用途:实时监控、故障排查、安全事件应急响应 温存储层(8-90 天) 存储介质:HDD / 标准 ES 集群 / 对象存储 用途:常规审计查询、月度报表、内部合规检查 冷存储层(91 天 - 2 年) 存储介质:压缩归档至对象存储(S3/MinIO) 用途:监管审计、法律取证、长期趋势分析 说明:各层时间窗口可根据业务量调整,但建议热存储不少于 3 天,冷存储不少于 1 年

具体到每个层级的策略,我建议这样:

层级 存储周期 存储介质 数据保留方式
热存储 0-7 天 SSD / 高性能 ES 原始格式,全量索引
温存储 8-90 天 HDD / 标准 ES 原始格式,按天分区
冷存储 91 天 - 2 年 对象存储(压缩) Gzip 压缩,按周打包
归档 2 年以上 磁带 / 冷归档 S3 加密压缩,仅保留必要字段
一个小技巧: 冷存储层的日志,我会去掉 request_body 和 response_body 这些大字段,只保留元数据。这样压缩比可以从 5:1 提升到 20:1。如果后续需要查具体请求内容,再从热存储或温存储里找。

关于日志轮转,我建议使用 基于时间的轮转策略,而不是基于文件大小。比如每天凌晨 3 点切分一次日志文件。配合 logrotate 或 ELK 的 ILM(Index Lifecycle Management),可以自动完成从热到冷的迁移。

避坑提醒: 我曾经遇到过一个案例,日志存储周期设了 180 天,但没考虑磁盘容量。结果第 150 天的时候磁盘满了,新日志写不进去,审计出现空白期。所以一定要设置磁盘告警阈值,比如磁盘使用率达到 80% 就触发告警。另外,冷存储的压缩任务要错开业务高峰期,我一般安排在凌晨 2-4 点执行。

最后说一句:日志规范不是写一次就完事的。随着业务发展,字段会变,脱敏规则会变,存储策略也要调整。我建议每半年 review 一次日志规范,看看有没有新的敏感字段出现,存储成本是否合理。嗯,今天就聊到这里。