### Log4j2 Configuration Example
Source: https://github.com/clojure/tools.logging/blob/master/README.md
A basic Log4j2 configuration file. Ensure the pattern layout includes %throwable to print data maps for exceptions.
```properties
status = warn
monitorInterval = 5
appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %date %level %logger %message%n%throwable
rootLogger.level = info
rootLogger.appenderRef.stdout.ref = STDOUT
```
--------------------------------
### `log-capture!` / `log-uncapture!` — Redirect System.out and System.err to Log
Source: https://context7.com/clojure/tools.logging/llms.txt
These functions allow you to redirect Java's `System.out` and `System.err` streams to the logging system. `log-capture!` starts the redirection for a given logger namespace, and `log-uncapture!` restores the original streams.
```APIDOC
## `log-capture!` / `log-uncapture!` — Redirect System.out and System.err to Log
Captures Java's `System.out` and `System.err` streams, routing all output to the log under a given logger namespace.
### Usage
```clojure
(require '[clojure.tools.logging :as log])
;; Capture with defaults (:info for stdout, :error for stderr)
(log/log-capture! 'myapp.stdout)
;; All System.out/System.err writes now go to the log
(println "This goes to :info log under myapp.stdout")
(.println System/err "This goes to :error log under myapp.stdout")
;; Restore originals
(log/log-uncapture!)
;; Capture with custom levels
(log/log-capture! 'myapp.legacy :debug :warn)
;; stdout => :debug, stderr => :warn
```
```
--------------------------------
### Configure SLF4J Backend via deps.edn
Source: https://context7.com/clojure/tools.logging/llms.txt
Set the `clojure.tools.logging.factory` JVM system property within a `:jvm-opts` alias in your `deps.edn` file to force the SLF4J backend.
```clojure
{:aliases
{:dev {:jvm-opts ["-Dclojure.tools.logging.factory=clojure.tools.logging.impl/slf4j-factory"]}}}
```
--------------------------------
### Maven Dependency
Source: https://github.com/clojure/tools.logging/blob/master/README.md
Add this to your pom.xml file to include the tools.logging library.
```xml
org.clojure
tools.logging
1.3.1
```
--------------------------------
### Leiningen Dependency
Source: https://github.com/clojure/tools.logging/blob/master/README.md
Add this to your project.clj file to include the tools.logging library.
```clojure
[org.clojure/tools.logging "1.3.1"]
```
--------------------------------
### Clojure CLI/deps.edn Dependency
Source: https://github.com/clojure/tools.logging/blob/master/README.md
Add this to your deps.edn file to include the tools.logging library.
```clojure
org.clojure/tools.logging {:mvn/version "1.3.1"}
```
--------------------------------
### Level-Specific Format Macros
Source: https://context7.com/clojure/tools.logging/llms.txt
Utilize format-string variants like `infof`, `warnf`, `errorf` for structured logging, similar to `clojure.core/format`. An optional Throwable can be included.
```clojure
(require '[clojure.tools.logging :as log])
;; Format string with args
(log/infof "User %s logged in from %s" "alice" "192.168.1.1")
;; => logs: "User alice logged in from 192.168.1.1"
```
```clojure
;; With exception
(try
(parse-int "not-a-number")
(catch Exception e
(log/warnf e "Failed to parse value: %s" "not-a-number")))
;; => logs warning with exception attached
```
```clojure
;; Useful for structured numeric formatting
(log/debugf "Processed %d records in %.2f ms" 1500 42.7)
;; => logs: "Processed 1500 records in 42.70 ms"
```
--------------------------------
### Format-String Logging Macro (`logf`)
Source: https://context7.com/clojure/tools.logging/llms.txt
Use `logf` for low-level format-string logging with an explicit level. It serves as the base for other formatted logging functions. It supports logging with an optional throwable.
```clojure
(require '[clojure.tools.logging :as log])
(log/logf :warn "Retry %d/%d for host %s" 2 5 "db-primary")
;; => logs: "Retry 2/5 for host db-primary"
;; With throwable
(log/logf :error (ex-info "Timeout" {:host "db-primary"}) "Connection failed to %s after %d ms" "db-primary" 5000)
```
--------------------------------
### Add clojure.tools.logging Dependency
Source: https://context7.com/clojure/tools.logging/llms.txt
Specify clojure.tools.logging as a dependency in your build tool configuration.
```clojure
;; deps.edn
{:deps {org.clojure/tools.logging {:mvn/version "1.3.1"}}}
```
```clojure
;; Leiningen project.clj
[org.clojure/tools.logging "1.3.1"]
```
```xml
org.clojure
tools.logging
1.3.1
```
--------------------------------
### Force SLF4J Backend in project.clj
Source: https://context7.com/clojure/tools.logging/llms.txt
Configure your project to use the SLF4J backend by setting the `clojure.tools.logging.factory` JVM system property in `project.clj`.
```clojure
;; project.clj — force SLF4J backend
:jvm-opts ["-Dclojure.tools.logging.factory=clojure.tools.logging.impl/slf4j-factory"]
```
--------------------------------
### General Logging Macro `log`
Source: https://context7.com/clojure/tools.logging/llms.txt
The foundational `log` macro allows specifying the log level, logger namespace, and factory overrides. It supports messages and optional Throwables.
```clojure
(require '[clojure.tools.logging :as log])
;; Basic form: (log level message)
(log/log :info "Application initialized")
```
```clojure
;; With throwable: (log level throwable message)
(log/log :error (ex-info "Bad input" {:input nil}) "Validation failed")
```
```clojure
;; With explicit namespace: (log logger-ns level throwable message)
(log/log 'myapp.subsystem :warn nil "Subsystem degraded")
```
```clojure
;; With explicit factory: (log logger-factory logger-ns level throwable message)
(log/log log/*logger-factory* *ns* :debug nil "Custom factory logging")
```
--------------------------------
### Level-Specific Print-Style Logging Macros
Source: https://context7.com/clojure/tools.logging/llms.txt
Use convenience macros like `info`, `error`, `debug`, `fatal` for logging messages with optional Throwables. Message arguments are evaluated lazily.
```clojure
(require '[clojure.tools.logging :as log])
;; Simple message
(log/info "Server started on port" 8080)
;; => logs: "Server started on port 8080"
```
```clojure
;; With a Throwable as first arg
(try
(/ 1 0)
(catch Exception e
(log/error e "Unhandled error during request processing")))
;; => logs: "Unhandled error during request processing" with stack trace
```
```clojure
;; Multiple args are joined with spaces
(log/debug "Request" {:method :GET :path "/api/users"} "from" "127.0.0.1")
;; => logs: "Request {:method :GET, :path \"/api/users\"} from 127.0.0.1"
```
```clojure
;; Fatal level — used for unrecoverable states
(log/fatal "Database connection pool exhausted — shutting down")
```
--------------------------------
### `clojure.tools.logging.readable` — Readable Print Variants
Source: https://context7.com/clojure/tools.logging/llms.txt
This namespace provides drop-in replacements for the standard logging macros. Its key feature is that non-string arguments are printed using `pr-str`, which preserves their Clojure data representation in the log output.
```APIDOC
## `clojure.tools.logging.readable` — Readable Print Variants
A drop-in namespace providing variants of all logging macros where non-string arguments are printed via `pr-str`, preserving their Clojure data representation in log output.
### Usage
```clojure
(require '[clojure.tools.logging :as log]
'[clojure.tools.logging.readable :as logr])
(def user {:name "alice" :roles #{:admin :user}})
;; Standard logging — values printed with print-str (no quotes on strings)
(log/info "User:" user)
;; => logs: "User: {:name alice, :roles #{:admin :user}}"
;; Readable logging — values printed with pr-str (preserves data types)
(logr/info "User:" user)
;; => logs: "User: {:name \"alice\", :roles #{:admin :user}}"
;; Readable format — all format args are pr-str'd
(log/debugf "Value: %s" "hello") ;; => "Value: hello"
(logr/debugf "Value: %s" "hello") ;; => "Value: \"hello\""
;; With throwable
(logr/error (ex-info "Failed" {:code 404}) "Response was:" {:status 404 :body "Not Found"})
;; => logs with exception and readable map representation
```
```
--------------------------------
### Testing Log Output with clojure.tools.logging.test
Source: https://context7.com/clojure/tools.logging/llms.txt
Use `with-log`, `logged?`, `matches`, and `the-log` for asserting log messages in unit tests. `logged?` supports exact message, regex, and exception type matching. `matches` returns matching entries, and `the-log` inspects all captured entries.
```clojure
(require '[clojure.tools.logging :as log]
'[clojure.tools.logging.test :refer [logged? matches the-log with-log]]
'[clojure.test :refer [deftest is]])
(deftest test-logging-behavior
(with-log
;; Execute code under test
(log/info "Hello World!")
(log/error (Exception. "Something went wrong") "Error occurred")
(log/debug "Detail" {:key "value"})
;; logged? — boolean assertion
(is (logged? 'user :info #"Hello")) ; regex match on message
(is (logged? 'user :info "Hello World!")) ; exact message match
(is (logged? 'user :error ; match exception type + message pattern
[Exception #"went wrong"]
#"Error"))
(is (not (logged? 'user :debug "Detail"))) ; default: all levels enabled, so this IS logged
;; matches — returns matching entries or nil
(is (seq (matches 'user :info #"Hello")))
;; the-log — inspect all captured entries
(let [entries (the-log)]
(is (= 3 (count entries)))
(is (= :info (:level (first entries)))))))
;; Test with namespace predicate
(with-log
(log/info "msg")
(logged? #(= (str %) "user") :info "msg")) ; fn predicate for namespace
```
--------------------------------
### Log and Return an Expression (`spy`)
Source: https://context7.com/clojure/tools.logging/llms.txt
Use `spy` to evaluate an expression, log its form and result at a specified level (defaults to :debug), and return the result. It's ideal for inline debugging.
```clojure
(require '[clojure.tools.logging :as log])
;; Default :debug level — logs form and result, returns value
(def result (log/spy (+ 1 2)))
;; => logs: "(+ 1 2)\n=> 3"
;; result => 3
;; Explicit level
(defn process [items]
(->> items
(log/spy :info (filter odd? items))
(map inc)))
;; Inline in threading pipelines
(-> {:user "alice" :role :admin}
(log/spy :debug)
(assoc :logged-at (java.time.Instant/now)))
```
--------------------------------
### Print-Style Logging Macro `logp`
Source: https://context7.com/clojure/tools.logging/llms.txt
The low-level `logp` macro takes an explicit level as the first argument, followed by messages and an optional exception. It forms the basis for other print-style macros.
```clojure
(require '[clojure.tools.logging :as log])
;; Level as first arg
(log/logp :info "Processing batch" 42 "of" 100)
;; => logs: "Processing batch 42 of 100"
```
```clojure
;; With exception as second arg
(log/logp :error (RuntimeException. "Timeout") "Request timed out after" 30 "seconds")
```
```clojure
;; Dynamic level selection
(doseq [[level msg] [[:debug "Starting"] [:info "Running"] [:warn "Slow"]]]
(log/logp level msg))
```
--------------------------------
### `logf` — Format-String Logging Macro
Source: https://context7.com/clojure/tools.logging/llms.txt
A low-level macro for formatted logging that accepts an explicit log level. It serves as the base for other formatted logging functions like `debugf`, `infof`, etc.
```APIDOC
## `logf` — Format-String Logging Macro
Low-level format macro with an explicit level argument. The foundation for `debugf`, `infof`, etc.
### Usage
```clojure
(require '[clojure.tools.logging :as log])
;; Basic usage with arguments
(log/logf :warn "Retry %d/%d for host %s" 2 5 "db-primary")
;; => logs: "Retry 2/5 for host db-primary"
;; Usage with a throwable
(log/logf :error (ex-info "Timeout" {:host "db-primary"}) "Connection failed to %s after %d ms" "db-primary" 5000)
```
```
--------------------------------
### Minimal log4j2.properties Console Configuration
Source: https://context7.com/clojure/tools.logging/llms.txt
A basic `log4j2.properties` file to configure console logging. It sets the status level, console appender, and a pattern layout that includes the date, level, logger, message, and throwable information.
```properties
# log4j2.properties — minimal console configuration
status = warn
monitorInterval = 5
appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
# Use %throwable (not %xThrowable) to print ex-info data maps
appender.console.layout.pattern = %date %level %logger %message%n%throwable
rootLogger.level = info
rootLogger.appenderRef.stdout.ref = STDOUT
```
--------------------------------
### Log Expression Result With a Format String (`spyf`)
Source: https://context7.com/clojure/tools.logging/llms.txt
Use `spyf` for logging an expression's result using a format string, similar to `spy` but with formatted output. It logs at a specified level (defaults to :debug).
```clojure
(require '[clojure.tools.logging :as log])
;; Default :debug level
(log/spyf "List size: %d" (count my-list))
;; => logs: "List size: 7", returns the count value
;; Explicit level
(log/spyf :info "User count: %d" (count (db/all-users)))
```
--------------------------------
### `with-logs` — Redirect `*out*` and `*err*` to Log Within a Scope
Source: https://context7.com/clojure/tools.logging/llms.txt
This macro evaluates its body forms within a context where Clojure's `*out*` and `*err*` are dynamically bound to log-backed writers. This is a scoped operation and does not affect the global `System.out` or `System.err`.
```APIDOC
## `with-logs` — Redirect `*out*` and `*err*` to Log Within a Scope
Evaluates body forms in a context where Clojure's `*out*` and `*err*` are bound to log-backed writers. Unlike `log-capture!`, this is scoped and does not affect `System.out`/`System.err`.
### Usage
```clojure
(require '[clojure.tools.logging :as log])
;; Basic usage — *out* => :info, *err* => :error
(log/with-logs 'myapp.legacy-lib
(run-legacy-code-that-uses-println))
;; Custom levels via vector syntax
(log/with-logs ['myapp.shell :debug :warn]
(clojure.java.shell/sh "some-script.sh"))
;; Capturing output of a third-party function
(log/with-logs 'myapp.vendor
(vendor/initialize!)
(vendor/run-job {:id 42}))
```
```
--------------------------------
### Logger and LoggerFactory Protocols
Source: https://context7.com/clojure/tools.logging/llms.txt
The `impl` namespace defines `Logger` and `LoggerFactory` protocols for custom backend integration. You can check the auto-selected factory or programmatically switch factories.
```clojure
(require '[clojure.tools.logging.impl :as impl])
;; Check which factory was auto-selected
(impl/name log/*logger-factory*)
;; => "org.slf4j" (or whichever backend was found)
;; Explicitly select a factory via system property (JVM startup flag):
;; -Dclojure.tools.logging.factory=clojure.tools.logging.impl/slf4j-factory
;; Programmatically switch factory at runtime
(alter-var-root #'log/*logger-factory* (constantly (impl/log4j2-factory)))
;; Use disabled factory to suppress all logging (e.g., in tests)
(binding [log/*logger-factory* impl/disabled-logger-factory]
(log/info "This will not be logged"))
;; Implement a custom LoggerFactory
(defn my-factory []
(reify impl/LoggerFactory
(name [_] "my-custom-logger")
(get-logger [_ logger-ns]
(reify impl/Logger
(enabled? [_ level] (contains? #{:warn :error :fatal} level))
(write! [_ level throwable message]
(println (str "[" level "] " message))
(when throwable (.printStackTrace throwable)))))))
(binding [log/*logger-factory* (my-factory ())]
(log/warn "This will print")
(log/debug "This will be suppressed"))
```
--------------------------------
### Set JVM Option for Logging Factory
Source: https://github.com/clojure/tools.logging/blob/master/README.md
Configure Leiningen to set a JVM option that specifies the logging implementation factory.
```clojure
:jvm-opts ["-Dclojure.tools.logging.factory=clojure.tools.logging.impl/slf4j-factory"]
```
--------------------------------
### Readable Print Variants (`clojure.tools.logging.readable`)
Source: https://context7.com/clojure/tools.logging/llms.txt
Use the `clojure.tools.logging.readable` namespace for logging variants that print non-string arguments using `pr-str`, preserving their Clojure data representation. This is useful for debugging complex data structures.
```clojure
(require '[clojure.tools.logging :as log]
'[clojure.tools.logging.readable :as logr])
(def user {:name "alice" :roles #{:admin :user}})
;; Standard logging — values printed with print-str (no quotes on strings)
(log/info "User:" user)
;; => logs: "User: {:name alice, :roles #{:admin :user}}"
;; Readable logging — values printed with pr-str (preserves data types)
(logr/info "User:" user)
;; => logs: "User: {:name \"alice\", :roles #{:admin :user}}"
;; Readable format — all format args are pr-str'd
(log/debugf "Value: %s" "hello") ;; => "Value: hello"
(logr/debugf "Value: %s" "hello") ;; => "Value: \"hello\""
;; With throwable
(logr/error (ex-info "Failed" {:code 404}) "Response was:" {:status 404 :body "Not Found"})
;; => logs with exception and readable map representation
```
--------------------------------
### `spyf` — Log Expression Result With a Format String
Source: https://context7.com/clojure/tools.logging/llms.txt
Similar to `spy`, this macro evaluates an expression, but it logs the result using a specified format string. It returns the evaluated result unchanged.
```APIDOC
## `spyf` — Log Expression Result With a Format String
Like `spy`, but logs using a format string applied to the expression's result.
### Usage
```clojure
(require '[clojure.tools.logging :as log])
;; Default :debug level
(log/spyf "List size: %d" (count my-list))
;; => logs: "List size: 7", returns the count value
;; Explicit level
(log/spyf :info "User count: %d" (count (db/all-users)))
```
```
--------------------------------
### Redirect `*out*`/`*err*` Within a Scope (`with-logs`)
Source: https://context7.com/clojure/tools.logging/llms.txt
Use `with-logs` to temporarily redirect Clojure's `*out*` and `*err*` streams to log-backed writers within a specific scope. This is useful for capturing output from functions that use `println` or `print` without affecting `System.out`/`System.err` globally.
```clojure
(require '[clojure.tools.logging :as log])
;; Basic usage — *out* => :info, *err* => :error
(log/with-logs 'myapp.legacy-lib
(run-legacy-code-that-uses-println))
;; Custom levels via vector syntax
(log/with-logs ['myapp.shell :debug :warn]
(clojure.java.shell/sh "some-script.sh"))
;; Capturing output of a third-party function
(log/with-logs 'myapp.vendor
(vendor/initialize!)
(vendor/run-job {:id 42}))
```
--------------------------------
### Redirect System.out/err to Log (`log-capture!`)
Source: https://context7.com/clojure/tools.logging/llms.txt
Use `log-capture!` to redirect Java's `System.out` and `System.err` streams to a specified logger namespace. This is useful for capturing output from libraries that write directly to stdout/stderr. Remember to call `log-uncapture!` to restore the original streams.
```clojure
(require '[clojure.tools.logging :as log])
;; Capture with defaults (:info for stdout, :error for stderr)
(log/log-capture! 'myapp.stdout)
;; All System.out/System.err writes now go to the log
(println "This goes to :info log under myapp.stdout")
(.println System/err "This goes to :error log under myapp.stdout")
;; Restore originals
(log/log-uncapture!)
;; Capture with custom levels
(log/log-capture! 'myapp.legacy :debug :warn)
;; stdout => :debug, stderr => :warn
```
--------------------------------
### Check If a Level Is Enabled (`enabled?`)
Source: https://context7.com/clojure/tools.logging/llms.txt
Use `enabled?` to check if a log level is active for the current or a specified namespace. This is useful for conditionally executing expensive operations that are only needed for debugging.
```clojure
(require '[clojure.tools.logging :as log])
;; Check current namespace
(when (log/enabled? :debug)
(log/debug "Expensive debug payload:" (compute-diagnostic-report)))
;; Check a specific namespace
(when (log/enabled? :trace 'myapp.db)
(log/log 'myapp.db :trace nil (str "SQL: " (render-query query))))
```
--------------------------------
### `enabled?` — Check If a Level Is Enabled
Source: https://context7.com/clojure/tools.logging/llms.txt
Determines if a given log level is currently enabled for the current or a specified namespace. This is useful for conditionally executing code that is expensive to compute, only when logging is active.
```APIDOC
## `enabled?` — Check If a Level Is Enabled
Returns `true` if the given log level is currently enabled for the current (or specified) namespace. Use only when you need to gate non-logging work, not just message construction.
### Usage
```clojure
(require '[clojure.tools.logging :as log])
;; Check current namespace
(when (log/enabled? :debug)
(log/debug "Expensive debug payload:" (compute-diagnostic-report)))
;; Check a specific namespace
(when (log/enabled? :trace 'myapp.db)
(log/log 'myapp.db :trace nil (str "SQL: " (render-query query))))
```
```
--------------------------------
### `spy` — Log and Return an Expression
Source: https://context7.com/clojure/tools.logging/llms.txt
Evaluates an expression, logs the expression itself and its result at the `:debug` level (or a specified level), and then returns the result unchanged. This is ideal for inline debugging.
```APIDOC
## `spy` — Log and Return an Expression
Evaluates an expression, logs the expression form and its result at `:debug` level (or a specified level), and returns the result unchanged. Ideal for inline debugging without restructuring code.
### Usage
```clojure
(require '[clojure.tools.logging :as log])
;; Default :debug level — logs form and result, returns value
(def result (log/spy (+ 1 2)))
;; => logs: "(+ 1 2)\n=> 3"
;; result => 3
;; Explicit level
(defn process [items]
(->> items
(log/spy :info (filter odd? items))
(map inc)))
;; Inline in threading pipelines
(-> {:user "alice" :role :admin}
(log/spy :debug)
(assoc :logged-at (java.time.Instant/now)))
```
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.