Logging
By default, logging functionality is provided by the clojure.tools.logging library. The library provides macros that delegate to a specific logging implementation. The default implementation used in Luminus is the logback library.
There are six log levels in clojure.tools.logging
, and any Clojure data structures can be logged directly. The log levels are trace
, debug
, info
, warn
, error
, and fatal
.
(ns example
(:require [clojure.tools.logging :as log]))
(log/info "Hello")
=>[2015-12-24 09:04:25,711][INFO][myapp.handler] Hello
(log/debug {:user {:id "Anonymous"}})
=>[2015-12-24 09:04:25,711][DEBUG][myapp.handler] {:user {:id "Anonymous"}}
(log/error (Exception. "I'm an error") "something bad happened")
=>[2015-12-24 09:43:47,193][ERROR][myapp.handler] something bad happened
java.lang.Exception: I'm an error
at myapp.handler$init.invoke(handler.clj:21)
at myapp.core$start_http_server.invoke(core.clj:44)
at myapp.core$start_app.invoke(core.clj:61)
...
Logging Configuration
The default logger configuration is found in the resources/logback.xml
file and looks as follows:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<charset>UTF-8</charset>
<pattern>%date{ISO8601} [%thread] %-5level %logger{36} - %msg %n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>log/myapp.log</file>
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>log/myapp.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- keep 30 days of history -->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<pattern>%date{ISO8601} [%thread] %-5level %logger{36} - %msg %n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
</configuration>
An external logging configuration can be provided by setting the logback.configurationFile
Java system property that points to the path of the log configuration file. For example, we could create a production configuration called prod-log-config.xml
and have it log to the /var/log/myapp.log
location.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/var/log/myapp.log</file>
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/var/log/myapp.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- keep 30 days of history -->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<pattern>%date{ISO8601} [%thread] %-5level %logger{36} - %msg %n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="FILE" />
</root>
</configuration>
Then we can start the app with the following flag to have it use this logging configuration:
java -Dlogback.configurationFile=prod-log-config.xml -jar myapp.jar
Please refer to the official documentation for further information on configuring logback
.
Logging HTTP requests
You can use ring-logger in order to log HTTP requests:
# project.clj
:dependencies [...
[ring-logger "1.1.1"]
]
(ns myapp.routes.home
(:require
...
[ring.logger]))
(defn home-routes []
[ ""
{:middleware [
ring.logger/wrap-with-logger
middleware/wrap-csrf
middleware/wrap-formats
]}
["/" {:get home-page}]
["/about" {:get about-page}]])
...