审计日志:记什么、留多久、给谁看

客户问上周三谁删了项目,你只翻到一条 DELETE 200。本文给 12 类必记事件、9 个字段与 before/after 差异写法,按 20 团队每月 3600 条复算 12 个月 16.5 MiB 存储,并给出五分钟内回答谁删了什么的验收口径。

出事后,日志要能当证据。

一、只翻到一条 DELETE 200

周五上午,一个团队客户来信:「上周三下午,谁把我的项目删了?整个看板都不见了。」

他打开应用日志,按时间筛到那条请求,只有一行:DELETE /api/projects/prj_8f21 200。没有操作人,没有工作区,也没有删除前的名字。日志能证明「删了」,不能回答「谁删的」。

以下情境为教学假设,人物、引语和数字不代表经核实的真实案例。

应用日志回答「发生了什么」,审计日志回答「谁做的」。 两者都叫日志,但字段、保留期和访问权限完全不同。

缺的那一列是 actor。应用日志围绕请求,审计日志围绕「人 + 动作 + 目标」;没有 actor,误删和定时任务在日志里长得一模一样。

客户要的也不只是追责。他要知道谁能删、删之前长什么样、能不能恢复;这三件事恰好对应审计记录里的 actor、before 和保留策略。

二、12 类事件:必记、保留与可见范围

先定清单,再写代码;清单之外的事件不加字段、不进审计表。

事件是否必记保留期谁能看
成员邀请必记24 个月Owner、Admin
成员接受必记24 个月Owner、Admin
成员移除必记24 个月Owner、Admin
角色变更必记24 个月Owner、Admin
所有权转移必记36 个月Owner
API key 签发/撤销必记24 个月Owner、Admin(只看末四位)
数据导出必记24 个月Owner、Admin
数据删除必记36 个月Owner
登录失败必记(摘要)90 天本人、Owner
SSO 配置必记36 个月Owner
账单变更必记7 年Owner、财务
集成安装选记12 个月Owner、Admin

必记与选记的界线是「能否改变权限或数据」。 能改的必须记,只读的可以抽样。

邀请和接受要分开记。邀请只证明某人发出了请求,接受才证明权限真的生效;只记一条,出事时分不清「发出去了」和「进来了」。

登录失败只记摘要:时间、账号哈希、来源 IP、失败原因。密码、验证码、完整令牌一律不落库;API key 也只留末四位,审计要回答「哪把钥匙开的门」,不需要把钥匙再抄一遍。

数据导出是最容易被忽略的一条。导出不是破坏性操作,但它常常是泄露的前一步;没有这条记录,客户问「数据出去过吗」时只能沉默。

保留期取「追责窗口」和「合规下限」中较长的一个:账单 7 年是税务口径,删除 36 个月覆盖一次年度审计,登录失败 90 天够查一次撞库。可见范围按最小必要给,Support 要看必须由客户在工单里授权。

写不进审计的事件,等于没发生。 这不是技术判断,是客户验收时的第一句话。

三、一条记录只需要 9 个字段

字段不是为了好看,是为了让「谁、何时、对什么、做了什么、变成什么」各占一格。

字段例子注意
时间2026-10-21T14:03:21.418ZUTC 存储,展示时再转时区
actorusr_7f3a / 简宁存 id,同时存显示名快照
组织org_2c91所有查询的第一道过滤
工作区ws_5d02团队档可能有多个工作区
动作project.delete固定词表,不用自然语言
目标类型project与动作前缀保持一致
目标 idprj_8f21稳定 id,不用名称
变更前后{“name”:“旧看板”}只存差异,不存整行
来源 IP/UA203.0.113.7 / Chrome 141用于异常登录判断

before/after 只存差异,不存整行。 整行会把审计表变成第二份业务数据,既贵又危险。

差异用 op / path / value 三个键表达:{"op":"replace","path":"/name","value":"旧看板"}。删除记 remove,新增记 add;读的人不需要猜测字段含义。

整行存储有三个问题:体积按行放大、敏感字段被复制、表结构一变历史记录就对不上。差异方案只碰真正变化的字段,也天然避开了「审计表里存着客户全量数据」。

审计表只允许追加。没有 UPDATE,没有 DELETE,需要更正就再写一条冲正记录;否则「不可篡改」只是一句口号。

