Logging
This guide explains how logging works in ShipClojure, how to configure log levels for different environments, and how to add logging to your own code.
Overview
ShipClojure uses Telemere as its logging library. Telemere is the next-generation replacement for Timbre, providing structured telemetry with a unified API.
Key Features:
Structured JSON logging for production
Contextual logging that accumulates request information
Unified backend for both Clojure logs and Java/SLF4J logs
Per-namespace log level configuration
Automatic sensitive data redaction
Telemere as the Unified Logging Backend
ShipClojure uses Telemere for all logging, including logs from Java libraries. This is achieved through telemere-slf4j, which routes all SLF4J logging calls through Telemere.
Dependencies
;; In deps.edn
com.taoensso/telemere {:mvn/version "1.2.0"}
com.taoensso/telemere-slf4j {:mvn/version "1.2.0"}
org.slf4j/slf4j-api {:mvn/version "2.0.17"}This setup means:
Your Clojure code uses
taoensso.telemeredirectlyJava libraries (HikariCP, HTTP-Kit, PostgreSQL driver, etc.) use SLF4J, which routes to Telemere
All logs go through the same handlers and filters
Verifying Interop
You can verify the SLF4J integration is working in your REPL:
Basic Logging
Creating Logs
Use t/log! for basic logging:
Log Levels
Telemere supports these levels (from most to least verbose):
:trace:debug:info:warn:error:fatal
Contextual Logging
One of Telemere's most powerful features is contextual logging with t/with-ctx+. ShipClojure uses this to gradually add context as a request flows through the middleware chain.
How Context Accumulates
This means any log statement deep in your business logic automatically includes the request method, URI, request ID, user info, and more.
Implementation
The context is added through Ring middleware in saas.web.middleware.logger:
Later middleware adds more context:
Benefits
With this setup, every log entry contains the full context:
You can trace all logs for a single request by filtering on req-id.
Configuring Log Levels
Per-Environment Configuration
Log levels are configured in the environment-specific env.clj files:
env/dev/clj/saas/env.clj- Development settingsenv/prod/clj/saas/env.clj- Production settingsenv/test/clj/saas/env.clj- Test settings
Understanding Signal Kinds
Telemere uses "signal kinds" to distinguish log sources:
:log- Logs from Telemere'slog!macro (your Clojure code):slf4j- Logs from Java libraries via SLF4J
When configuring levels, you need to specify the correct kind:
Silencing Noisy Dependencies
When you add a new Java dependency that's too chatty, silence it by adding to the appropriate configure-*-log-levels! function:
Development Configuration
Development is configured to be verbose for application code but quiet for third-party libraries:
Production Configuration
Production keeps application logs at INFO:
Test Configuration
Tests are configured to be as quiet as possible:
Sensitive Data Redaction
The logging middleware automatically redacts sensitive fields from request parameters:
Logged as:
Adding Custom Redaction
Edit src/clj/saas/logging.clj:
JSON File Logging
In production, logs are written to JSON files for easy querying.
Configuration
The JSON file handler is configured in env/prod/clj/saas/env.clj:
Log Format
Each line is a single JSON object:
Common jq Queries
Adding Logging to Your Code
In Handlers
Error Logging
Debugging Logging Issues
Check Active Handlers
Check Current Filters
Test Signal Creation
Temporarily Disable Filters
Further Reading
Last updated