Skip to content

Context-aware Logging

Lakta uses github.com/Vilsol/slox for context-aware logging. Instead of passing a *slog.Logger around explicitly, slox retrieves the logger from the context.

Middleware (gRPC interceptors, HTTP middleware) can inject structured fields — trace IDs, request IDs, user IDs — into the logger on the context. Any code that uses slox downstream automatically inherits those fields.

import (
"log/slog"
"github.com/Vilsol/slox"
)
func (m *MyModule) handleRequest(ctx context.Context, req *Request) {
slox.Info(ctx, "handling request",
slog.String("user_id", req.UserID),
slog.Int("item_count", len(req.Items)),
)
if err := m.process(ctx, req); err != nil {
slox.Error(ctx, "processing failed", slog.Any("err", err))
return
}
slox.Info(ctx, "request complete")
}
slox.Debug(ctx, "cache miss", slog.String("key", key))
slox.Info(ctx, "server started", slog.String("addr", addr))
slox.Warn(ctx, "retry attempt", slog.Int("attempt", n))
slox.Error(ctx, "fatal query", slog.Any("err", err))

Middleware can enrich the logger for all downstream calls:

func withRequestID(ctx context.Context, id string) context.Context {
logger := slox.FromContext(ctx).With(slog.String("request_id", id))
return slox.WithContext(ctx, logger)
}

If no logger has been injected into the context, slox falls back to slog.Default(), so code using slox is always safe to call even in tests without a full runtime.