haxx qua non

Grails stacktrace.log

by jja on
Topics:

Grails 1.0.x started creating a stacktrace.log file in the directory where the servlet container starts. In a development environment, using grails run-app, that's simple enough--- it appears in the top level of your application. In a production environment, this becomes a problem. Your production container (e.g. Tomcat) may start someplace where it can't create files, like /. Thus you get exceptions sent to your container's log files like:

java.io.FileNotFoundException: stacktrace.log (Permission denied)

Also, messages are appended to stacktrace.log-- so it will continue to grow if you don't do something about it. One option is to change where your container starts, e.g. have the startup script change to its logs directory. You can also configure your grails app to change the location of the stacktrace.log file or turn it off completely.

Changing file location

To change the file location/name, edit your grails-app/conf/Config.groovy file. In Grails 1.0.3, you want to change the line:

appender.'stacktraceLog.File'="stacktrace.log"

to something like:

appender.'stacktraceLog.File'="/var/log/myapp/stacktrace.log"

In Grails 1.0, 1.0.1, and 1.0.2, the property is:

appender.'errors.File'="stacktrace.log"

Turning off stacktrace.log

To turn off the log file completely, in 1.0.3 change the line:

StackTrace="error,stacktraceLog"

to:

StackTrace="error"

In previous versions, the original line is:

StackTrace="error,errors"

What we are doing is removing the only appender from the StackTrace logger (automatically created by Grails) so the messages will not be written anywhere (and the file will not be created). The word error that remains indicates the logging level. We could also change it to off to be clear (only doing that without removing the appender will still result in an attempt to create the file).

No stacktrace processing

According to the Grails docs, you can also turn this off by setting a command line property:

grails -Dgrails.full.stacktrace=true run-app

(yes, true) If you don't also turn off the logging property, the stacktrace.log file will still be created but won't be written to. Putting that line in Config.groovy doesn't do anything. It also doesn't help when creating a war for deployment. You'll have to put that property definition into your container startup (setting the environment variable CATALINA_OPTS or adding to your jsvc command line).

Console logging

The docs also suggest sending the stacktrace to the console (for Tomcat 5.x that's $CATALINA_HOME/logs/catalina.out), by changing the stacktraceLog or errors appender in the above line to stdout, like this:

StackTrace="error,stdout"

But error messages seem to go to the console anyways and so will be doubled.

Environments

You can make these changes for different environments. In Config.groovy add something like this:

environments {
    production { log4j.logger.StackTrace="error" }
}

which will turn off the stacktrace logging file for your production environment, but leave it on for development.

Upgrades

I didn't notice all this until just now with my new grails projects, because my original Grails projects were created before these shenanigans started happening. I've done grails upgrade several times since then, but stacktrace.log configuration isn't yet in the upgrade script.

Grails 1.1 update

Grails 1.1 introduces a new Log4J DSL that completely changes this. I could not immediately get the above methods working. There are several undocumented behaviors that both cause and cure this. Assigning a different appender to the StrackTrace logger sends the stacktraces to that appender but it does not prevent the creation of a FileAppender named stacktraceLog and thus the file stacktrace.log is still created.

Reviewing the code (Log4jConfig.groovy), I found that the stacktraceLog appender is not created if an appender named stacktrace already exists, i.e. it's created at the top of the log4j DSL closure. You can create a different file or rollingFile appender or whatever you like, just use name:'stacktrace'.

I like to turn off stacktrace logging completely. It's an error, so it's already going to the root logger anyways. Or I may want to send it to my appender named something else (like 'tomcatLog', which I use for other loggers and don't want to call it 'stacktrace'). Use the undocumented NullAppender which exists in Grails by default. Create an appender of type 'null' -- with quotes to avoid referencing the null object:

log4j = {
  appenders {
    'null' name:'stacktrace'
    ...
  }
...
}

Comments

Ben commented at 2009-10-29 10:31:31

Hi, Thanks for your tip (Grails 1.1 update) it worked for me!


How to Setup a VPS to Run Grails on Jetty - Anders Zakrisson commented at 2010-01-05 03:15:36

[...] Jetty on Port 80 Virtual hosts in Jetty Fixing the stacktrace.log Using GNU Screen MySQL Console [...]


ew commented at 2012-05-29 09:56:57

this doesnt work

log4j = {
    // Example of changing the log pattern for the default console
    // appender:
    //
    appenders {
    //  console name:'stdout', layout:pattern(conversionPattern: '%c{2} %m%n')
        appender.'stacktraceLog.File'="/tmp/logs/cms-stacktrace.log"
    }

15:48:31,044 ERROR [stderr] (MSC service thread 1-4) log4j:ERROR Property missing when configuring log4j: appender
15:48:31,060 ERROR [stderr] (MSC service thread 1-4) log4j:ERROR WARNING: Exception occured configuring log4j logging: Cannot set property 'stacktraceLog.File' on null object

jja commented at 2012-06-05 12:10:14

What version of Grails? that syntax came along in 1.1 and still works for me in 1.3.x

(comments are closed)