Skip to content

cl-log-kit

cl-log-kit is an SBCL-only structured logging toolkit for Common Lisp, inspired by Go's log/slog. It separates immutable log records from handlers that serialize them: every built-in handler emits a record through exactly one handle-log-record method, so there is no way to end up with duplicate output paths. It is built on the nerima-lisp toolkit family — cl-date-kit, cl-concurrent-kit, and cl-host-kit — used directly, with no adapter layer in between.

New to cl-log-kit?

Load it, build a logger, and emit your first structured log line:

(asdf:load-system "cl-log-kit")

Continue with Getting Started → Levels and Fields.

Explore the docs

  •   Getting Started


    Load the system with ASDF or Nix, and build your first logger and log line.

    Getting Started

  •   Core Concepts


    Severity levels, structured fields, and how loggers derive scoped children with logger-with, derive-logger, and with-log-context.

    Levels · Fields · Logger Derivation and Context

  •   Handlers


    Text and JSON output, composition with multi-handler and filter-handler, file rotation and trigger-based buffering, and the handler lifecycle protocol.

    Handlers

  •   Conditions and Spans


    Turn Lisp conditions into structured fields, and time an operation with paired start/end log records.

    Logging Conditions · Log Spans

Project checks

  • Small runtime dependency set. The shipped cl-log-kit system depends on cl-date-kit, cl-concurrent-kit, and cl-host-kit; cl-log-kit/test additionally depends on cl-weave and cl-json-kit.
  • Semantic Versioning. Behavior changes are recorded in the release notes under the version it shipped in, and anything that changes the shape or behavior of an exported symbol gets a major version and its own migration path. See Compatibility.
  • Automated checks. nix flake check runs the SBCL suite, formatting, and this site's --strict build. A separate coverage gate checks expression and branch coverage, currently 95.03% / 98.51%. See Development.
  • Concurrency. Every handler that owns a stream serializes writes and closes through a single reentrant lock, and every composite handler's lifecycle guarantees close-at-most-once under concurrent and recursive callers. These are exercised by dedicated multi-thread specs, not single-threaded examples. See Handlers.
  • Input limits. Field depth, node count, string length, and collection size are capped, with structured conditions for each limit. See Fields.
  • MIT-licensed, with a public bug tracker and no undocumented private API — every exported symbol has a docstring.

Status

Version 2.2.0. cl-log-kit is a small, stable surface — see the API Reference for the full list of exported symbols. The capability list below is the intended public surface, validated by the test suite documented in Development:

  • make-logger / logger-with / derive-logger / logger-child for building and deriving structured loggers
  • explicit-logger logging macros (log-debug, log-info, log-warn, log-error, log-fatal), which always take the logger as their first argument, and their log-default-* counterparts against *default-logger*
  • with-default-logger for a dynamically scoped default logger
  • with-log-context for dynamically scoped fields shared across a call stack, plus capture-log-context / with-captured-log-context for carrying that context into a new thread
  • five built-in severity levels (DEBUG < INFO < WARN < ERROR < FATAL) plus arbitrary integer custom levels
  • level-gated macros that skip message/field evaluation, clock access, and handler work entirely when a call is filtered
  • property-list fields with case-insensitive canonical keys and strict duplicate-key rejection
  • recursive, cycle-safe snapshotting of strings, conses, arrays, hash tables, and explicit JSON containers, with fixed resource limits
  • text-handler and json-handler built-in output handlers, both injection-safe against control characters, DEL, and bidi/invisible spoofing controls
  • an explicit JSON value model (json-object, json-array, +json-null+, +json-false+) so field encoding is never guessed from Lisp types
  • multi-handler, filter-handler, function-handler, and null-handler for composing and adapting handlers
  • processor-handler for per-record enrichment, rotating-file-handler for clock-driven file rotation with retention pruning, and buffered-handler for holding records back until a trigger-level record arrives
  • an extensible handler lifecycle protocol (handler-open-p, flush-handler, close-handler, with-handler, flush-logger) with atomic handle/flush/close admission
  • condition-fields / log-condition for turning Lisp conditions into structured :condition-type / :condition-message / :backtrace fields
  • with-log-span for paired :start / :end records with duration, outcome, and parent/child span correlation
  • a handler extension protocol for new destinations and encodings

Guide Map

  • Getting Started — loading cl-log-kit with ASDF or Nix, then a first logger, a first log line, and JSON output.
  • Levels — the five built-in severities, custom levels, and level-gated evaluation.
  • Fields — property-list fields, canonicalization, snapshotting, and resource limits.
  • Logger Derivation and Context — logger-with, derive-logger, logger-child, and with-log-context precedence rules.
  • Handlers — text-handler, json-handler, composition, and the lifecycle protocol.
  • Logging Conditions — condition-fields and log-condition.
  • Log Spans — with-log-span timing and correlation.
  • Records and Extension — log-record, implementing new handlers, and injecting a deterministic clock.
  • API Reference — every exported symbol, grouped by area.
  • Compatibility — supported platforms, the stability promise, and what counts as a security defect.
  • Benchmarks — handle-log-record timings and the allocation bounds checked by the suite.
  • Development — the dev shell, coverage floors, and the Nix-driven test workflow.

Nix Workflow

The flake.nix at the repository root packages cl-log-kit as a Nix flake:

  • nix develop — a devShell with SBCL and the test-only dependencies (cl-weave, cl-json-kit) wired into CL_SOURCE_REGISTRY.
  • nix build — builds the cl-log-kit ASDF system as a Nix package.
  • nix run .#test — runs the test suite under a bounded timeout.
  • nix flake check — the test suite, the formatting gate, and this site's --strict build, each as a reproducible derivation.
  • nix build .#docs — builds this documentation site with MkDocs (Material) in --strict mode, so broken links fail the build.

Project

cl-log-kit is part of the nerima-lisp org and follows its shared community health files:

Report reproducible defects, documentation gaps, and concrete feature requests through GitHub Issues. Include the smallest form that reproduces the problem and your SBCL version; for anything involving concurrency, ordering, or a hang, say how many threads were involved and whether it reproduces every run — that distinction usually determines where to look first.

License

MIT. See LICENSE.