Logback could be groovy! But XML FTW
Anyone who works with Tridion content delivery will be familiar with the fact that Logback is used as the logging framework. Recently I found myself looking into this more than I had previously, so here are a couple of observations that might be interesting. The first is that you can use the groovy scripting language instead of XML to write your configuration files. (I'll get to exactly how useful, or otherwise, this might be in a bit...) Anyway - the following is a machine translation of the logback.xml file that ships with Tridion, Now - proponents of the groovy approach will tell us that groovy can be much terser than the XML equivalent. At first sight it doesn't look much different, but I imagine you could factor out the creation of all those appenders to some sort of factory, and then it would look a lot shorter. Can I leave that as "an exercise for the student"? :-)
import ch.qos.logback.classic.encoder.PatternLayoutEncoder import ch.qos.logback.core.rolling.RollingFileAppender import ch.qos.logback.core.rolling.TimeBasedRollingPolicy import java.nio.charset.Charset import static ch.qos.logback.classic.Level.${LOG.LEVEL} import static ch.qos.logback.classic.Level.OFF scan() def log.pattern = "%date %-5level %logger{0} - %message%n" def log.history = "7" def log.folder = "c:/tridion/log" def log.level = "ERROR" def log.encoding = "UTF-8" appender("rollingTransportLog", RollingFileAppender) { rollingPolicy(TimeBasedRollingPolicy) { fileNamePattern = "${log.folder}/cd_transport.%d{yyyy-MM-dd}.log" maxHistory = "${log.history}" } encoder(PatternLayoutEncoder) { charset = Charset.forName("${log.encoding}") pattern = "${log.pattern}" } prudent = true } appender("rollingDeployerLog", RollingFileAppender) { rollingPolicy(TimeBasedRollingPolicy) { fileNamePattern = "${log.folder}/cd_deployer.%d{yyyy-MM-dd}.log" maxHistory = "${log.history}" } encoder(PatternLayoutEncoder) { charset = Charset.forName("${log.encoding}") pattern = "${log.pattern}" } prudent = true } appender("rollingMonitorLog", RollingFileAppender) { rollingPolicy(TimeBasedRollingPolicy) { fileNamePattern = "${log.folder}/cd_monitor.%d{yyyy-MM-dd}.log" maxHistory = "${log.history}" } encoder(PatternLayoutEncoder) { charset = Charset.forName("${log.encoding}") pattern = "${log.pattern}" } prudent = true } appender("rollingCoreLog", RollingFileAppender) { rollingPolicy(TimeBasedRollingPolicy) { fileNamePattern = "${log.folder}/cd_core.%d{yyyy-MM-dd}.log" maxHistory = "${log.history}" } encoder(PatternLayoutEncoder) { charset = Charset.forName("${log.encoding}") pattern = "${log.pattern}" } prudent = true } appender("rollingSessionPreviewLog", RollingFileAppender) { rollingPolicy(TimeBasedRollingPolicy) { fileNamePattern = "${log.folder}/cd_preview.%d{yyyy-MM-dd}.log" maxHistory = "${log.history}" } encoder(PatternLayoutEncoder) { charset = Charset.forName("${log.encoding}") pattern = "${log.pattern}" } prudent = true } logger("com.tridion", ${LOG.LEVEL}) logger("com.tridion.transport", ["rollingTransportLog"]) logger("com.tridion.transport.HTTPSReceiverServlet", ["rollingDeployerLog"]) logger("com.tridion.transport.transportpackage", ["rollingDeployerLog"]) logger("com.tridion.transformer", ["rollingDeployerLog"]) logger("com.tridion.deployer", ["rollingDeployerLog"]) logger("com.tridion.tcdl", ["rollingDeployerLog"]) logger("com.tridion.event", ["rollingDeployerLog"]) logger("com.tridion.monitor", ["rollingMonitorLog"]) logger("Tridion.ContentDelivery", ${LOG.LEVEL}, ["rollingCoreLog"]) logger("com.tridion.preview", ["rollingSessionPreviewLog"]) logger("com.tridion.storage.persistence.session", ["rollingSessionPreviewLog"]) root(OFF, ["rollingCoreLog"])
So why might this be interesting to Tridion infrastructure specialists? Well it isn't. Not at all. At least not right now - because doing it this way requires the groovy runtime to be available, and that isn't in a standard Tridion content delivery setup. I attempted a trivial hack by dropping a couple of the groovy jars in place, but no joy. Realistically, this would only be a practical approach if Tridion decided to build it into the product and support it. I imagine the dev team puts quite some effort into keeping their dependency tree as clean as possible, so this might come under the heading of stuff that would only get added if people really, really wanted it!
Anyway - I love the smell of XML in the morning, so it's all the same to me. So on with the useful part of this post. If you check out exactly how logback gets its configuration settings, you'll see that before it picks up logback.xml, it first looks for a file called logback-test.xml. I'm happy to say that this does work out-of-the-box. This means that when you come across a server where you need to debug a problem, and its standard logging settings need to be boosted up to DEBUG, you don't have to edit the existing config file. Just drop your insanely debuggy logback-test.xml file in next to logback.xml (and restart things) and Bob's your uncle. When you're done, just delete (and restart). Even the restarting might be optional - another feature of logback is that you can configure it to scan for configuration changes, although I have no clue whether it would then pick up the existence of logback-test.xml)
Ok - this is such a minor benefit over copying and renaming that it hardly justifies the deaths of all those IP packets that were bravely lost in transmission during the serving of this web page. Whatever.... that's the thing with research, eh? Negative results are also important to report. In short - logback.groovy looked cool, but won't work - and maybe carrying a customised logback-test.xml around in your toolkit might be handy, but then again, maybe not.
I'll sign off with one more public service announcement. I recently saw someone using a logback configuration that specified a logging level of ON. Apparently they had been advised to do so by someone who ought to have checked first. The possible values are OFF, ERROR, WARN, INFO, DEBUG and TRACE. Anything other than that will not be recognised, and you'll get DEBUG logging, which is the default if that happens.