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]
脱敏应该在日志输出层统一处理。我建议写一个 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 天的历史日志,查询响应时间会明显增加。
我建议采用“分层存储”策略。下面这张图展示了我的常用方案:
具体到每个层级的策略,我建议这样:
| 层级 | 存储周期 | 存储介质 | 数据保留方式 |
|---|---|---|---|
| 热存储 | 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),可以自动完成从热到冷的迁移。
最后说一句:日志规范不是写一次就完事的。随着业务发展,字段会变,脱敏规则会变,存储策略也要调整。我建议每半年 review 一次日志规范,看看有没有新的敏感字段出现,存储成本是否合理。嗯,今天就聊到这里。