// Package logger 实现轻量级结构化日志系统. // // 输出格式:2024-01-15T10:30:00Z [INFO] message key1=val1 key2=val2 // 日志级别:DEBUG, INFO, WARN, ERROR // 支持上下文字段(请求 ID,会话 ID,工具名等) // 默认输出到 stderr,不引入外部依赖. package logger import ( "fmt" "io" "os" "regexp" "strings" "sync" "time" ) // Level 是日志级别. type Level int const ( LevelDebug Level = iota LevelInfo LevelWarn LevelError ) // String 返回日志级别的字符串表示. func (l Level) String() string { switch l { case LevelDebug: return "DEBUG" case LevelInfo: return "INFO" case LevelWarn: return "WARN" case LevelError: return "ERROR" default: return "UNKNOWN" } } // ParseLevel 从字符串解析日志级别. func ParseLevel(s string) Level { switch strings.ToUpper(strings.TrimSpace(s)) { case "DEBUG": return LevelDebug case "INFO": return LevelInfo case "WARN", "WARNING": return LevelWarn case "ERROR": return LevelError default: return LevelInfo } } // Field 是一个结构化日志字段. type Field struct { Key string Value string // sensitive 表示该字段的值是否需要脱敏 sensitive bool } // String 创建字符串字段. func String(key, value string) Field { return Field{Key: key, Value: value} } // Int 创建整数字段. func Int(key string, value int) Field { return Field{Key: key, Value: fmt.Sprintf("%d", value)} } // Int64 创建 int64 字段. func Int64(key string, value int64) Field { return Field{Key: key, Value: fmt.Sprintf("%d", value)} } // Float 创建浮点数字段. func Float(key string, value float64) Field { return Field{Key: key, Value: fmt.Sprintf("%.4f", value)} } // Duration 创建时间段字段. func Duration(key string, value time.Duration) Field { return Field{Key: key, Value: value.String()} } // Err 创建错误字段. func Err(key string, err error) Field { if err == nil { return Field{Key: key, Value: ""} } return Field{Key: key, Value: err.Error()} } // Sensitive 创建一个敏感字段,写入时自动脱敏. // // 升华改进(ELEVATED): 早期方案 logger 无凭据脱敏--API key,token,secret 明文写入日志. // 攻击场景:日志聚合系统(Splunk/ELK)存储明文凭据,泄露风险极高. // Sensitive 字段自动脱敏,保留键名便于排查,只隐藏值. // 替代方案:<在消费层统一过滤 key=value> - 否决:日志可能在写入前就已泄露到日志系统. func Sensitive(key, value string) Field { return Field{Key: key, Value: value, sensitive: true} } // ── 凭据脱敏 ────────────────────────────────────────────────────────────────── var ( // keyValueRE 匹配 KEY=VALUE 格式(如 ANTHROPIC_API_KEY=sk-ant-...) // 脱敏后:KEY=[REDACTED] keyValueRE = regexp.MustCompile(`(?i)((?:api[_-]?key|token|secret|password|passwd|authorization|bearer|auth)[_\w]*)=([^\s]+)`) // jsonSecretRE 匹配 JSON 字段中的敏感值 // 脱敏字段:api_key, token, password, secret, authorization jsonSecretFieldRE = regexp.MustCompile(`(?i)"(api[_-]?key|token|password|secret|authorization)"\s*:\s*"(?:[^"\\]|\\.)*"`) // headerAuthRE 匹配 HTTP Header 中的 Authorization: Bearer xxx headerAuthRE = regexp.MustCompile(`(?i)(Authorization:\s*Bearer\s+)[^\s]+`) ) // sanitizeSecrets 对日志消息进行凭据脱敏. // // 覆盖格式: // - KEY=VALUE 格式(api_key, token, secret, password, authorization 等) // - JSON 字段("api_key", "token", "password", "secret", "authorization") // - HTTP Header(Authorization: Bearer ...) // // 保留键名,只将值替换为 [REDACTED]. func sanitizeSecrets(s string) string { // 替换 key=value 格式 s = keyValueRE.ReplaceAllStringFunc(s, func(match string) string { idx := strings.Index(match, "=") if idx < 0 { return match } return match[:idx+1] + "[REDACTED]" }) // 替换 JSON 字段中的敏感值 s = jsonSecretFieldRE.ReplaceAllStringFunc(s, func(match string) string { colon := strings.Index(match, ":") if colon < 0 { return match } return match[:colon+1] + ` "[REDACTED]"` }) // 替换 Header Authorization s = headerAuthRE.ReplaceAllString(s, "${1}[REDACTED]") return s } // Bool 创建布尔字段. func Bool(key string, value bool) Field { if value { return Field{Key: key, Value: "true"} } return Field{Key: key, Value: "false"} } // Logger 是结构化日志接口. type Logger interface { // Debug 输出 DEBUG 级别日志. Debug(msg string, fields ...Field) // Info 输出 INFO 级别日志. Info(msg string, fields ...Field) // Warn 输出 WARN 级别日志. Warn(msg string, fields ...Field) // Error 输出 ERROR 级别日志. Error(msg string, fields ...Field) // With 创建带默认字段的子 logger. // 子 logger 每次输出时会自动包含这些字段. With(fields ...Field) Logger // Level 返回当前日志级别. GetLevel() Level } // defaultLogger 是默认的 Logger 实现. type defaultLogger struct { mu sync.Mutex output io.Writer level Level fields []Field // 默认字段(With 创建的子 logger 会携带) } // 全局默认 logger var ( globalMu sync.RWMutex globalLogger Logger = newDefaultLogger(os.Stderr, LevelInfo) ) // Default 返回全局默认 logger. func Default() Logger { globalMu.RLock() defer globalMu.RUnlock() return globalLogger } // SetDefault 替换全局默认 logger. func SetDefault(l Logger) { globalMu.Lock() defer globalMu.Unlock() globalLogger = l } // SetLevel 设置全局默认 logger 的日志级别. // 仅当全局 logger 是默认实现时有效. func SetLevel(level Level) { globalMu.Lock() defer globalMu.Unlock() if dl, ok := globalLogger.(*defaultLogger); ok { dl.level = level } } // New 创建一个新的 Logger,输出到指定的 writer. func New(output io.Writer, level Level) Logger { return newDefaultLogger(output, level) } // newDefaultLogger 创建默认 logger 实现. func newDefaultLogger(output io.Writer, level Level) *defaultLogger { return &defaultLogger{ output: output, level: level, } } func (l *defaultLogger) Debug(msg string, fields ...Field) { l.log(LevelDebug, msg, fields) } func (l *defaultLogger) Info(msg string, fields ...Field) { l.log(LevelInfo, msg, fields) } func (l *defaultLogger) Warn(msg string, fields ...Field) { l.log(LevelWarn, msg, fields) } func (l *defaultLogger) Error(msg string, fields ...Field) { l.log(LevelError, msg, fields) } func (l *defaultLogger) With(fields ...Field) Logger { newFields := make([]Field, len(l.fields)+len(fields)) copy(newFields, l.fields) copy(newFields[len(l.fields):], fields) return &defaultLogger{ output: l.output, level: l.level, fields: newFields, } } func (l *defaultLogger) GetLevel() Level { return l.level } // log 是实际的日志输出方法. // 输出格式:2024-01-15T10:30:00Z [INFO] message key1=val1 key2=val2 func (l *defaultLogger) log(level Level, msg string, fields []Field) { if level < l.level { return } // 消息和字段值脱敏 msg = sanitizeSecrets(msg) var sb strings.Builder // 时间戳 sb.WriteString(time.Now().UTC().Format(time.RFC3339)) // 级别 sb.WriteString(" [") sb.WriteString(level.String()) sb.WriteString("] ") // 消息 sb.WriteString(msg) // 默认字段(With 注入的) for _, f := range l.fields { sb.WriteByte(' ') writeField(&sb, f) } // 本次调用的字段 for _, f := range fields { sb.WriteByte(' ') writeField(&sb, f) } sb.WriteByte('\n') l.mu.Lock() defer l.mu.Unlock() io.WriteString(l.output, sb.String()) } // writeField 将 Field 写入 builder. // 如果值包含空格或特殊字符,用引号包裹. // 敏感字段的值会被替换为 [REDACTED]. func writeField(sb *strings.Builder, f Field) { sb.WriteString(f.Key) sb.WriteByte('=') value := f.Value if f.sensitive { value = "[REDACTED]" } needsQuote := strings.ContainsAny(value, " \t\n\"=") if needsQuote { sb.WriteByte('"') // 转义内部引号 for _, r := range value { if r == '"' { sb.WriteString("\\\"") } else if r == '\n' { sb.WriteString("\\n") } else { sb.WriteRune(r) } } sb.WriteByte('"') } else { sb.WriteString(value) } } // --- 全局便捷函数 --- // Debug 通过全局 logger 输出 DEBUG 日志. func Debug(msg string, fields ...Field) { Default().Debug(msg, fields...) } // Info 通过全局 logger 输出 INFO 日志. func Info(msg string, fields ...Field) { Default().Info(msg, fields...) } // Warn 通过全局 logger 输出 WARN 日志. func Warn(msg string, fields ...Field) { Default().Warn(msg, fields...) } // Error 通过全局 logger 输出 ERROR 日志. func Error(msg string, fields ...Field) { Default().Error(msg, fields...) }