clog

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 10, 2026 License: MIT Imports: 5 Imported by: 0

README

clog

Contextual logging for Go, compatible with the standard log/slog package.

Installation

go get github.com/muonsoft/clog

Requires Go 1.21+ (for log/slog). Optional: github.com/muonsoft/errors for structured error logging.

Features

  • Context handler — inject attributes from context.Context into every log record (e.g. trace_id, user_id).
  • Logger in context — attach a request-scoped logger to the context once (e.g. in middleware), then call clog.Info(ctx, "message") without passing attributes every time.
  • HTTP middleware — subpackage clog/http provides middleware that adds request_id, http.method, http.path (and optionally remote_addr) to the context logger, with time-sortable request IDs and optional request start/finish logs.
  • Structured error logging — integration with muonsoft/errors for logging errors with attributes and stack traces at configurable levels.

Quick start

package main

import (
	"context"
	"os"

	"github.com/muonsoft/clog"
	"log/slog"
)

func main() {
	// Wrap your handler to add context attributes to every record
	h := clog.NewContextHandler(
		slog.NewJSONHandler(os.Stdout, nil),
		[]clog.ContextKey{"trace_id", "user_id"},
	)
	slog.SetDefault(slog.New(h))

	ctx := context.Background()
	ctx = context.WithValue(ctx, clog.ContextKey("trace_id"), "abc-123")
	ctx = clog.NewContext(ctx, clog.FromContext(ctx).With("request_id", "req-1"))

	clog.Info(ctx, "request started")
	clog.Info(ctx, "work done", "items", 42)
}

Context API

Function Description
FromContext(ctx) Returns the logger stored in the context, or slog.Default() if none.
NewContext(ctx, logger) Returns a copy of ctx that stores the given logger.
With(ctx, args...) Returns a new context whose logger has the given attributes (e.g. request_id, path).
WithGroup(ctx, name) Returns a new context whose logger starts a group with the given name.

Logging

Use the same context in handlers and services so logs carry the same attributes:

  • clog.Debug(ctx, msg, args...)
  • clog.Info(ctx, msg, args...)
  • clog.Warn(ctx, msg, args...)
  • clog.Error(ctx, msg, args...)
  • clog.Log(ctx, level, msg, args...)
  • clog.LogAttrs(ctx, level, msg, attrs...)

All use the logger from FromContext(ctx) and record the caller’s source location correctly.

Error logging (muonsoft/errors)

When using github.com/muonsoft/errors:

  • clog.Errorf(ctx, msg, args...) — builds an error with errors.Errorf(msg, args...) and logs it at Error level with attributes and stack trace.
  • clog.ErrorLevel(ctx, err, level) — logs an existing error at the given slog.Level. Does nothing if err is nil.
err := errors.Wrap(dbErr, slog.String("query", sql), slog.Int("id", id))
clog.ErrorLevel(ctx, err, slog.LevelWarn)

HTTP middleware

Import the HTTP subpackage and wrap your handler:

import (
	"net/http"
	cloghttp "github.com/muonsoft/clog/http"
)

mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
	clog.Info(r.Context(), "handler")
	w.WriteHeader(http.StatusOK)
})

handler := cloghttp.Middleware(mux, &cloghttp.MiddlewareOptions{
	AddRemoteAddr: true,
	LogStart:      true,
	LogFinish:     true,
})
http.ListenAndServe(":8080", handler)

The middleware:

  • Injects a request-scoped logger with request_id, http.method, http.path (and optionally remote_addr).
  • Uses a time-sortable request ID (similar to UUID v7): 6 bytes timestamp + 2 bytes random, 16 hex chars. Reads or echoes X-Request-Id when provided.
  • Optionally logs "request started" and "request completed" (with duration_ms and http.status).
  • Wraps http.ResponseWriter so SSE (Flush), WebSockets (Hijack), and sendfile (ReadFrom) keep working.

License

MIT. See LICENSE.

Documentation

Overview

Package clog provides contextual logging compatible with log/slog.

It combines two mechanisms:

  1. Context Handler — wrap any slog.Handler with NewContextHandler(inner, keys). For each log record, values for the given context keys are read from context.Context and added as attributes. Use context.WithValue to set values (e.g. trace_id, user_id) and pass the same context to logging calls.

  2. Logger in context — store a *slog.Logger in the context with NewContext, retrieve it with FromContext. In middleware, create a request-scoped logger (e.g. With("request_id", id)) and attach it to the context so handlers can call Info(ctx, "message") without passing attributes every time.

