package logger import ( "context" "log" "log/slog" "os" "strconv" "sync" slogotel "github.com/remychantenay/slog-otel" ) var ConfigPrefix = "" var rootLogger *slog.Logger var setup sync.Once func Setup() *slog.Logger { setup.Do(func() { var programLevel = new(slog.LevelVar) // Info by default envVar := "DEBUG" if len(ConfigPrefix) > 0 { envVar = ConfigPrefix + "_" + envVar } if opt := os.Getenv(envVar); len(opt) > 0 { if debug, _ := strconv.ParseBool(opt); debug { programLevel.Set(slog.LevelDebug) } } logOptions := &slog.HandlerOptions{Level: programLevel} if len(os.Getenv("INVOCATION_ID")) > 0 { // don't add timestamps when running under systemd log.Default().SetFlags(0) logReplace := func(groups []string, a slog.Attr) slog.Attr { // Remove time if a.Key == slog.TimeKey && len(groups) == 0 { return slog.Attr{} } return a } logOptions.ReplaceAttr = logReplace } logHandler := slogotel.OtelHandler{ Next: slog.NewTextHandler(os.Stderr, logOptions), } // https://github.com/cyrusaf/ctxlog/pull/1 // log := slog.New(ctxlog.NewHandler(logHandler)) log := slog.New(logHandler) slog.SetDefault(log) rootLogger = log }) return rootLogger } type loggerKey struct{} // NewContext adds the logger to the context. func NewContext(ctx context.Context, l *slog.Logger) context.Context { return context.WithValue(ctx, loggerKey{}, l) } // FromContext retrieves a logger from the context. If there is none, // it returns the default logger. func FromContext(ctx context.Context) *slog.Logger { if l, ok := ctx.Value(loggerKey{}).(*slog.Logger); ok { return l } return Setup() }