Quick Start¶
This walkthrough builds a logger, emits a few log lines, derives a scoped
child logger, and switches to JSON output. See Installation
first if cl-log-kit is not loaded yet.
Your first logger¶
(defpackage #:my-app
(:use #:cl #:log-kit)
(:shadowing-import-from #:log-kit #:log))
(in-package #:my-app)
(defparameter *logger*
(make-logger
:name "api"
:handler (make-instance 'text-handler)
:level +level-info+
:fields '(:service "orders")))
(log-info *logger* "server started" :port 8080)
;; ts=... level=INFO logger="api" msg="server started" field."port"="8080" field."service"="orders"
make-logger accepts a :name, a :handler instance, a minimum :level
(the default is +level-info+), and a base :fields property list shared by
every record the logger emits.
Switching to JSON¶
Swap the handler — nothing else about the call sites changes:
(defparameter *logger*
(make-logger
:name "api"
:handler (make-instance 'json-handler)
:level +level-info+
:fields '(:service "orders")))
(log-info *logger* "server started" :port 8080)
;; {"time":...,"level":"INFO","logger":"api","message":"server started","fields":{"port":8080,"service":"orders"}}
See Handlers for the full text-handler / json-handler
wire formats and how to compose multiple handlers.
Deriving a scoped logger¶
logger-with returns a new logger with additional fields; call-site fields
still override logger fields with the same canonical name:
(defparameter *request-logger*
(logger-with *logger* :request-id "abc123"))
(log-warn *request-logger* "slow response" :duration-ms 842)
See Logger Derivation and Context for derive-logger,
logger-child, and dynamically scoped context via with-log-context.
Using a default logger¶
Explicit-logger and default-logger calls are separate macro families.
log-debug / log-info / log-warn / log-error / log-fatal always
evaluate their first argument as the logger, so (log-info "server started"
:port 8080) signals a type-error rather than falling back to
*default-logger*. Set *default-logger* once, then use the log-default-*
macros anywhere without threading a logger through every call site:
(set-default-logger
(make-logger :handler (make-instance 'text-handler)))
(log-default-error "request failed" :reason "timeout")
;; ts=... level=ERROR logger="root" msg="request failed" field."reason"="timeout"
with-default-logger scopes a different default dynamically, without
mutating the process-wide default:
log-default is the generic form behind log-default-debug /
log-default-info / … — reach for it when the level is itself a variable
instead of a compile-time constant:
Next steps¶
- Levels — severities, thresholds, and level-gated evaluation.
- Fields — structured field rules and resource limits.
- Logging Conditions — logging a caught Lisp condition.
- Log Spans — timing an operation with
with-log-span.