
Hi all I've read the documentation on Logback's Groovy Configuration API and found that API is not concise and convenient as it can be. I would like to explain my thoughts. 1) There is no need to use string literals to identify appenders. Configuring an appender in a closure mainly takes several lines of code, a better idea is to use groovy map literal to specify appender's parameters. For example: appender myAppender, [class: FileAppender, file: "myComponent.log", append: true, encoder: encoder(class: PatternEncoder, pattern: "%m%n" ] appender myConsole // configuring an appender with default settings such as ConsoleAppender etc // or excluding square brackets, but name of the appender is specified right in the parameter's map appender name: myAppender, class: FileAppender, file: "myComponent.log", append: true, encoder: patternEncoder("%m%n") // here convenient method patternEncoder is used to avoid specifying the encoder's class 2) Loggers can refer appenders by name, no need to use strings. There is no need to use list literal to specify a collection of appenders, just using varargs. logger com.myorg.myapp.mypackage, INFO, myAppender logger com.myorg.myapp.mypackage.subpackage, DEBUG, false, myAppender, myConsole In the last statement also additivity flag is specified. logger root, INFO, myApp // configuring the root logger is achieved with the same API In my opinion this DSL is looking more naturally and clearer. All statements take one line. If convenient functions for creating encoders and other components is provided it would be easy-to-use configuration API. In the rest of this letter I've added sample code to show how it can be implemented. Hope this is useful. Best regards, Dmitry Here is logging configuration in method config. package org.logger import org.junit.Test import org.apache.log4j.* import static org.apache.log4j.Level.* /** * Configuration API * @author Dmitry Dobrynin * Created 28.06.2010 16:35:45 */ class GrTest { Map<String, Logger> loggers = [:] Map<String, Appender> appenders = [:] @Before void config() { appender myApp, [class: FileAppender, file: "myLog.log", encoder: encoder()] appender oneMoreAppender logger root, INFO, myApp logger org.nomin, DEBUG, false, myApp, oneMoreAppender } @Test void test() { getLogger(org.logger).info "Loggers work! " getLogger(GrTest).info "Found logger by class!" getLogger(List).info "Found logger by class!" } def propertyMissing(String name) { appenders[name] ?: loggers[name] ?: new Name(name: name, config: this) } def encoder(params = null) {} Logger getLogger(loggerName) { switch (loggerName) { case String: return loggers[loggerName] case Name: return loggers[loggerName.toString()] default: throw new Exception("Undefined logger ${loggerName}!" } } Logger getLogger(Class clazz) { loggers.find { clazz.name.startsWith(it.key) }?.value ?: loggers["root"] } Logger logger(Name name, Level level, boolean additivity = true, Appender... appenders) { loggers[name.toString()] = new Logger(additivity: additivity, appenders: appenders.collect { it }) } Appender appender(Name name, Map params = [:]) { appenders[name.toString()] = new Appender(name: name.toString(), appenderClass: params.class ?: ConsoleAppender) } static class Logger { def name def additivity = true def appenders = [] void info(message) { appenders.each { it.append("${name}: ${message}" }} } static class Appender { String name Class appenderClass void append(message) { println("${name}:${appenderClass?.name}: ${message}" } } } class Name { String name Name prev def config def propertyMissing(String name) { new Name(name: name, prev: this) } String toString() { prev ? "${prev.toString()}.${name}" : name } }