学习

自动脱敏

在控制台输出和传输之前自动清理事件中的个人身份信息(PII)。内置对信用卡、电子邮件、IP地址、电话号码、JWT等的智能屏蔽。

宽事件(Wide events)会捕获全面的上下文信息,这使得意外记录敏感数据变得很容易。自动脱敏会在事件输出到控制台以及**传输到任何接收器(drain)**之前,对事件中的个人身份信息(PII)进行清理。

脱敏在生产环境中默认启用NODE_ENV === 'production')。在开发环境中,脱敏处于关闭状态,因此你可以看到完整值以进行调试。无需配置,直接部署即可。

关闭它,或缩小范围

如果你需要在生产环境中禁用脱敏:

export default defineNuxtConfig({
  modules: ['evlog/nuxt'],
  evlog: {
    redact: false,
  },
})

你也可以通过设置 redact: true 在开发环境中显式启用脱敏。

wide event·raw
user.email"[email protected]" ...
payment.card"4111111111111111" ...
user.ip"192.168.1.42" ...
auth"Bearer sk_live_abc123def" ...
metadata.password"hunter2-correct-horse" ...
user.phone"+33 6 12 34 56 78" ...
user.id42 ...
cart.total9999 ...
smart mask path redact untouched ready for drain · 0 PII leaked

充分屏蔽,同时保留调试所需信息

内置模式使用部分屏蔽,而不是简单地替换为 [REDACTED],在保护实际数据的同时保留足够的上下文以进行调试。

模式示例输入屏蔽输出
creditCard4111111111111111****1111
email[email protected]a***@***.com
ipv4192.168.1.100***.***.***.100
phone+33 6 12 34 56 78+33 ****5678
jwteyJhbGciOiJIUzI1NiIs...eyJ***.***
bearerBearer sk_live_abc123...Bearer ***
ibanFR76 3000 6000 0112 ...189FR76****189
127.0.0.10.0.0.0 从 IPv4 屏蔽中排除,因为它们不是真实的客户端地址。

配置

路径模式

使用单个 paths 数组,并配合点号表示法和通配符。像 password 这样的单独片段是 **.password 的简写,因此它会在任意嵌套深度屏蔽该键:

evlog: {
  redact: {
    paths: [
      'password',              // 等同于 '**.password'
      '*_token',               // 任意深度的键名通配符
      'headers.x-forwarded-for', // 精确路径
      'user.*',                // user 下的所有直接字段
    ],
  }
}
模式匹配项
user.email仅精确路径
password**.password任意深度的 password
*_token类似 access_tokenrefresh_token 的键名
user.*user.emailuser.password
audit.changes.*.password精确匹配 + 通配符片段混合

路径脱敏会将整个值(包括嵌套对象)替换为 replacement。当你需要对字段内的字符串值使用正则时,请使用 patterns

这与 auditDiff({ redactPaths: ['password'] }) 匹配,使用相同的 glob 语法,并在发射时全局应用。

选择性内置模式

仅选择你需要的模式:

evlog: {
  redact: {
    builtins: ['email', 'creditCard'],
  }
}

自定义模式

添加你自己的正则表达式模式。这些使用简单的 replacement 字符串,而非智能屏蔽:

evlog: {
  redact: {
    patterns: [/SECRET_\w+/g, /sk_live_\w+/g],
    replacement: '***',
  }
}

计算替换值

当替换内容必须根据被替换的值派生时,请传入函数而不是字符串。它会在与其他脱敏操作相同的阶段运行:在写入控制台之前、在任何 drain 之前。

常见的场景是在不暴露用于标识请求的凭据的情况下,保持请求之间的可关联性:

initLogger({
  redact: {
    patterns: [/\/public\/claim\/([A-Za-z0-9._-]{12,})/g],
    replacement: (_match, ctx) => `/public/claim/[tok:${fingerprint(ctx.groups[0])}]`,
  },
})
// /public/claim/eyJhbGciOi...  →  /public/claim/[tok:9f3a1c]

该函数会接收匹配的值和一个上下文对象:

