package logger import ( "context" "io" "log/slog" "os" "strings" "github.com/spf13/viper" "go.opentelemetry.io/contrib/bridges/otelslog" "go.opentelemetry.io/otel/trace" ) type multiHandler struct { handlers []slog.Handler } func (m *multiHandler) Enabled(ctx context.Context, l slog.Level) bool { for _, h := range m.handlers { if h.Enabled(ctx, l) { return true } } return false } func (m *multiHandler) Handle(ctx context.Context, r slog.Record) error { span := trace.SpanFromContext(ctx) if span.SpanContext().HasTraceID() { r.AddAttrs( slog.String("trace_id", span.SpanContext().TraceID().String()), slog.String("span_id", span.SpanContext().SpanID().String()), ) } for _, h := range m.handlers { _ = h.Handle(ctx, r) } return nil } func (m *multiHandler) WithAttrs(attrs []slog.Attr) slog.Handler { newHandlers := make([]slog.Handler, len(m.handlers)) for i, h := range m.handlers { newHandlers[i] = h.WithAttrs(attrs) } return &multiHandler{handlers: newHandlers} } func (m *multiHandler) WithGroup(name string) slog.Handler { newHandlers := make([]slog.Handler, len(m.handlers)) for i, h := range m.handlers { newHandlers[i] = h.WithGroup(name) } return &multiHandler{handlers: newHandlers} } func Init() { levelStr := strings.ToLower(viper.GetString("server.log_level")) var level slog.Level switch levelStr { case "debug": level = slog.LevelDebug case "warn": level = slog.LevelWarn case "error": level = slog.LevelError default: level = slog.LevelInfo } var writer io.Writer = os.Stdout if level == slog.LevelDebug { file, _ := os.OpenFile("app.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) writer = io.MultiWriter(os.Stdout, file) } localHandler := slog.NewJSONHandler(writer, &slog.HandlerOptions{ Level: level, AddSource: true, ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { if a.Key == slog.TimeKey { return slog.String(a.Key, a.Value.Time().Format("2006-01-02 15:04:05")) } return a }, }) otelHandler := otelslog.NewHandler(viper.GetString("server.service_name")) combinedHandler := &multiHandler{ handlers: []slog.Handler{localHandler, otelHandler}, } slog.SetDefault(slog.New(combinedHandler)) }