package logging import ( "context" "io" "log/slog" "strings" ) type CtxKey int const ( _ CtxKey = iota CtxKeyLogger CtxKeyTraceID ) func parseLogLevel(levelStr string) slog.Level { switch strings.ToUpper(levelStr) { case "DEBUG": return slog.LevelDebug case "INFO": return slog.LevelInfo case "WARN": return slog.LevelWarn case "ERROR": return slog.LevelError default: return slog.LevelInfo // Default level } } // InitLogggin initializes global structured logging for the entire application func InitLogging(level string, w io.Writer, addSource bool) *slog.Logger { // Use os.Stderr // // Stderr is used for diagnostics and logging. Stdout is used for program // output. Stderr also have greater likely hood of being seen if a programs // output is being redirected. parsedLogLevel := parseLogLevel(level) loggerHandler := slog.NewJSONHandler(w, &slog.HandlerOptions{Level: parsedLogLevel, AddSource: addSource}) logger := slog.New(loggerHandler) slog.SetDefault(logger) return logger } func LoggerFromCtx(ctx context.Context) *slog.Logger { // context doesn't enforce type when context.WithValue(ctx, ctxKeyLogger, logger) is used // it is stored as empty interface. So use type assertion .(*slog.Logger) to retrieve the // value if l, ok := ctx.Value(CtxKeyLogger).(*slog.Logger); ok && l != nil { return l } return slog.Default() }