I soon wanted to do 3 things with logging in my Griffon app:
- Inject a logger (e.g. log4j) into all my MVC components.
- Filter out the cruft from Groovy's massive stacktraces.
- Log unhandled exceptions in the same way as everything else.
Here's how ...
* Injecting log4j loggers in MVC components
I want to be able to write a log entry from anywhere in my MVC code, without needing to explicitly define the logger in each component. Griffon makes this easy ...
1. Grab a copy of log4j-1.x.xx.jar (there's one in GRIFFON_HOME/lib), and copy it into [projectname]/lib
2. Initialise logging at application startup:
Edit the file [projectname]/griffon-app/lifecycle/Initialize.groovy
To simply send logs to the console, just use BasicConfigurator. Add this line:
org.apache.log4j.BasicConfigurator.configure()
... Normally, you would use a PropertyConfigurator, or maybe even call System.setProperty to tell log4j about your log4j configuration file.
3. Inject a logger into each MVC component.
* Create or edit the file [projectname]/griffon-app/conf/Events.groovy
* Add the following (I added 'mvc.' to the logger name because my MVC components are all in the 'default' package):
import org.apache.log4j.Logger
onNewInstance = { klass, type, instance ->
instance.metaClass.logger = Logger.getLogger("mvc.${klass.name}")
}
Now, you can call logger.debug("variable x is ${x}") from anywhere in your Model, View and Controller classes.
Sweet.
* Filtering Groovy Stacktraces
Groovy generates extremely long, unwieldy stacktraces.
Groovy itself provides support for filtering a lot of the 'noise', in org.codehaus.groovy.runtime.StackTraceUtils, so let's filter and forget.
Just wrap this call around any Throwable, whenever you log it:
StackTraceUtils.deepSanitize(t)
e.g.
logger.error("Uncaught exception: ${e.message}", StackTraceUtils.deepSanitize(e))
... You could take this further and override 'logger.warn()' and 'logger.error()'
* Logging unhandled exceptions
I want to log unhandled exceptions in the same way I log everything else.
Simply configure an UncaughtExceptionHandler.
Again, I configure this in Initialize.groovy
1. Create an exception handler, something like this (I've put in the deepSanitize)
import org.apache.log4j.Logger
import org.codehaus.groovy.runtime.StackTraceUtils
import java.lang.Thread.UncaughtExceptionHandler
class LoggingExceptionHandler implements UncaughtExceptionHandler {
private static Logger logger = Logger.getLogger(LoggingExceptionHandler.class)
public void uncaughtException(Thread t, Throwable e)
{
logger.error("Uncaught exception ${e.message}", StackTraceUtils.deepSanitize(e) )
}
}
2. Configure the app to use the ExceptionHandler
Edit the file [projectname]/griffon-app/lifecycle/Initialize.groovy
Add the following lines, *after* the log4j configuration lines:
Thread.setDefaultUncaughtExceptionHandler(new LoggingExceptionHandler());
System.setProperty("sun.awt.exception.handler",LoggingExceptionHandler.class.getName())
Note that this replaces the functionality of griffon.util.GriffonExceptionHandler. If you prefer, you could extend this instead
HTH.
Amir
2 comments:
Hi,
after following your steps, logger.error will send messages to the console but logger.debug doesn't.
Has this to do with the basic log4j configuration in Config.groovy?
cheers,
Fabian
Thanks for the tip on making a logger available 'automagically'. I used the slf4j LoggerFactory so I don't have to import the log4j dependency, but the basic idea is great!
Post a Comment