Insights overview
Insights lets you observe what your Go application does in production.
The Honeybadger Go package ships handlers for the standard slog package
and for zerolog. Wire one in and every log line becomes a structured
event in Insights, fields and all. From there, you can attach per-request
fields to a derived logger, send custom events for moments that don’t fit
a log shape, and use BadgerQL to ask
questions across the whole event stream. Any field you send is queryable
as soon as it arrives, with no schema to define ahead of time.
Wire up structured logging
Section titled “Wire up structured logging”Construct an slog logger backed by the Honeybadger handler. The handler
accepts a custom event type, which is the BadgerQL event_type field you
will filter on later:
import ( "log/slog" "github.com/honeybadger-io/honeybadger-go" hbslog "github.com/honeybadger-io/honeybadger-go/slog")
hbClient := honeybadger.New(honeybadger.Configuration{APIKey: "..."})insightsLogger := slog.New( hbslog.New(hbClient).WithEventType("http_request"),).With("service", "checkouts", "commit", commit)service and commit ride on every event from this logger, so you can
filter by service across a fleet or split metrics by release.
Keep this logger separate from your application’s main slog logger.
Stdout and Insights serve different purposes, and routing every log line
through Honeybadger inflates your event volume with noise. Stash the
request-scoped derivative on context (below) so handlers emit through it
deliberately.
Add per-request context
Section titled “Add per-request context”slog’s .With() returns a new logger with extra attributes attached to
every subsequent log call. Use that to derive a request-scoped logger
inside HTTP middleware. Attach only attributes that make sense for every
request here, typically the request ID:
func InsightsMiddleware(insightsLogger *slog.Logger) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() wrapped := &responseWriter{ResponseWriter: w, status: 200}
requestLogger := insightsLogger.With( "request_id", r.Header.Get("X-Request-Id"), ) r = r.WithContext(context.WithValue(r.Context(), loggerKey, requestLogger))
next.ServeHTTP(wrapped, r)
requestLogger.LogAttrs(r.Context(), slog.LevelInfo, "request", slog.String("method", r.Method), slog.String("path", r.URL.Path), slog.Int("status", wrapped.status), slog.Int64("duration", time.Since(start).Microseconds()), ) }) }}Every request now produces one http_request event with method, path,
status, duration (microseconds), and request_id. Slowest endpoints
by p95:
filter event_type::str == "http_request"| stats percentile(95, duration::float) as p95_us by path::str| sort p95_us desc| limit 5| only path, toHumanString(p95_us, "microseconds") as p95| path | p95 |
|---|---|
| /checkouts/authorize | 412ms |
| /reports/generate | 287ms |
| /search | 138ms |
| /accounts/upgrade | 96ms |
| /users/me | 41ms |
Record application events
Section titled “Record application events”For application events recorded from inside a handler (a payment
authorized, a subscription upgrading, a feature toggle flipping), emit
them through a further-derived logger pulled from context. Handler-specific attributes
attached via .With() ride along with request_id and anything else the
middleware put on the request logger:
func authorizeCheckout(w http.ResponseWriter, r *http.Request) { logger := r.Context().Value(loggerKey).(*slog.Logger). With("checkout_variant", r.URL.Query().Get("variant")) // ... logger.LogAttrs(r.Context(), slog.LevelInfo, "payment authorized", slog.String("event_type", "payment.authorized"), slog.String("payment_provider", payment.Provider), slog.Float64("amount", checkout.Total), slog.String("currency", checkout.Currency), slog.String("authorization_id", payment.AuthorizationID), )}This query breaks down the amounts collected by variant and provider:
filter event_type::str == "payment.authorized"| stats count() as authorizations, sum(amount::float) as authorized_amount by checkout_variant::str, payment_provider::str| sort authorized_amount desc| authorizations | authorized_amount | checkout_variant | payment_provider |
|---|---|---|---|
| 413 | 34108.00 | new | stripe |
| 218 | 18722.00 | new | paypal |
| 418 | 32167.00 | control | stripe |
| 220 | 13639.00 | control | paypal |