4.1.2 Logging - Reference Documentation
Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith, Lari Hotari
Version: 2.3.8
4.1.2 Logging
The Basics
Grails uses its common configuration mechanism to provide the settings for the underlying Log4j log system, so all you have to do is add alog4j
setting to the file grails-app/conf/Config.groovy
.So what does this log4j
setting look like? Here's a basic example:log4j = { error 'org.codehaus.groovy.grails.web.servlet', // controllers 'org.codehaus.groovy.grails.web.pages' // GSP warn 'org.apache.catalina' }
Logging levels
There are several standard logging levels, which are listed here in order of descending priority:- off
- fatal
- error
- warn
- info
- debug
- trace
- all
log.error(msg)
will log a message at the 'error' level. Likewise, log.debug(msg)
will log it at 'debug'. Each of the above levels apart from 'off' and 'all' have a corresponding log method of the same name.The logging system uses that message level combined with the configuration for the logger (see next section) to determine whether the message gets written out. For example, if you have an 'org.example.domain' logger configured like so:warn 'org.example.domain'
Loggers
Loggers are fundamental to the logging system, but they are a source of some confusion. For a start, what are they? Are they shared? How do you configure them?A logger is the object you log messages to, so in the calllog.debug(msg)
, log
is a logger instance (of type Log). These loggers are cached and uniquely identified by name, so if two separate classes use loggers with the same name, those loggers are actually the same instance.There are two main ways to get hold of a logger:
- use the
log
instance injected into artifacts such as domain classes, controllers and services; - use the Commons Logging API directly.
log
property, then the name of the logger is 'grails.app.<type>.<className>', where type
is the type of the artifact, for example 'controllers' or 'services', and className
is the fully qualified name of the artifact. For example, if you have this service:package org.exampleclass MyService {
…
}
package org.otherimport org.apache.commons.logging.LogFactoryclass MyClass { private static final log = LogFactory.getLog(this) … }
getLog()
method, such as "myLogger", but this is less common because the logging system treats names with dots ('.') in a special way.Configuring loggers
You have already seen how to configure loggers in Grails:log4j = { error 'org.codehaus.groovy.grails.web.servlet' }
org.codehaus.groovy.grails.web.servlet.GrailsDispatcherServlet
class and the org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest
one.In other words, loggers are hierarchical. This makes configuring them by package much simpler than it would otherwise be.The most common things that you will want to capture log output from are your controllers, services, and other artifacts. Use the convention mentioned earlier to do that: grails.app.<artifactType>.<className> . In particular the class name must be fully qualifed, i.e. with the package if there is one:log4j = { // Set level for all application artifacts info "grails.app" // Set for a specific controller in the default package debug "grails.app.controllers.YourController" // Set for a specific domain class debug "grails.app.domain.org.example.Book" // Set for all taglibs info "grails.app.taglib" }
conf
- For anything undergrails-app/conf
such asBootStrap.groovy
(but excluding filters)filters
- For filterstaglib
- For tag librariesservices
- For service classescontrollers
- For controllersdomain
- For domain entities
org.codehaus.groovy.grails.commons
- Core artifact information such as class loading etc.org.codehaus.groovy.grails.web
- Grails web request processingorg.codehaus.groovy.grails.web.mapping
- URL mapping debuggingorg.codehaus.groovy.grails.plugins
- Log plugin activitygrails.spring
- See what Spring beans Grails and plugins are definingorg.springframework
- See what Spring is doingorg.hibernate
- See what Hibernate is doing
The Root Logger
All logger objects inherit their configuration from the root logger, so if no explicit configuration is provided for a given logger, then any messages that go to that logger are subject to the rules defined for the root logger. In other words, the root logger provides the default configuration for the logging system.Grails automatically configures the root logger to only handle messages at 'error' level and above, and all the messages are directed to the console (stdout for those with a C background). You can customise this behaviour by specifying a 'root' section in your logging configuration like so:log4j = { root { info() } … }
log4j = {
appenders {
file name:'file', file:'/var/logs/mylog.log'
}
root {
debug 'stdout', 'file'
}
}
org.apache.log4j.Logger
instance is passed as an argument to the log4j closure. This lets you work with the logger directly:log4j = { root -> root.level = org.apache.log4j.Level.DEBUG … }
Logger
instance, refer to the Log4j API documentation.Those are the basics of logging pretty well covered and they are sufficient if you're happy to only send log messages to the console. But what if you want to send them to a file? How do you make sure that messages from a particular logger go to a file but not the console? These questions and more will be answered as we look into appenders.Appenders
Loggers are a useful mechanism for filtering messages, but they don't physically write the messages anywhere. That's the job of the appender, of which there are various types. For example, there is the default one that writes messages to the console, another that writes them to a file, and several others. You can even create your own appender implementations!This diagram shows how they fit into the logging pipeline:
log4j = { appenders { rollingFile name: "myAppender", maxFileSize: 1024, file: "/tmp/logs/myApp.log" } }
Name | Class | Description |
---|---|---|
jdbc | JDBCAppender | Logs to a JDBC connection. |
console | ConsoleAppender | Logs to the console. |
file | FileAppender | Logs to a single file. |
rollingFile | RollingFileAppender | Logs to rolling files, for example a new file each day. |
name
, maxFileSize
and file
properties of the RollingFileAppender
instance.You can have as many appenders as you like - just make sure that they all have unique names. You can even have multiple instances of the same appender type, for example several file appenders that log to different files.If you prefer to create the appender programmatically or if you want to use an appender implementation that's not available in the above syntax, simply declare an appender
entry with an instance of the appender you want:import org.apache.log4j.*log4j = { appenders { appender new RollingFileAppender( name: "myAppender", maxFileSize: 1024, file: "/tmp/logs/myApp.log") } }
JMSAppender
, SocketAppender
, SMTPAppender
, and more.Once you have declared your extra appenders, you can attach them to specific loggers by passing the name as a key to one of the log level methods from the previous section:error myAppender: "grails.app.controllers.BookController"
error myAppender: "grails.app.controllers.BookController", myFileAppender: ["grails.app.controllers.BookController", "grails.app.services.BookService"], rollingFile: "grails.app.controllers.BookController"
myFileAppender
) by using a list.Be aware that you can only configure a single level for a logger, so if you tried this code:error myAppender: "grails.app.controllers.BookController" debug myFileAppender: "grails.app.controllers.BookController" fatal rollingFile: "grails.app.controllers.BookController"
log4j = {
appenders {
console name: "stdout", threshold: org.apache.log4j.Level.INFO
}
}
threshold
argument which determines the cut-off for log messages. This argument is available for all appenders, but do note that you currently have to specify a Level
instance - a string such as "info" will not work.Custom Layouts
By default the Log4j DSL assumes that you want to use a PatternLayout. However, there are other layouts available including:xml
- Create an XML log filehtml
- Creates an HTML log filesimple
- A simple textual logpattern
- A Pattern layout
layout
setting:log4j = { appenders { console name: "customAppender", layout: pattern(conversionPattern: "%c{2} %m%n") } }
log4j = { appenders { console name: "stdout", layout: pattern(conversionPattern: "%c{2} %m%n") } }
Environment-specific configuration
Since the logging configuration is insideConfig.groovy
, you can put it inside an environment-specific block. However, there is a problem with this approach: you have to provide the full logging configuration each time you define the log4j
setting. In other words, you cannot selectively override parts of the configuration - it's all or nothing.To get around this, the logging DSL provides its own environment blocks that you can put anywhere in the configuration:log4j = { appenders { console name: "stdout", layout: pattern(conversionPattern: "%c{2} %m%n") environments { production { rollingFile name: "myAppender", maxFileSize: 1024, file: "/tmp/logs/myApp.log" } } } root { //… } // other shared config info "grails.app.controller" environments { production { // Override previous setting for 'grails.app.controller' error "grails.app.controllers" } } }
root
definition, but you can put the root
definition inside an environment block.Full stacktraces
When exceptions occur, there can be an awful lot of noise in the stacktrace from Java and Groovy internals. Grails filters these typically irrelevant details and restricts traces to non-core Grails/Groovy class packages.When this happens, the full trace is always logged to theStackTrace
logger, which by default writes its output to a file called stacktrace.log
. As with other loggers though, you can change its behaviour in the configuration. For example if you prefer full stack traces to go to the console, add this entry:error stdout: "StackTrace"
log4j = { appenders { rollingFile name: "stacktrace", maxFileSize: 1024, file: "/var/tmp/logs/myApp-stacktrace.log" } }
log4j = { appenders { 'null' name: "stacktrace" } }
grails.full.stacktrace
VM property to true
:grails -Dgrails.full.stacktrace=true run-app
Masking Request Parameters From Stacktrace Logs
When Grails logs a stacktrace, the log message may include the names and values of all of the request parameters for the current request. To mask out the values of secure request parameters, specify the parameter names in thegrails.exceptionresolver.params.exclude
config property:grails.exceptionresolver.params.exclude = ['password', 'creditCard']
grails.exceptionresolver.logRequestParameters
config property to false
. The default value is true
when the application is running in DEVELOPMENT mode and false
for all other modes.grails.exceptionresolver.logRequestParameters=false
Logger inheritance
Earlier, we mentioned that all loggers inherit from the root logger and that loggers are hierarchical based on '.'-separated terms. What this means is that unless you override a parent setting, a logger retains the level and the appenders configured for that parent. So with this configuration:log4j = {
appenders {
file name:'file', file:'/var/logs/mylog.log'
}
root {
debug 'stdout', 'file'
}
}
log4j = { appenders { … } root { … } info additivity: false stdout: ["grails.app.controllers.BookController", "grails.app.services.BookService"] }
info additivity: false, ["grails.app.controllers.BookController", "grails.app.services.BookService"]
Customizing stack trace printing and filtering
Stacktraces in general and those generated when using Groovy in particular are quite verbose and contain many stack frames that aren't interesting when diagnosing problems. So Grails uses a implementation of theorg.codehaus.groovy.grails.exceptions.StackTraceFilterer
interface to filter out irrelevant stack frames. To customize the approach used for filtering, implement that interface in a class in src/groovy or src/java and register it in Config.groovy
:grails.logging.stackTraceFiltererClass = 'com.yourcompany.yourapp.MyStackTraceFilterer'
org.codehaus.groovy.grails.exceptions.StackTracePrinter
interface in a class in src/groovy or src/java and register it in Config.groovy
:grails.logging.stackTracePrinterClass = 'com.yourcompany.yourapp.MyStackTracePrinter'
org.codehaus.groovy.grails.web.errors.ErrorsViewStackTracePrinter
and it's registered as a Spring bean. To use your own implementation, either implement the org.codehaus.groovy.grails.exceptions.StackTraceFilterer
directly or subclass ErrorsViewStackTracePrinter
and register it in grails-app/conf/spring/resources.groovy
as:import com.yourcompany.yourapp.MyErrorsViewStackTracePrinterbeans = { errorsViewStackTracePrinter(MyErrorsViewStackTracePrinter,
ref('grailsResourceLocator'))
}
Alternative logging libraries
By default, Grails uses Log4J to do its logging. For most people this is absolutely fine, and many users don't even care what logging library is used. But if you're not one of those and want to use an alternative, such as the JDK logging package or logback, you can do so by simply excluding a couple of dependencies from the global set and adding your own:grails.project.dependency.resolution = { inherits("global") { excludes "grails-plugin-logging", "log4j" } … dependencies { runtime "ch.qos.logback:logback-core:0.9.29" … } … }