宽事件(Wide events)会捕获全面的上下文信息,这使得意外记录敏感数据变得很容易。自动脱敏会在事件输出到控制台以及**传输到任何接收器(drain)**之前,对事件中的个人身份信息(PII)进行清理。
脱敏在生产环境中默认启用(NODE_ENV === 'production')。在开发环境中,脱敏处于关闭状态,因此你可以看到完整值以进行调试。无需配置,直接部署即可。
关闭它,或缩小范围
如果你需要在生产环境中禁用脱敏:
export default defineNuxtConfig({
modules: ['evlog/nuxt'],
evlog: {
redact: false,
},
})
import { createEvlog } from 'evlog/next'
export const { withEvlog, useLogger } = createEvlog({
service: 'my-app',
redact: false,
})
import { initLogger } from 'evlog'
initLogger({
env: { service: 'my-app' },
redact: false,
})
你也可以通过设置 redact: true 在开发环境中显式启用脱敏。
充分屏蔽,同时保留调试所需信息
内置模式使用部分屏蔽,而不是简单地替换为 [REDACTED],在保护实际数据的同时保留足够的上下文以进行调试。
| 模式 | 示例输入 | 屏蔽输出 |
|---|---|---|
creditCard | 4111111111111111 | ****1111 |
email | [email protected] | a***@***.com |
ipv4 | 192.168.1.100 | ***.***.***.100 |
phone | +33 6 12 34 56 78 | +33 ****5678 |
jwt | eyJhbGciOiJIUzI1NiIs... | eyJ***.*** |
bearer | Bearer sk_live_abc123... | Bearer *** |
iban | FR76 3000 6000 0112 ...189 | FR76****189 |
127.0.0.1 和 0.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_token、refresh_token 的键名 |
user.* | user.email、user.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]
该函数会接收匹配的值和一个上下文对象:
| 字段 | 类型 | 描述 |
|---|---|---|
path | string | 从事件根开始的点表示法路径(user.email、items.0.token) |
key | string | 字段的叶键(email) |
groups | string[] | 匹配 patterns 条目的捕获组。仅对 patterns 设置 |
对于 paths,匹配的值是整个字段值,可以是任意类型,因为路径脱敏会替换整个子树。对于 patterns,匹配的值是匹配到的子字符串。
如果函数抛出异常或返回非字符串,脱敏会回退为 [REDACTED],并记录此次失败。损坏的策略会退化为过度脱敏,而绝不会泄露它原本要清除的值。
条件策略
有些策略无法表示为路径列表:仅对特定租户屏蔽字段、仅当同级字段具有指定值时屏蔽字段,或者保留允许列表而不是拒绝列表。请使用 transform:
initLogger({
redact: {
transform: (event) => {
if (event.tenant === 'regulated') delete event.query
},
},
})
transform 会在 paths、builtins 和 patterns 之前运行,因此它看到的是原始值,而声明式规则仍会应用于它留下的内容;如果钩子遗漏了某个字段,也不会成为你的最后一道防线。在原地修改事件;它已经是一个私有副本,因此你记录的对象不会被触碰。
它必须是同步的,因为它会在写入控制台之前、事件发出路径上运行。错误会像 drain 失败一样被捕获并报告:声明式阶段仍会运行,事件也仍会被记录。
replacement 和 transform 不能在 nuxt.config.ts 或 Nitro 模块的选项中声明,因为该配置会在构建时序列化为 JSON,从而丢弃函数。请在 server plugin 中使用 initLogger(),或使用 createEvlog() 在运行时声明它们。如果仍然这样做,模块会发出构建时警告。禁用内置模式
如果你只想使用自定义脱敏:
evlog: {
redact: {
builtins: false,
paths: ['user.ssn'],
patterns: [/INTERNAL_\w+/g],
}
}
配置参考
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
redact | boolean | RedactConfig | 生产环境中为 true | 在生产环境中默认启用。设为 false 可禁用。对象用于精细控制 |
paths | string[] | undefined | 使用点号表示法并支持通配符的路径(password、**.password、*_token、user.*) |
patterns | RegExp[] | undefined | 应用于字符串值的自定义正则表达式。使用统一的 replacement 字符串 |
builtins | false | string[] | 全部启用 | 设为 false 可禁用内置规则。数组用于选择特定规则 |
replacement | string | (matched, ctx) => string | '[REDACTED]' | 用于路径和自定义模式的替换内容。内置规则会改用智能遮蔽。函数根据匹配到的值计算替换内容 |
transform | (event) => void | undefined | 为条件式、租户范围或白名单式策略提供的后门。在声明式阶段之前运行 |
可用的内置名称:creditCard、email、ipv4、phone、jwt、bearer、iban。
脱敏在管道中的运行位置
脱敏在发射管道中运行,在宽事件完全构建后、但在任何输出之前执行:
- 转换:如果存在
transform钩子,它会首先看到原始事件 - 路径脱敏:将精确路径和通配符替换为
[REDACTED] - 智能屏蔽:内置模式递归扫描所有字符串值,并进行部分屏蔽
- 模式脱敏:自定义正则模式扫描所有字符串值,并进行简单替换
- 控制台输出:将已屏蔽的事件打印到 stdout
- Drain:将已屏蔽的事件发送到外部服务
脱敏是唯一一个在写入控制台之前运行的阶段。enrich 和 drain 会在其后运行,因此无法清理已经到达 stdout 的内容。任何需要在输出前执行的操作都应放在 transform 或函数类型的 replacement 中。
一份可直接部署的配置
脱敏在生产环境中默认已启用。结合采样功能可以实现典型配置:
export default defineNuxtConfig({
modules: ['evlog/nuxt'],
evlog: {
env: { service: 'my-app' },
},
$production: {
evlog: {
sampling: {
rates: { info: 10, debug: 0 },
keep: [{ status: 400 }, { duration: 1000 }],
},
},
},
})
import { createEvlog } from 'evlog/next'
export const { withEvlog, useLogger } = createEvlog({
service: 'my-app',
sampling: {
rates: { info: 10, debug: 0 },
keep: [{ status: 400 }, { duration: 1000 }],
},
})
import { initLogger } from 'evlog'
initLogger({
env: { service: 'my-app' },
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。