字段类型描述
pathstring从事件根开始的点表示法路径(user.emailitems.0.token
keystring字段的叶键(email
groupsstring[]匹配 patterns 条目的捕获组。仅对 patterns 设置

对于 paths,匹配的值是整个字段值,可以是任意类型,因为路径脱敏会替换整个子树。对于 patterns,匹配的值是匹配到的子字符串。

如果函数抛出异常或返回非字符串,脱敏会回退为 [REDACTED],并记录此次失败。损坏的策略会退化为过度脱敏,而绝不会泄露它原本要清除的值。

条件策略

有些策略无法表示为路径列表:仅对特定租户屏蔽字段、仅当同级字段具有指定值时屏蔽字段,或者保留允许列表而不是拒绝列表。请使用 transform

initLogger({
  redact: {
    transform: (event) => {
      if (event.tenant === 'regulated') delete event.query
    },
  },
})

transform 会在 pathsbuiltinspatterns 之前运行,因此它看到的是原始值,而声明式规则仍会应用于它留下的内容;如果钩子遗漏了某个字段,也不会成为你的最后一道防线。在原地修改事件;它已经是一个私有副本,因此你记录的对象不会被触碰。

它必须是同步的,因为它会在写入控制台之前、事件发出路径上运行。错误会像 drain 失败一样被捕获并报告:声明式阶段仍会运行,事件也仍会被记录。

函数类型的 replacementtransform 不能在 nuxt.config.ts 或 Nitro 模块的选项中声明,因为该配置会在构建时序列化为 JSON,从而丢弃函数。请在 server plugin 中使用 initLogger(),或使用 createEvlog() 在运行时声明它们。如果仍然这样做,模块会发出构建时警告。

禁用内置模式

如果你只想使用自定义脱敏:

evlog: {
  redact: {
    builtins: false,
    paths: ['user.ssn'],
    patterns: [/INTERNAL_\w+/g],
  }
}

配置参考

选项类型默认值描述
redactboolean | RedactConfig生产环境中为 true在生产环境中默认启用。设为 false 可禁用。对象用于精细控制
pathsstring[]undefined使用点号表示法并支持通配符的路径(password**.password*_tokenuser.*
patternsRegExp[]undefined应用于字符串值的自定义正则表达式。使用统一的 replacement 字符串
builtinsfalse | string[]全部启用设为 false 可禁用内置规则。数组用于选择特定规则
replacementstring | (matched, ctx) => string'[REDACTED]'用于路径和自定义模式的替换内容。内置规则会改用智能遮蔽。函数根据匹配到的值计算替换内容
transform(event) => voidundefined为条件式、租户范围或白名单式策略提供的后门。在声明式阶段之前运行

可用的内置名称:creditCardemailipv4phonejwtbeareriban

脱敏在管道中的运行位置

脱敏在发射管道中运行,在宽事件完全构建后、但在任何输出之前执行:

  1. 转换:如果存在 transform 钩子,它会首先看到原始事件
  2. 路径脱敏:将精确路径和通配符替换为 [REDACTED]
  3. 智能屏蔽:内置模式递归扫描所有字符串值,并进行部分屏蔽
  4. 模式脱敏:自定义正则模式扫描所有字符串值,并进行简单替换
  5. 控制台输出:将已屏蔽的事件打印到 stdout
  6. Drain:将已屏蔽的事件发送到外部服务

脱敏是唯一一个在写入控制台之前运行的阶段。enrich 和 drain 会在其后运行,因此无法清理已经到达 stdout 的内容。任何需要在输出前执行的操作都应放在 transform 或函数类型的 replacement 中。

脱敏在 HTTP 响应发送之后运行,因此不会增加 API 响应的延迟。

一份可直接部署的配置

脱敏在生产环境中默认已启用。结合采样功能可以实现典型配置:

export default defineNuxtConfig({
  modules: ['evlog/nuxt'],
  evlog: {
    env: { service: 'my-app' },
  },
  $production: {
    evlog: {
      sampling: {
        rates: { info: 10, debug: 0 },
        keep: [{ status: 400 }, { duration: 1000 }],
      },
    },
  },
})

查看已脱敏事件的样子

未启用脱敏时,敏感数据会进入你的日志和传输器:

{
  "user": { "email": "[email protected]", "ip": "192.168.1.42" },
  "payment": { "card": "4111111111111111" },
  "auth": "Bearer sk_live_abc123def456"
}

启用 redact: true 后:

{
  "user": { "email": "a***@***.com", "ip": "***.***.***.42" },
  "payment": { "card": "****1111" },
  "auth": "Bearer ***"
}

相同的调试上下文,没有 PII 进入 Axiom/Datadog/Sentry。

下一步

  • 最佳实践 - 安全指南和生产检查清单
  • 采样 - 控制生产环境中的日志量
  • 配置 - 完整配置参考。