Skip to content

Commit

Permalink
Add structured logging support to promhttp
Browse files Browse the repository at this point in the history
In order to better support the standard library `log/slog` add a
new interface to the `promhttp` `HandlerOpts`.

Signed-off-by: SuperQ <superq@gmail.com>
  • Loading branch information
SuperQ committed Sep 3, 2024
1 parent 97aa049 commit c652332
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 9 deletions.
18 changes: 18 additions & 0 deletions prometheus/promhttp/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerO
if opts.ErrorLog != nil {
opts.ErrorLog.Println("error gathering metrics:", err)
}
if opts.StructuredErrorLog != nil {
opts.StructuredErrorLog.Error("error gathering metrics", "error", err)
}
errCnt.WithLabelValues("gathering").Inc()
switch opts.ErrorHandling {
case PanicOnError:
Expand Down Expand Up @@ -197,6 +200,9 @@ func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerO
if opts.ErrorLog != nil {
opts.ErrorLog.Println("error getting writer", err)
}
if opts.StructuredErrorLog != nil {
opts.StructuredErrorLog.Error("error getting writer", "error", err)
}
w = io.Writer(rsp)
encodingHeader = string(Identity)
}
Expand All @@ -218,6 +224,9 @@ func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerO
if opts.ErrorLog != nil {
opts.ErrorLog.Println("error encoding and sending metric family:", err)
}
if opts.StructuredErrorLog != nil {
opts.StructuredErrorLog.Error("error encoding and sending metric family", "error", err)
}
errCnt.WithLabelValues("encoding").Inc()
switch opts.ErrorHandling {
case PanicOnError:
Expand Down Expand Up @@ -344,6 +353,12 @@ type Logger interface {
Println(v ...interface{})
}

// StructuredLogger is a minimal interface HandlerOpts needs for structured
// logging. This is implementd by the standard library log/slog.Logger type.
type StructuredLogger interface {
Error(msg string, args ...any)
}

// HandlerOpts specifies options how to serve metrics via an http.Handler. The
// zero value of HandlerOpts is a reasonable default.
type HandlerOpts struct {
Expand All @@ -354,6 +369,9 @@ type HandlerOpts struct {
// latter, create a Logger implementation that detects a
// prometheus.MultiError and formats the contained errors into one line.
ErrorLog Logger
// StructuredErrorLog StructuredLogger specifies an optional structured log
// handler.
StructuredErrorLog StructuredLogger
// ErrorHandling defines how errors are handled. Note that errors are
// logged regardless of the configured ErrorHandling provided ErrorLog
// is not nil.
Expand Down
25 changes: 16 additions & 9 deletions prometheus/promhttp/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import (
"fmt"
"io"
"log"
"log/slog"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -130,25 +132,30 @@ func TestHandlerErrorHandling(t *testing.T) {
logBuf := &bytes.Buffer{}
logger := log.New(logBuf, "", 0)

slogger := slog.New(slog.NewTextHandler(os.Stderr, nil))

writer := httptest.NewRecorder()
request, _ := http.NewRequest("GET", "/", nil)
request.Header.Add("Accept", "test/plain")

mReg := &mockTransactionGatherer{g: reg}
errorHandler := HandlerForTransactional(mReg, HandlerOpts{
ErrorLog: logger,
ErrorHandling: HTTPErrorOnError,
Registry: reg,
ErrorLog: logger,
StructuredErrorLog: slogger,
ErrorHandling: HTTPErrorOnError,
Registry: reg,
})
continueHandler := HandlerForTransactional(mReg, HandlerOpts{
ErrorLog: logger,
ErrorHandling: ContinueOnError,
Registry: reg,
ErrorLog: logger,
StructuredErrorLog: slogger,
ErrorHandling: ContinueOnError,
Registry: reg,
})
panicHandler := HandlerForTransactional(mReg, HandlerOpts{
ErrorLog: logger,
ErrorHandling: PanicOnError,
Registry: reg,
ErrorLog: logger,
StructuredErrorLog: slogger,
ErrorHandling: PanicOnError,
Registry: reg,
})
// Expect gatherer not touched.
if got := mReg.gatherInvoked; got != 0 {
Expand Down

0 comments on commit c652332

Please sign in to comment.