字段验证
📖 使用教程 · 完整 API 签名见 结构化字段 - Field 构造器
DD 的字段验证子系统在日志写入前对结构化字段的键名进行命名约定校验和安全防护。它能防止字段键不一致导致的日志解析困难,并拦截通过字段键注入的恶意内容。
验证模式
| 模式 | 常量 | 行为 |
|---|---|---|
| 关闭(默认) | FieldValidationNone | 不校验,所有键均接受 |
| 警告 | FieldValidationWarn | 不合规范的键输出 stderr 警告,日志仍写入 |
| 严格 | FieldValidationStrict | 不合规范的键输出 stderr 错误,日志仍写入 |
日志方法不返回错误
由于日志方法(InfoWith 等)不返回 error,验证失败只能通过 stderr 输出提示。严格模式不会阻止日志写入,但会在 stderr 明确报错。
命名约定
| 约定 | 常量 | 示例 |
|---|---|---|
| 任意(默认) | NamingConventionAny | 不校验命名风格 |
| snake_case | NamingConventionSnakeCase | user_id、created_at |
| camelCase | NamingConventionCamelCase | userId、createdAt |
| PascalCase | NamingConventionPascalCase | UserId、CreatedAt |
| kebab-case | NamingConventionKebabCase | user-id、created-at |
快速启用
方案一:预设配置
go
package main
import (
"log"
"github.com/cybergodev/dd"
)
func main() {
cfg := dd.DefaultConfig()
cfg.FieldValidation = dd.StrictSnakeCaseConfig()
// 等价于:
// &dd.FieldValidationConfig{
// Mode: dd.FieldValidationStrict,
// Convention: dd.NamingConventionSnakeCase,
// AllowCommonAbbreviations: true,
// EnableSecurityValidation: true,
// }
logger, err := dd.New(cfg)
if err != nil {
log.Fatal(err)
}
defer logger.Close()
logger.InfoWith("用户操作",
dd.String("user_id", "123"), // ✅ 符合 snake_case
dd.String("userName", "alice"), // ⚠️ 不符合,stderr 输出错误
)
}方案二:自定义配置
go
cfg := dd.DefaultConfig()
cfg.FieldValidation = &dd.FieldValidationConfig{
Mode: dd.FieldValidationWarn,
Convention: dd.NamingConventionCamelCase,
AllowCommonAbbreviations: true,
EnableSecurityValidation: true,
}方案三:运行时动态切换
go
// 启用严格 snake_case
logger.SetFieldValidation(dd.StrictSnakeCaseConfig())
// 关闭验证
logger.SetFieldValidation(nil)
// 查询当前配置
fv := logger.GetFieldValidation()常见缩写豁免
启用 AllowCommonAbbreviations: true(预设默认开启)后,以下缩写即使不完全匹配命名约定也予以放行:
| 缩写 | 说明 |
|---|---|
id、url、uri、ip | 基础标识 |
http、https、api | 协议与接口 |
json、xml、html、sql | 数据格式 |
tcp、udp、ssl、tls | 网络协议 |
jwt、oauth | 认证 |
*_id、*_url、*_api 等后缀 | 以缩写结尾的组合(如 user_id) |
安全验证
EnableSecurityValidation: true(预设默认开启)在命名约定之前执行以下安全检查:
| 检查项 | 拦截内容 | 说明 |
|---|---|---|
| Log4Shell 检测 | ${jndi:ldap://...} | 防止 JNDI 注入攻击通过日志键传播 |
| 同形异义字符 | Cyrillic а 替代 Latin a | 防止视觉欺骗攻击 |
| 过长 UTF-8 编码 | 非最短形式编码 | 防止绕过安全过滤 |
零值陷阱
直接使用 &dd.FieldValidationConfig{Mode: dd.FieldValidationStrict} 而不设置 EnableSecurityValidation 时,安全验证为 false(零值),会静默跳过安全检查。始终使用 DefaultFieldValidationConfig() 或预设函数(StrictSnakeCaseConfig() 等),它们将此字段设为 true。
多约定混合项目
如果项目同时有 Go 后端(snake_case)和 JavaScript 前端(camelCase)日志,可以分 Logger 使用不同约定:
go
// 后端 Logger:snake_case
backendCfg := dd.DefaultConfig()
backendCfg.FieldValidation = dd.StrictSnakeCaseConfig()
backendLogger, _ := dd.New(backendCfg)
// 前端日志聚合 Logger:camelCase
frontendCfg := dd.DefaultConfig()
frontendCfg.FieldValidation = dd.StrictCamelCaseConfig()
frontendLogger, _ := dd.New(frontendCfg)验证规则细节
每种命名约定的具体校验规则:
| 约定 | 规则 |
|---|---|
| snake_case | 小写字母 + 数字 + 下划线;不以 _ 开头/结尾;无连续 __ |
| camelCase | 字母 + 数字;首字符为小写字母 |
| PascalCase | 字母 + 数字;首字符为大写字母 |
| kebab-case | 小写字母 + 数字 + 连字符;不以 - 开头/结尾;无连续 -- |