diff --git a/cmd/cachewd/main.go b/cmd/cachewd/main.go index 47d2ce4..3a71fad 100644 --- a/cmd/cachewd/main.go +++ b/cmd/cachewd/main.go @@ -150,6 +150,21 @@ func newMux(ctx context.Context, cr *cache.Registry, sr *strategy.Registry, prov _, _ = w.Write([]byte("OK")) //nolint:errcheck }) + mux.HandleFunc("GET /admin/log/level", func(w http.ResponseWriter, _ *http.Request) { + _, _ = fmt.Fprintln(w, logging.GetLevel().String()) + }) + + mux.HandleFunc("PUT /admin/log/level", func(w http.ResponseWriter, r *http.Request) { + var level slog.Level + if err := level.UnmarshalText([]byte(strings.TrimSpace(r.FormValue("level")))); err != nil { + http.Error(w, fmt.Sprintf("invalid level: %s", err), http.StatusBadRequest) + return + } + logging.SetLevel(level) + logging.FromContext(r.Context()).Info("Log level changed", "level", level) + _, _ = fmt.Fprintln(w, logging.GetLevel().String()) + }) + if err := config.Load(ctx, cr, sr, providersConfigHCL, mux, vars); err != nil { return nil, errors.Errorf("load config: %w", err) } diff --git a/internal/logging/logging.go b/internal/logging/logging.go index 45a9bbd..20a1796 100644 --- a/internal/logging/logging.go +++ b/internal/logging/logging.go @@ -15,12 +15,23 @@ type Config struct { Remap map[string]string `hcl:"remap,optional" help:"Remap field names from old to new (e.g., msg=message, time=timestamp)."` } +var levelVar = &slog.LevelVar{} //nolint:gochecknoglobals + type logKey struct{} +// SetLevel sets the global log level at runtime. +func SetLevel(level slog.Level) { levelVar.Set(level) } + +// GetLevel returns the current global log level. +func GetLevel() slog.Level { return levelVar.Level() } + +// Configure sets up logging with the given config and returns the logger and updated context. func Configure(ctx context.Context, config Config) (*slog.Logger, context.Context) { + levelVar.Set(config.Level) + var handler slog.Handler if config.JSON { - options := &slog.HandlerOptions{Level: config.Level} + options := &slog.HandlerOptions{Level: levelVar} if len(config.Remap) > 0 { options.ReplaceAttr = func(groups []string, a slog.Attr) slog.Attr { if len(groups) > 0 { @@ -35,7 +46,7 @@ func Configure(ctx context.Context, config Config) (*slog.Logger, context.Contex handler = &messageHandler{inner: slog.NewJSONHandler(os.Stdout, options)} } else { handler = tint.NewHandler(os.Stderr, &tint.Options{ - Level: config.Level, + Level: levelVar, ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { if a.Key == slog.TimeKey && len(groups) == 0 { return slog.Attr{}