Files
lijiaoqiao/supply-api/internal/pkg/logging/slog_logger.go
Your Name 789707e4f3 feat(logging): 添加 slog-based 结构化日志支持
1. 添加 slog_logger.go 实现基于 Go 1.21+ slog 的结构化日志
2. 支持 trace_id、request_id、tenant_id 等标准字段注入
3. 添加日志标准化重构方案文档

推荐使用 Go 内置 log/slog,无需第三方依赖。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 07:41:37 +08:00

102 lines
2.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package logging
import (
"context"
"log/slog"
"os"
)
// SlogLogger 基于 Go 1.21+ slog 的结构化日志实现
type SlogLogger struct {
*slog.Logger
}
// NewSlogLogger 创建 slog-based 日志实例
func NewSlogLogger(service string, minLevel LogLevel) *SlogLogger {
opts := &slog.HandlerOptions{
Level: levelToSlog(minLevel),
}
handler := slog.NewJSONHandler(os.Stdout, opts)
logger := slog.New(handler)
logger = logger.With("service", service)
return &SlogLogger{Logger: logger}
}
// levelToSlog 转换日志级别
func levelToSlog(l LogLevel) slog.Level {
switch l {
case LogLevelDebug:
return slog.LevelDebug
case LogLevelInfo:
return slog.LevelInfo
case LogLevelWarn:
return slog.LevelWarn
case LogLevelError:
return slog.LevelError
case LogLevelFatal:
return slog.LevelError // slog 没有 fatal用 error 代替
default:
return slog.LevelInfo
}
}
// WithTraceID 返回带 trace_id 的日志实例
func (l *SlogLogger) WithTraceID(ctx context.Context) *SlogLogger {
traceID := GetTraceIDFromContext(ctx)
if traceID == "" {
return l
}
return &SlogLogger{Logger: l.Logger.With("trace_id", traceID)}
}
// WithRequestID 返回带 request_id 的日志实例
func (l *SlogLogger) WithRequestID(requestID string) *SlogLogger {
if requestID == "" {
return l
}
return &SlogLogger{Logger: l.Logger.With("request_id", requestID)}
}
// WithTenantID 返回带 tenant_id 的日志实例
func (l *SlogLogger) WithTenantID(tenantID int64) *SlogLogger {
return &SlogLogger{Logger: l.Logger.With("tenant_id", tenantID)}
}
// GetTraceIDFromContext 从 context 提取 trace_id
func GetTraceIDFromContext(ctx context.Context) string {
if ctx == nil {
return ""
}
// 尝试从 W3C traceparent 提取
if traceparent, ok := ctx.Value("traceparent").(string); ok && traceparent != "" {
// 格式: 00-{trace-id}-{span-id}-{trace-flags}
return extractTraceID(traceparent)
}
return ""
}
// extractTraceID 从 traceparent 提取 trace_id
func extractTraceID(traceparent string) string {
// 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
if len(traceparent) < 55 {
return ""
}
return traceparent[3:35] // 0af7651916cd43dd8448eb211c80319c
}
// Fields 便捷方法:使用 map 创建带字段的日志
func (l *SlogLogger) Fields(fields map[string]interface{}) *SlogLogger {
args := make([]any, 0, len(fields)*2)
for k, v := range fields {
args = append(args, k, v)
}
return &SlogLogger{Logger: l.Logger.With(args...)}
}
// DebugInfo 记录调试信息
func (l *SlogLogger) DebugInfo(msg string) {
l.Debug(msg)
}