package logging import ( "context" "fmt" "log/slog" "os" "time" ) // loggerKey is the context key for logger values type loggerKey struct{} // AppLogger is the application-wide logger instance var AppLogger *slog.Logger // SetupLogger initializes the structured logger based on environment settings func SetupLogger(env string) *slog.Logger { // Determine log level logLevel := slog.LevelInfo if levelStr := os.Getenv("LOG_LEVEL"); levelStr != "" { if err := logLevel.UnmarshalText([]byte(levelStr)); err != nil { panic(fmt.Sprintf("invalid log level: %s", levelStr)) } } var handler slog.Handler // Configure handler based on environment if env == "development" { // In development, use text output with source information if in debug mode opts := &slog.HandlerOptions{ Level: logLevel, AddSource: logLevel == slog.LevelDebug, ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { // Format time for better readability in dev mode if a.Key == slog.TimeKey { if t, ok := a.Value.Any().(time.Time); ok { return slog.String(slog.TimeKey, t.Format(time.RFC3339)) } } return a }, } handler = slog.NewTextHandler(os.Stdout, opts) } else { // In production, use JSON output handler = slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ Level: logLevel, }) } // Create and set the default logger logger := slog.New(handler) slog.SetDefault(logger) AppLogger = logger logger.Info("logger initialized", slog.String("environment", env), slog.String("level", logLevel.String())) return logger } // FromContext retrieves a logger from the given context // If no logger exists in the context, returns the default logger func FromContext(ctx context.Context) *slog.Logger { if ctx == nil { return AppLogger } if logger, ok := ctx.Value(loggerKey{}).(*slog.Logger); ok { return logger } return AppLogger } // WithContext stores a logger in the given context func WithContext(ctx context.Context, logger *slog.Logger) context.Context { return context.WithValue(ctx, loggerKey{}, logger) } // WithValues returns a new logger with additional context values func WithValues(logger *slog.Logger, attrs ...any) *slog.Logger { return logger.With(attrs...) }