actor 要存 id 加显示名快照。成员改名或离职后,只存 id 会让三年后的查询显示「已注销用户」,存快照才能回答「当时是谁」。

时间用 UTC,另加一个单调递增的序列号。同一毫秒内的两条记录靠序列号排序,不靠时间戳猜先后。

四、3600 条一个月,成本不在磁盘

按 20 个团队、每团队每月 180 条估算,存储小到可以忽略,真正的工作在索引与查询。

写入量 = 20 团队 × 180 条/月 = 3,600 条/月
单条 400 字节 → 3,600 × 400 = 1,440,000 字节 = 1.37 MiB/月
保留 12 个月 → 1.37 × 12 = 16.5 MiB
整行存储(均 2 KB)→ 3,600 × 2,000 = 7,200,000 字节 = 6.87 MiB/月
含索引与副本约 25% → 8.58 MiB/月,约 8.6 MiB/月
单条放大 5 倍,月体积约 6.3 倍
查询目标:谁 + 何时 + 删了什么 ≤ 5 分钟

16.5 MiB 一年的表,瓶颈只可能是索引和权限。 数据量从来不是这类系统的难点。

16.5 MiB 比一张手机照片还小。若你的审计表超过 1 GB,先查是不是把读操作也记了,而不是急着分库。

整行方案每月 8.6 MiB,一年约 103 MiB,看起来也能接受。但它的代价不是磁盘,是把客户数据复制了一份,保留期越长,泄露面越大。

查询要按「组织 + 时间」建复合索引,再按 actor 和目标 id 建两条辅助索引。没有组织前缀的查询,迟早会扫到别的租户。

导出给客户时按组织过滤,并在文件里写明导出人和导出时间;导出动作本身也要进审计。保留期到期用分区删除,按月分区删一个分区是元数据操作,用 DELETE 扫表会在最忙的时候锁住全表。

五、行动清单与复算验收

先把字段和清单补齐,再谈审计平台。

  • 固定 12 类事件名,写进一份共享词表。
  • 每条记录写 actor_id 与显示名快照。
  • before/after 用差异表达,禁止整行复制。
  • 审计表只开 INSERT 权限,禁 UPDATE 与 DELETE。
  • 按「组织 + 时间」建复合索引,再补 actor 与目标 id。
  • 按事件设定保留期,账单 7 年、删除 36 个月。
  • 后台加「按目标查历史」入口,支持按组织导出。
  • 每季度演练一次「谁在何时删了什么」。

验收标准:客户报出一个时间和一个对象,你能在五分钟内给出 actor、动作、变更前后与来源 IP;做不到,说明审计表还只是应用日志的第二份拷贝。

使用边界与实操验收

本文只讨论产品内的租户审计,不覆盖基础设施日志、法律取证与跨境数据合规。

审计日志不是应用日志的复制品。 应用日志可以采样、可以轮转、可以丢;审计日志一条都不能少。

读操作默认不记。只有涉及导出、密钥、账单的读才值得留痕;把每次列表查询都记下来,3,600 条会变成 360 万条。

审计日志本身要备份,而且备份要独立于业务库。业务库被误删时,审计记录往往是最先被一起删掉的那份。

不可篡改不等于上链。追加写、独立账号、只读副本三件事做到,已经能挡住绝大多数内部误操作。

客户导出审计记录时,要先脱敏其他租户的 id。审计表里有 actor 和 IP,跨租户泄露一次,比没有审计更糟;员工访问审计表同样要记一条,谁看了谁的记录本身就是审计的一部分。

关键参考与核验范围

核验日期:2026-10-25。以下链接用于核对日志管理与审计字段的一般做法,本文未逐条引用其内容。

引用口径。 本文只借用「事件清单 + 结构化字段 + 追加写」这一一般结构;20 团队、3,600 条、400 字节与 12 个月全部是教学假设,与上述材料无关。

核验范围。 核验的是「九个字段能否支撑五分钟回答」,不是比对某家审计服务的功能与计价。

如何自行复核。 取一条最近的删除操作,只允许看审计表,计时回答 actor、时间、目标与变更前内容;超过五分钟就补字段或补索引。

系列导航

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「Indie」更多文章

  1. 开源项目验收:十项能力、四维评分与三种不该开源的项目
  2. 治理、fork 与退出:17 位贡献者、82% 的提交与一次改协议的代价
  3. 双许可与企业版:九家询价、两家成交与 4,800 美元