package logging import ( "context" "log/slog" "os" "go.opentelemetry.io/contrib/bridges/otelslog" ) type Logger interface { Info(msg string, args ...any) Warn(msg string, args ...any) Error(msg string, args ...any) } var defaultLogger *slog.Logger type contextKey string const loggerKey = contextKey("logger") func SetupLogger(logLevel, logFormat, serviceName, buildVersion string) *slog.Logger { var leveler slog.LevelVar err := leveler.UnmarshalText([]byte(logLevel)) handlerOpts := &slog.HandlerOptions{ AddSource: false, Level: leveler.Level(), ReplaceAttr: nil, } var handler slog.Handler switch logFormat { case "json": handler = slog.NewJSONHandler(os.Stdout, handlerOpts) case "text": handler = slog.NewTextHandler(os.Stdout, handlerOpts) case "otel": handler = otelslog.NewHandler(serviceName, otelslog.WithVersion(buildVersion)) } defaultLogger = slog.New(handler).With("service", serviceName).With("version", buildVersion) if err != nil { defaultLogger.With("err", err).Error("Failed to parse log level") os.Exit(1) } slog.SetDefault(defaultLogger) return defaultLogger } // ContextWithLogger returns a new Context with the logger attached func ContextWithLogger(ctx context.Context, logger *slog.Logger) context.Context { return context.WithValue(ctx, loggerKey, logger) } // LoggerFromContext returns a logger from the passed context or the default logger func LoggerFromContext(ctx context.Context) *slog.Logger { logger := ctx.Value(loggerKey) if l, ok := logger.(*slog.Logger); ok { return l } return defaultLogger }