[logback-dev] svn commit: r2130 - in logback/trunk: logback-classic/src/main/java/ch/qos/logback/classic/joran logback-core/src/main/java/ch/qos/logback/core logback-core/src/main/java/ch/qos/logback/core/joran logback-core/src/main/java/ch/qos/logback/core/joran/action logback-core/src/main/java/ch/qos/logback/core/joran/spi logback-core/src/test/java/ch/qos/logback/core/joran/spi
noreply.ceki at qos.ch
noreply.ceki at qos.ch
Wed Jan 21 23:06:57 CET 2009
Author: ceki
Date: Wed Jan 21 23:06:57 2009
New Revision: 2130
Added:
logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistry.java
logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/HostClassAndPropertyDouble.java
logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistryTest.java
Modified:
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/JoranConfigurator.java
logback/trunk/logback-core/src/main/java/ch/qos/logback/core/ConsoleAppender.java
logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/GenericConfigurator.java
logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/action/NestedComplexPropertyIA.java
logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/InterpretationContext.java
logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/PropertySetter.java
logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PackageTest.java
logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PropertySetterTest.java
Log:
Fixing LBCLASSIC-103
Joran is now able to assume a default type for nested components according
to rules declared in a JoranConfigurator instance. The rules are located in
an instance of the DefaultNestedComponentRegistry class.
Thus, in many cases it will no longer be necessary to declare the class of a
component in configuration files.
Since the logback-classic and logback-access modes have their own JoranConfigurator
classes, it is now possible to register rules specific to each logback module.
For example, there is now a rule which maps the layout of an appender to a
c.q.l.classic.PatternLayout instance for all appenders in logback-classic and to
and instance of c.q.l.access.PatternLayout for all appenders in logback-access.
This is still ongoing and incomplete work.
Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/JoranConfigurator.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/JoranConfigurator.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/JoranConfigurator.java Wed Jan 21 23:06:57 2009
@@ -1,7 +1,7 @@
/**
- * LOGBack: the generic, reliable, fast and flexible logging framework.
+ * Logback: the generic, reliable, fast and flexible logging framework.
*
- * Copyright (C) 1999-2006, QOS.ch
+ * Copyright (C) 2000-2009, QOS.ch
*
* This library is free software, you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
@@ -10,27 +10,31 @@
package ch.qos.logback.classic.joran;
+import ch.qos.logback.classic.PatternLayout;
+import ch.qos.logback.classic.boolex.JaninoEventEvaluator;
import ch.qos.logback.classic.joran.action.ConfigurationAction;
import ch.qos.logback.classic.joran.action.ConsolePluginAction;
import ch.qos.logback.classic.joran.action.ContextNameAction;
import ch.qos.logback.classic.joran.action.EvaluatorAction;
import ch.qos.logback.classic.joran.action.InsertFromJNDIAction;
import ch.qos.logback.classic.joran.action.JMXConfiguratorAction;
-import ch.qos.logback.classic.joran.action.LayoutAction;
import ch.qos.logback.classic.joran.action.LevelAction;
import ch.qos.logback.classic.joran.action.LoggerAction;
import ch.qos.logback.classic.joran.action.RootLoggerAction;
import ch.qos.logback.classic.sift.SiftAction;
import ch.qos.logback.classic.spi.PlatformInfo;
+import ch.qos.logback.core.AppenderBase;
+import ch.qos.logback.core.filter.EvaluatorFilter;
import ch.qos.logback.core.joran.JoranConfiguratorBase;
import ch.qos.logback.core.joran.action.AppenderRefAction;
import ch.qos.logback.core.joran.action.IncludeAction;
import ch.qos.logback.core.joran.action.NOPAction;
+import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry;
import ch.qos.logback.core.joran.spi.Pattern;
import ch.qos.logback.core.joran.spi.RuleStore;
/**
- * A JoranConfigurator add few rules specific to the Classic module.
+ * This JoranConfiguratorclass adds rules specific to logback-classic.
*
* @author Ceki Gülcü
*/
@@ -46,14 +50,15 @@
rs.addRule(new Pattern("configuration"), new ConfigurationAction());
- rs.addRule(new Pattern("configuration/contextName"), new ContextNameAction());
- rs.addRule(new Pattern("configuration/insertFromJNDI"), new InsertFromJNDIAction());
+ rs.addRule(new Pattern("configuration/contextName"),
+ new ContextNameAction());
+ rs.addRule(new Pattern("configuration/insertFromJNDI"),
+ new InsertFromJNDIAction());
rs.addRule(new Pattern("configuration/evaluator"), new EvaluatorAction());
rs.addRule(new Pattern("configuration/appender/sift"), new SiftAction());
rs.addRule(new Pattern("configuration/appender/sift/*"), new NOPAction());
-
-
+
rs.addRule(new Pattern("configuration/logger"), new LoggerAction());
rs.addRule(new Pattern("configuration/logger/level"), new LevelAction());
@@ -63,19 +68,31 @@
new AppenderRefAction());
rs.addRule(new Pattern("configuration/root/appender-ref"),
new AppenderRefAction());
- rs
- .addRule(new Pattern("configuration/appender/layout"),
- new LayoutAction());
-
+
+ //rs
+ // .addRule(new Pattern("configuration/appender/layout"),
+ // new LayoutAction());
+
// add jmxConfigurator only if we have JMX available.
// If running under JDK 1.4 (retrotranslateed logback) then we
// might not have JMX.
- if(PlatformInfo.hasJMXObjectName()) {
- rs.addRule(new Pattern("configuration/jmxConfigurator"), new JMXConfiguratorAction());
+ if (PlatformInfo.hasJMXObjectName()) {
+ rs.addRule(new Pattern("configuration/jmxConfigurator"),
+ new JMXConfiguratorAction());
}
rs.addRule(new Pattern("configuration/include"), new IncludeAction());
-
- rs.addRule(new Pattern("configuration/consolePlugin"), new ConsolePluginAction());
+
+ rs.addRule(new Pattern("configuration/consolePlugin"),
+ new ConsolePluginAction());
+ }
+
+ @Override
+ protected void addDefaultNestedComponentRegistryRules(
+ DefaultNestedComponentRegistry registry) {
+ registry.add(AppenderBase.class, "layout", PatternLayout.class);
+ registry
+ .add(EvaluatorFilter.class, "evaluator", JaninoEventEvaluator.class);
+
}
}
Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/ConsoleAppender.java
==============================================================================
--- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/ConsoleAppender.java (original)
+++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/ConsoleAppender.java Wed Jan 21 23:06:57 2009
@@ -1,7 +1,7 @@
/**
* Logback: the generic, reliable, fast and flexible logging framework.
*
- * Copyright (C) 1999-2007, QOS.ch
+ * Copyright (C) 2000-2009, QOS.ch
*
* This library is free software, you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/GenericConfigurator.java
==============================================================================
--- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/GenericConfigurator.java (original)
+++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/GenericConfigurator.java Wed Jan 21 23:06:57 2009
@@ -20,6 +20,7 @@
import ch.qos.logback.core.joran.event.SaxEvent;
import ch.qos.logback.core.joran.event.SaxEventRecorder;
+import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry;
import ch.qos.logback.core.joran.spi.EventPlayer;
import ch.qos.logback.core.joran.spi.InterpretationContext;
import ch.qos.logback.core.joran.spi.Interpreter;
@@ -79,6 +80,10 @@
abstract protected void addImplicitRules(Interpreter interpreter);
+ protected void addDefaultNestedComponentRegistryRules(DefaultNestedComponentRegistry registry) {
+
+ }
+
protected Pattern initialPattern() {
return new Pattern();
}
@@ -90,6 +95,7 @@
InterpretationContext ec = interpreter.getInterpretationContext();
ec.setContext(context);
addImplicitRules(interpreter);
+ addDefaultNestedComponentRegistryRules(ec.getDefaultNestedComponentRegistry());
}
final public void doConfigure(final InputSource inputSource)
Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/action/NestedComplexPropertyIA.java
==============================================================================
--- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/action/NestedComplexPropertyIA.java (original)
+++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/action/NestedComplexPropertyIA.java Wed Jan 21 23:06:57 2009
@@ -91,23 +91,32 @@
// perform variable name substitution
className = ec.subst(className);
- // guess class name via implicit rules
- if (OptionHelper.isEmpty(className)) {
- PropertySetter parentBean = actionData.parentBean;
- className = parentBean.getClassNameViaImplicitRules(actionData
- .getComplexPropertyName(), actionData.getAggregationType());
- }
+ Class componentClass = null;
+ try {
- if (OptionHelper.isEmpty(className)) {
- actionData.inError = true;
- String errMsg = "No class name attribute in [" + localName + "]";
- addError(errMsg);
- return;
- }
+ if (!OptionHelper.isEmpty(className)) {
+ componentClass = Loader.loadClass(className, context);
+ } else {
+ // guess class name via implicit rules
+ PropertySetter parentBean = actionData.parentBean;
+ componentClass = parentBean.getClassNameViaImplicitRules(actionData
+ .getComplexPropertyName(), actionData.getAggregationType(), ec
+ .getDefaultNestedComponentRegistry());
+ }
- try {
- actionData.setNestedComplexProperty(Loader.loadClass(className, context)
- .newInstance());
+ if (componentClass == null) {
+ actionData.inError = true;
+ String errMsg = "Could not find an appropriate class for property ["
+ + localName + "]";
+ addError(errMsg);
+ return;
+ }
+
+ if(OptionHelper.isEmpty(className)) {
+ addInfo("Assuming default type ["+componentClass.getName()+"] for ["+localName+"] property");
+ }
+
+ actionData.setNestedComplexProperty(componentClass.newInstance());
// pass along the repository
if (actionData.getNestedComplexProperty() instanceof ContextAware) {
@@ -118,12 +127,14 @@
addInfo("Pushing component [" + localName
+ "] on top of the object stack.");
ec.pushObject(actionData.getNestedComplexProperty());
+
} catch (Exception oops) {
actionData.inError = true;
String msg = "Could not create component [" + localName + "] of type ["
+ className + "]";
addError(msg, oops);
}
+
}
public void end(InterpretationContext ec, String tagName) {
Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistry.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistry.java Wed Jan 21 23:06:57 2009
@@ -0,0 +1,49 @@
+/**
+ * Logback: the generic, reliable, fast and flexible logging framework.
+ *
+ * Copyright (C) 2000-2009, QOS.ch
+ *
+ * This library is free software, you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation.
+ */
+package ch.qos.logback.core.joran.spi;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A registry which maps a property in a host class to a default class.
+ *
+ * @author Cek Gülcü
+ *
+ */
+public class DefaultNestedComponentRegistry {
+
+ Map<HostClassAndPropertyDouble, Class> defaultComponentMap = new HashMap<HostClassAndPropertyDouble, Class>();
+
+ public void add(Class hostClass, String propertyName, Class componentClass) {
+ HostClassAndPropertyDouble hpDouble = new HostClassAndPropertyDouble(
+ hostClass, propertyName.toLowerCase());
+ defaultComponentMap.put(hpDouble, componentClass);
+ }
+
+ public Class findDefaultComponentType(Class hostClass, String propertyName) {
+ propertyName = propertyName.toLowerCase();
+ while (hostClass != null) {
+ Class componentClass = oneShotFind(hostClass, propertyName);
+ if (componentClass != null) {
+ return componentClass;
+ }
+ hostClass = hostClass.getSuperclass();
+ }
+ return null;
+ }
+
+ private Class oneShotFind(Class hostClass, String propertyName) {
+ HostClassAndPropertyDouble hpDouble = new HostClassAndPropertyDouble(
+ hostClass, propertyName);
+ return defaultComponentMap.get(hpDouble);
+ }
+
+}
Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/HostClassAndPropertyDouble.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/HostClassAndPropertyDouble.java Wed Jan 21 23:06:57 2009
@@ -0,0 +1,72 @@
+/**
+ * Logback: the generic, reliable, fast and flexible logging framework.
+ *
+ * Copyright (C) 2000-2009, QOS.ch
+ *
+ * This library is free software, you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation.
+ */
+package ch.qos.logback.core.joran.spi;
+
+/**
+ * A 2-tuple (a double) consisting of a Class and a String. The Class references
+ * the hosting class of a component and the String represents the property name
+ * under which a nested component is referenced the host.
+ *
+ * This class is used by {@link DefaultNestedComponentRegistry}.
+ *
+ * @author Ceki Gulcu
+ *
+ */
+public class HostClassAndPropertyDouble {
+
+ final Class hostClass;
+ final String propertyName;
+
+ public HostClassAndPropertyDouble(Class hostClass, String propertyName) {
+ this.hostClass = hostClass;
+ this.propertyName = propertyName;
+ }
+
+ public Class getHostClass() {
+ return hostClass;
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((hostClass == null) ? 0 : hostClass.hashCode());
+ result = prime * result
+ + ((propertyName == null) ? 0 : propertyName.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ final HostClassAndPropertyDouble other = (HostClassAndPropertyDouble) obj;
+ if (hostClass == null) {
+ if (other.hostClass != null)
+ return false;
+ } else if (!hostClass.equals(other.hostClass))
+ return false;
+ if (propertyName == null) {
+ if (other.propertyName != null)
+ return false;
+ } else if (!propertyName.equals(other.propertyName))
+ return false;
+ return true;
+ }
+
+}
Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/InterpretationContext.java
==============================================================================
--- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/InterpretationContext.java (original)
+++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/InterpretationContext.java Wed Jan 21 23:06:57 2009
@@ -43,7 +43,8 @@
Interpreter joranInterpreter;
final List<InPlayListener> listenerList = new ArrayList<InPlayListener>();
-
+ DefaultNestedComponentRegistry defaultNestedComponentRegistry = new DefaultNestedComponentRegistry();
+
public InterpretationContext(Context context, Interpreter joranInterpreter) {
this.context = context;
this.joranInterpreter = joranInterpreter;
@@ -52,6 +53,11 @@
propertiesMap = new HashMap<String, String>(5);
}
+
+ public DefaultNestedComponentRegistry getDefaultNestedComponentRegistry() {
+ return defaultNestedComponentRegistry;
+ }
+
void setPropertiesMap(Map<String, String> propertiesMap) {
this.propertiesMap = propertiesMap;
}
Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/PropertySetter.java
==============================================================================
--- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/PropertySetter.java (original)
+++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/PropertySetter.java Wed Jan 21 23:06:57 2009
@@ -559,19 +559,17 @@
}
}
- String getDefaultClassNameByAnnonation(String name, Method relevantMethod) {
+ Class getDefaultClassNameByAnnonation(String name, Method relevantMethod) {
DefaultClass defaultClassAnnon = getAnnotation(name, DefaultClass.class,
relevantMethod);
if (defaultClassAnnon != null) {
Class defaultClass = defaultClassAnnon.value();
- if (defaultClass != null) {
- return defaultClass.getName();
- }
+ return defaultClass;
}
return null;
}
- String getByConcreteType(String name, Method relevantMethod) {
+ Class getByConcreteType(String name, Method relevantMethod) {
Class<?> paramType = getParameterClassForMethod(relevantMethod);
if (paramType == null) {
@@ -580,22 +578,26 @@
boolean isUnequivocallyInstantiable = isUnequivocallyInstantiable(paramType);
if(isUnequivocallyInstantiable) {
- return paramType.getName();
+ return paramType;
} else {
return null;
}
}
- public String getClassNameViaImplicitRules(String name,
- AggregationType aggregationType) {
+ public Class getClassNameViaImplicitRules(String name,
+ AggregationType aggregationType, DefaultNestedComponentRegistry registry) {
+ Class registryResult = registry.findDefaultComponentType(obj.getClass(), name);
+ if(registryResult!= null) {
+ return registryResult;
+ }
// find the relevant method for the given property name and aggregationType
Method relevantMethod = getRelevantMethod(name, aggregationType);
if (relevantMethod == null) {
-
+ return null;
}
- String byAnnotation = getDefaultClassNameByAnnonation(name, relevantMethod);
+ Class byAnnotation = getDefaultClassNameByAnnonation(name, relevantMethod);
if (byAnnotation != null) {
return byAnnotation;
}
Added: logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistryTest.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistryTest.java Wed Jan 21 23:06:57 2009
@@ -0,0 +1,37 @@
+package ch.qos.logback.core.joran.spi;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DefaultNestedComponentRegistryTest {
+
+ DefaultNestedComponentRegistry registry = new DefaultNestedComponentRegistry();
+
+ @Before
+ public void setUp() throws Exception {
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void smoke() {
+ String propertyName = "window";
+ registry.add(House.class, propertyName, Window.class);
+ Class result = registry.findDefaultComponentType(House.class, propertyName);
+ assertEquals(Window.class, result);
+ }
+
+ @Test
+ public void absent() {
+ registry.add(House.class, "a", Window.class);
+ Class result = registry.findDefaultComponentType(House.class, "other");
+ assertNull(result);
+ }
+}
Modified: logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PackageTest.java
==============================================================================
--- logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PackageTest.java (original)
+++ logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PackageTest.java Wed Jan 21 23:06:57 2009
@@ -14,6 +14,7 @@
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
- at SuiteClasses({PatternTest.class, SimpleStoreTest.class, PropertySetterTest.class})
+ at SuiteClasses( { PatternTest.class, SimpleStoreTest.class,
+ PropertySetterTest.class, DefaultNestedComponentRegistryTest.class })
public class PackageTest {
}
Modified: logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PropertySetterTest.java
==============================================================================
--- logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PropertySetterTest.java (original)
+++ logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PropertySetterTest.java Wed Jan 21 23:06:57 2009
@@ -20,6 +20,8 @@
public class PropertySetterTest {
+ DefaultNestedComponentRegistry defaultComponentRegistry = new DefaultNestedComponentRegistry();
+
@Test
public void testCanAggregateComponent() {
House house = new House();
@@ -112,23 +114,21 @@
public void testgetClassNameViaImplicitRules() {
House house = new House();
PropertySetter setter = new PropertySetter(house);
- String className = setter.getClassNameViaImplicitRules("door",
- AggregationType.AS_COMPLEX_PROPERTY);
- assertEquals(Door.class.getName(), className);
+ Class compClass = setter.getClassNameViaImplicitRules("door",
+ AggregationType.AS_COMPLEX_PROPERTY, defaultComponentRegistry);
+ assertEquals(Door.class, compClass);
}
-
-
@Test
public void testgetComplexPropertyColleClassNameViaImplicitRules() {
House house = new House();
PropertySetter setter = new PropertySetter(house);
- String className = setter.getClassNameViaImplicitRules("window",
- AggregationType.AS_COMPLEX_PROPERTY_COLLECTION);
- assertEquals(Window.class.getName(), className);
+ Class compClass = setter.getClassNameViaImplicitRules("window",
+ AggregationType.AS_COMPLEX_PROPERTY_COLLECTION,
+ defaultComponentRegistry);
+ assertEquals(Window.class, compClass);
}
-
@Test
public void testPropertyCollection() {
House house = new House();
@@ -208,13 +208,14 @@
Method relevantMethod = setter.getRelevantMethod("SwimmingPool",
AggregationType.AS_COMPLEX_PROPERTY);
assertNotNull(relevantMethod);
- String spClassName = setter.getDefaultClassNameByAnnonation("SwimmingPool",
+ Class spClass = setter.getDefaultClassNameByAnnonation("SwimmingPool",
relevantMethod);
- assertEquals(SwimmingPoolImpl.class.getName(), spClassName);
+ assertEquals(SwimmingPoolImpl.class, spClass);
- String classNameViaImplicitRules = setter.getClassNameViaImplicitRules(
- "SwimmingPool", AggregationType.AS_COMPLEX_PROPERTY);
- assertEquals(SwimmingPoolImpl.class.getName(), classNameViaImplicitRules);
+ Class classViaImplicitRules = setter.getClassNameViaImplicitRules(
+ "SwimmingPool", AggregationType.AS_COMPLEX_PROPERTY,
+ defaultComponentRegistry);
+ assertEquals(SwimmingPoolImpl.class, classViaImplicitRules);
}
}
More information about the logback-dev
mailing list