Example setup:

h := clog.NewContextHandler(slog.NewJSONHandler(os.Stdout, nil),
    []clog.ContextKey{"trace_id", "user_id"})
slog.SetDefault(slog.New(h))

Example in HTTP middleware:

ctx = clog.NewContext(ctx, clog.FromContext(ctx).With("request_id", id, "path", r.URL.Path))

Example in a handler:

clog.Info(ctx, "request started")
clog.Error(ctx, "operation failed", "error", err)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Debug

func Debug(ctx context.Context, msg string, args ...any)

Debug logs at LevelDebug using the logger from ctx (or the default).

func Error

func Error(ctx context.Context, msg string, args ...any)

Error logs at LevelError using the logger from ctx (or the default).

func ErrorLevel

func ErrorLevel(ctx context.Context, err error, level slog.Level)

ErrorLevel logs err at the given slog level using the logger from ctx (or the default). The error is logged with all structured attributes and stack trace from the muonsoft/errors chain. If err is nil, nothing is logged.

func Errorf

func Errorf(ctx context.Context, msg string, args ...any)

Errorf creates an error with errors.Errorf(msg, args...) and logs it at Error level using the logger from ctx (or the default). The error is logged with all structured attributes and stack trace from the muonsoft/errors chain.

func FromContext

func FromContext(ctx context.Context) *slog.Logger

FromContext returns the Logger stored in ctx, or slog.Default() if none. Use this to obtain the request-scoped logger when one was set via NewContext.

func Info

func Info(ctx context.Context, msg string, args ...any)

Info logs at LevelInfo using the logger from ctx (or the default).

func Log

func Log(ctx context.Context, level slog.Level, msg string, args ...any)

Log logs at the given level using the logger from ctx (or the default).

func LogAttrs

func LogAttrs(ctx context.Context, level slog.Level, msg string, attrs ...slog.Attr)

LogAttrs logs at the given level with the given attrs using the logger from ctx (or the default). It is more efficient than Log when all arguments are already Attrs.

func NewContext

func NewContext(ctx context.Context, logger *slog.Logger) context.Context

NewContext returns a copy of ctx that stores the given Logger. Retrieve it later with FromContext. Use this in middleware to attach a request-scoped logger (e.g. with request_id, path) to the context.

func Warn

func Warn(ctx context.Context, msg string, args ...any)

Warn logs at LevelWarn using the logger from ctx (or the default).

func With

func With(ctx context.Context, args ...any) context.Context

With returns a new context that stores a Logger with the given attributes attached. The Logger is obtained from ctx (or the default), then With(args...) is called on it, and the result is stored in a child context.

func WithGroup

func WithGroup(ctx context.Context, name string) context.Context

WithGroup returns a new context that stores a Logger with the given group name. The Logger is obtained from ctx (or the default), then WithGroup(name) is called on it, and the result is stored in a child context.

Types

type ContextHandler

type ContextHandler struct {
	// contains filtered or unexported fields
}

ContextHandler wraps a slog.Handler and adds attributes from context.Context for each key in keys. Values are retrieved via ctx.Value(key) and added to the record before passing to the inner handler.

func NewContextHandler

func NewContextHandler(inner slog.Handler, keys []ContextKey) *ContextHandler

NewContextHandler returns a Handler that adds context attributes for the given keys to every record, then delegates to inner.

func (*ContextHandler) Enabled

func (h *ContextHandler) Enabled(ctx context.Context, level slog.Level) bool

Enabled reports whether the handler handles records at the given level.

func (*ContextHandler) Handle

func (h *ContextHandler) Handle(ctx context.Context, r slog.Record) error

Handle adds attributes from ctx for each configured key, then passes the record to the inner handler.

func (*ContextHandler) WithAttrs

func (h *ContextHandler) WithAttrs(attrs []slog.Attr) slog.Handler

WithAttrs returns a new ContextHandler with the same context keys and the inner handler wrapped with the given attrs.

func (*ContextHandler) WithGroup

func (h *ContextHandler) WithGroup(name string) slog.Handler

WithGroup returns a new ContextHandler with the same context keys and the inner handler wrapped with the given group name.

type ContextKey

type ContextKey string

ContextKey is the type for context keys that ContextHandler reads to add attributes to each log record. Use it with context.WithValue.

Directories

Path Synopsis
Package http provides HTTP middleware for clog.
Package http provides HTTP middleware for clog.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL