From 9bbf4c90a4d55b075646b3bbef3b6d92f93ac49b Mon Sep 17 00:00:00 2001 From: TJ Hoplock Date: Mon, 7 Jul 2025 23:39:43 -0400 Subject: [PATCH] fix: make `debug` param log output work regardless of log level Fixes issue reported here: https://github.com/prometheus/blackbox_exporter/pull/1381#issuecomment-3044749631 In aligning behavior for how the `--log.prober` flag is currently expected to work, I didn't test it's effect on logs when used with the `debug` URL parameter, as is often done for testing. This changes the handler to always write to the internal buffer logger (which is what is used to store the logs for displaying with the `debug` url param), and it only writes through to the real logger if it's enabled for the level the prober is set to. This ensures that logs are always available with the `debug` param, while still correctly filtering levels for console output at stderr. As a consequence of thinking about how to make this work, I also realized it wasn't necessary to manually iterate record attrs or maintain our own attr list when hijacking the log call -- the level can be overridden directly from the record, we just need to grab a copy via Clone() first. So should be more efficient, as well. Signed-off-by: TJ Hoplock --- prober/handler.go | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/prober/handler.go b/prober/handler.go index 6a51967b..00e85d77 100644 --- a/prober/handler.go +++ b/prober/handler.go @@ -16,6 +16,7 @@ package prober import ( "bytes" "context" + "errors" "fmt" "log/slog" "net/http" @@ -181,20 +182,24 @@ func (sl *scrapeLogger) Enabled(ctx context.Context, level slog.Level) bool { // slog.Handler. func (sl *scrapeLogger) Handle(ctx context.Context, r slog.Record) error { level := getSlogLevel(sl.logLevelProber.String()) + var errs []error + // Clone record so we can override the level. We hijack log calls to + // the scrapeLogger and override the level from the original log call + // with the level set via the `--log.prober` flag. + rec := r.Clone() + rec.Level = level + + // Scrape logger should only write to next, the "real" logger, if next + // is enabled to write at the level the `--log.prober` flag is set to. + if sl.next.Enabled(context.Background(), level) { + errs = append(errs, sl.next.Handler().Handle(ctx, rec)) + } - // Collect attributes from record so we can log them directly. We - // hijack log calls to the scrapeLogger and override the level from the - // original log call with the level set via the `--log.prober` flag. - attrs := make([]slog.Attr, r.NumAttrs()) - r.Attrs(func(a slog.Attr) bool { - attrs = append(attrs, a) - return true - }) - - sl.next.LogAttrs(ctx, level, r.Message, attrs...) - sl.bufferLogger.LogAttrs(ctx, level, r.Message, attrs...) + // Always log to the bufferLogger, this is used to retain scrape log + // output for the `debug` URL param. + errs = append(errs, sl.bufferLogger.Handler().Handle(ctx, rec)) - return nil + return errors.Join(errs...) } // WithAttrs adds the provided attributes to the scrapeLogger's internal logger and