Files
lijiaoqiao/supply-api/internal/pkg/logging/logger.go

261 lines
6.1 KiB
Go
Raw Normal View History

package logging
import (
"encoding/json"
"fmt"
"os"
"time"
)
// ==================== P1-010 日志规范 ====================
// LogLevel 日志级别
type LogLevel string
const (
LogLevelDebug LogLevel = "DEBUG"
LogLevelInfo LogLevel = "INFO"
LogLevelWarn LogLevel = "WARN"
LogLevelError LogLevel = "ERROR"
LogLevelFatal LogLevel = "FATAL"
)
// LogEntry 标准日志条目JSON格式
type LogEntry struct {
Timestamp string `json:"timestamp"` // ISO8601格式
Level string `json:"level"` // DEBUG|INFO|WARN|ERROR|FATAL
Service string `json:"service"` // 服务名称
TraceID string `json:"trace_id,omitempty"` // 追踪ID
SpanID string `json:"span_id,omitempty"` // Span ID
RequestID string `json:"request_id,omitempty"` // 请求ID
Message string `json:"message"` // 日志消息
Fields map[string]interface{} `json:"fields,omitempty"` // 额外字段
}
// Logger 日志接口
type Logger interface {
Debug(msg string, fields ...map[string]interface{})
Info(msg string, fields ...map[string]interface{})
Warn(msg string, fields ...map[string]interface{})
Error(msg string, fields ...map[string]interface{})
Fatal(msg string, fields ...map[string]interface{})
}
// jsonLogger JSON格式日志实现
type jsonLogger struct {
service string
minLevel LogLevel
output *os.File
}
// NewLogger 创建日志实例
func NewLogger(service string, minLevel LogLevel) *jsonLogger {
return &jsonLogger{
service: service,
minLevel: minLevel,
output: os.Stdout,
}
}
// shouldLog 检查是否应该记录此级别
func (l *jsonLogger) shouldLog(level LogLevel) bool {
levels := map[LogLevel]int{
LogLevelDebug: 0,
LogLevelInfo: 1,
LogLevelWarn: 2,
LogLevelError: 3,
LogLevelFatal: 4,
}
return levels[level] >= levels[l.minLevel]
}
// formatEntry 格式化日志条目
func (l *jsonLogger) formatEntry(level LogLevel, msg string, fields map[string]interface{}) *LogEntry {
entry := &LogEntry{
Timestamp: time.Now().UTC().Format(time.RFC3339Nano),
Level: string(level),
Service: l.service,
Message: msg,
}
// 添加fields
if fields != nil {
entry.Fields = sanitizeFields(fields)
}
return entry
}
// log 输出日志
func (l *jsonLogger) log(level LogLevel, msg string, fields map[string]interface{}) {
if !l.shouldLog(level) {
return
}
entry := l.formatEntry(level, msg, fields)
// 序列化为JSON
data, err := json.Marshal(entry)
if err != nil {
return
}
// 添加换行符
l.output.Write(append(data, '\n'))
}
func (l *jsonLogger) Debug(msg string, fields ...map[string]interface{}) {
var f map[string]interface{}
if len(fields) > 0 {
f = fields[0]
}
l.log(LogLevelDebug, msg, f)
}
func (l *jsonLogger) Info(msg string, fields ...map[string]interface{}) {
var f map[string]interface{}
if len(fields) > 0 {
f = fields[0]
}
l.log(LogLevelInfo, msg, f)
}
func (l *jsonLogger) Warn(msg string, fields ...map[string]interface{}) {
var f map[string]interface{}
if len(fields) > 0 {
f = fields[0]
}
l.log(LogLevelWarn, msg, f)
}
func (l *jsonLogger) Error(msg string, fields ...map[string]interface{}) {
var f map[string]interface{}
if len(fields) > 0 {
f = fields[0]
}
l.log(LogLevelError, msg, f)
}
func (l *jsonLogger) Fatal(msg string, fields ...map[string]interface{}) {
var f map[string]interface{}
if len(fields) > 0 {
f = fields[0]
}
l.log(LogLevelFatal, msg, f)
}
// Infof 格式化信息日志
func (l *jsonLogger) Infof(format string, args ...interface{}) {
l.Info(fmt.Sprintf(format, args...), nil)
}
// Errorf 格式化错误日志
func (l *jsonLogger) Errorf(format string, args ...interface{}) {
l.Error(fmt.Sprintf(format, args...), nil)
}
// Warnf 格式化警告日志
func (l *jsonLogger) Warnf(format string, args ...interface{}) {
l.Warn(fmt.Sprintf(format, args...), nil)
}
// Debugf 格式化调试日志
func (l *jsonLogger) Debugf(format string, args ...interface{}) {
l.Debug(fmt.Sprintf(format, args...), nil)
}
// Fatalf 格式化致命日志
func (l *jsonLogger) Fatalf(format string, args ...interface{}) {
l.Fatal(fmt.Sprintf(format, args...), nil)
os.Exit(1)
}
// sanitizeFields 敏感字段脱敏
func sanitizeFields(fields map[string]interface{}) map[string]interface{} {
sanitized := make(map[string]interface{})
sensitiveKeys := []string{
"password", "secret", "token", "api_key", "apikey",
"credential", "authorization", "private_key",
"credit_card", "ssn", "passport",
}
for k, v := range fields {
lowerK := toLower(k)
for _, sensitive := range sensitiveKeys {
if contains(lowerK, sensitive) {
sanitized[k] = "[REDACTED]"
break
}
}
if _, ok := sanitized[k]; !ok {
// 检查嵌套map
if nestedMap, ok := v.(map[string]interface{}); ok {
sanitized[k] = sanitizeFields(nestedMap)
} else {
sanitized[k] = v
}
}
}
return sanitized
}
// String helpers
func toLower(s string) string {
result := make([]byte, len(s))
for i := 0; i < len(s); i++ {
c := s[i]
if c >= 'A' && c <= 'Z' {
c += 'a' - 'A'
}
result[i] = c
}
return string(result)
}
func contains(s, substr string) bool {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}
// LogFieldKeys 日志字段名常量(防止拼写错误)
const (
FieldKeyTenantID = "tenant_id"
FieldKeyUserID = "user_id"
FieldKeyRequestID = "request_id"
FieldKeyTraceID = "trace_id"
FieldKeySpanID = "span_id"
FieldKeyOperation = "operation"
FieldKeyDuration = "duration_ms"
FieldKeyStatusCode = "status_code"
FieldKeyError = "error"
FieldKeyErrorCode = "error_code"
FieldKeyClientIP = "client_ip"
FieldKeyUserAgent = "user_agent"
FieldKeyMethod = "method"
FieldKeyPath = "path"
FieldKeyQuery = "query"
FieldKeyRoute = "route"
)
// 标准字段常量
var SensitiveFields = []string{
"password",
"secret",
"token",
"api_key",
"apikey",
"credential",
"authorization",
"private_key",
"credit_card",
"ssn",
}