Skip to content

log

The log package provides structured application logs, HTTP trace propagation, and request-wide event logging with tail sampling.

Core Components:

  • Logger, SetDefault, Debug/Info/Warn/Error: Package-level logging API built on top of slog.
  • New: Builds a text or JSON logger that automatically extracts values like traceId and serviceName from context.Context.
  • TraceIDMiddleware: Adds a per-request trace ID to context and response headers.
  • Event: Mutable wide-event model with attrs, steps, errors, severity, and duration.
  • WideEventLogger: Writes finalized Event values through a Sampler.
  • WideEventMiddleware: Creates request-wide events, stores them in context, and emits them after handlers finish.
  • Sampler, SamplerFunc, DefaultSampler: Tail-sampling rules for keeping errors, slow requests, selected status codes, and random samples.
  • EventFromContext: Fetches the current request-wide event from context using WideEventKey.

Full package docs at pkg.go.dev

  1. Configure default structured logging

    logger := log.New(os.Stdout, "json", slog.LevelInfo, nil)
    log.SetDefault(logger)

    This switches package-level logging to JSON and keeps context extraction enabled.

  2. Add trace IDs to each request

    server := httpserver.New("8080", 3*time.Second)
    server.Use(log.NewTraceIDMiddleware(nil, ""))

    With default arguments, the middleware stores IDs under log.TraceIDKey and writes Platforma-Trace-Id response headers.

  3. Configure wide-event logging with sampling

    wideLogger := log.NewWideEventLogger(
    os.Stdout,
    log.NewDefaultSampler(2*time.Second, 500, 0.05),
    "json",
    nil,
    )
    server.Use(log.NewWideEventMiddleware(wideLogger, "", nil))

    This keeps all error events, slow requests (>=2s), 5xx responses, and 5% of the remaining traffic.

  4. Enrich events and logs inside handlers

    server.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
    log.InfoContext(r.Context(), "users request started")
    if ev := log.EventFromContext(r.Context()); ev != nil {
    ev.AddStep(slog.LevelInfo, "query users table")
    ev.AddAttrs(map[string]any{"users.limit": 50})
    }
    w.WriteHeader(http.StatusOK)
    })

    InfoContext includes request metadata (for example traceId), and EventFromContext lets handlers attach detailed wide-event data.

  5. Verify runtime output

    {"level":"INFO","msg":"users request started","traceId":"14b3..."}
    {"level":"INFO","name":"http.request","duration":"12ms","request.status":200}

    The first line is an immediate log entry. The second line is the finalized request-wide event emitted after the response is completed.

Integrate logging by registering an HTTP service and attaching log middlewares before app.Run:

app := application.New()
server := httpserver.New("8080", 3*time.Second)
server.Use(log.NewTraceIDMiddleware(nil, ""))
wideLogger := log.NewWideEventLogger(
os.Stdout,
log.NewDefaultSampler(2*time.Second, 500, 0.05),
"json",
nil,
)
server.Use(log.NewWideEventMiddleware(wideLogger, "", nil))
server.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
log.InfoContext(r.Context(), "health check")
w.WriteHeader(http.StatusOK)
})
app.RegisterService("api", server)
app.Run(ctx)
wide-events.go
package main
import (
"context"
"errors"
"log/slog"
"os"
"time"
"github.com/platforma-dev/platforma/log"
)
func main() {
logger := log.NewWideEventLogger(
os.Stdout,
log.NewDefaultSampler(3*time.Second, 200, 0.1),
"json",
nil,
)
ev := log.NewEvent("test_event")
ev.AddStep(slog.LevelInfo, "some step")
ev.AddError(errors.New("some error"))
ev.AddAttrs(map[string]any{
"attr1": 1,
"attr2": true,
})
logger.WriteEvent(context.Background(), ev)
}