The last couple of days I've been learning about SLF4J and Logback. IMHO this is a great piece of software.
One of my needs was to configure logging to a JNDI shared database inside tomcat 5.5. I followed the directions outlined in http://logback.qos.ch/manual/appenders.html for configuring the DBAppender with a JNDIConnectionSource. The example code in the manual is like this: <connectionSource class="ch.qos.logback.core.db.JNDIConnectionSource"> <param name="jndiLocation" value="jdbc/MySQLDS" /> <param name="username" value="myUser" /> <param name="password" value="myPassword" /> </connectionSource> (BTW, the "username" param in the documentation is wrong, it must be "user"). I will outline the configuration I needed for tomcat5.5 in case you want to add a samples section and as documentation in the list archives. After some headaches the configuration I got working was this simple one: In logback.xml: ... <connectionSource class="ch.qos.logback.core.db.JNDIConnectionSource"> <param name="jndiLocation" value="java:comp/env/jdbc/myDataSource"/> </connectionSource> ... In context.xml: ... <Resource name="jdbc/myDataSource" auth="Container" type="javax.sql.DataSource" driverClassName="org.postgresql.Driver" url="jdbc:postgresql://localhost/mydb" username="myuser" password="mypass" maxActive="150" maxIdle="50" maxWait="10000" removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true" validationQuery="SELECT COUNT(*) FROM PingTable"/> ... In web.xml: ... <resource-ref> <description>sample datasource</description> <res-ref-name>jdbc/myDataSource</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> ... (In my experience the <resource-ref> definition is **not** required but it is referenced as mandatory in the tomcat docs http://tomcat.apache.org/tomcat-5.5-doc/jndi-resources-howto.html & http://tomcat.apache.org/tomcat-5.5-doc/jndi-datasource-examples-howto.html) Using this configuration and only including the slf4j-api-1.5.2.jar, logback-classic-0.9.9.jar and logback-core-0.9.9.jar jars in WEB-INF/lib (removed commons-logging.jar also) I got the DB logging working. Comments: using tomcat5.5, username/password can't be specified when trying to use a JNDI-shared DataSource. The following configuration: <connectionSource class="ch.qos.logback.core.db.JNDIConnectionSource"> <param name="jndiLocation" value="java:comp/env/jdbc/myDataSource"/> <param name="user" value="myuser"/> <param name="password" value="mypass"/> </connectionSource> did not work and I got a "java.lang.UnsupportedOperationException: Not supported by BasicDataSource" exception. The reason is outlined in http://www.bedework.org/pipermail/bedework-users/2007-December/000890.html, but long story short, when using pooled data sources the user and password version of getConnection method can't be used. I don't know if this is tomcat-specific behaviour. Related lines in ch.qos.logback.core.db.JNDIConnectionSource: ... public Connection getConnection() throws SQLException { Connection conn = null; try { if (dataSource == null) { dataSource = lookupDataSource(); } if (getUser() == null) { conn = dataSource.getConnection(); } else { conn = dataSource.getConnection(getUser(), getPassword()); } } catch (final NamingException ne) { addError("Error while getting data source", ne); throw new SQLException("NamingException while looking up DataSource: " + ne.getMessage()); } catch (final ClassCastException cce) { addError("ClassCastException while looking up DataSource.", cce); throw new SQLException("ClassCastException while looking up DataSource: " + cce.getMessage()); } return conn; } ... The "if (getUser() == null)" test is the source of this problem. Maybe some sort of "if jndi then don't use user/pass" testing could be done for better error reporting (at least better than "not supported method exception"), but I'm not an expert on logback architecture so I don't know what implications this could have. Added to that, the JNDI name used in the "jndiLocation" must be prepended by "java:comp/env", as this is the standard context for java objects and the JNDIConnectionSource does not prepend the searched values with it. From ch.qos.logback.core.db.JNDIConnectionSource: ... private DataSource lookupDataSource() throws NamingException, SQLException { DataSource ds; Context ctx = new InitialContext(); Object obj = ctx.lookup(jndiLocation); // PortableRemoteObject was introduced in JDK 1.3. We won't use it. // ds = (DataSource)PortableRemoteObject.narrow(obj, DataSource.class); ds = (DataSource) obj; if (ds == null) { throw new SQLException("Failed to obtain data source from JNDI location " + jndiLocation); } else { return ds; } } ... Another thing I got problems with was slf4j's jcl-over-slf4j-1.5.2.jar. At first I included this jar as I thought it would be harmless to still get JCL compatibility in my app. But including this jar made my application miserably fail. The exception I got was that the JNDI context for my data source did not exist, which was weird since I got it working before including jcl-over-slf4j-1.5.2.jar. After checking stack traces, I concluded that this is due to logback being initialized too early when using jcl-over-slf4j, before tomcat makes the JNDI resources available to the web application. Check this stack trace: 12:44:45,287 |-ERROR in [EMAIL PROTECTED] - Error while getting data source javax.naming.NameNotFoundException: javax.naming.NameNotFoundException: 'comp' can't be found in this context at org.apache.naming.NamingContext.lookup(NamingContext.java:770) at org.apache.naming.NamingContext.lookup(NamingContext.java:153) at org.apache.naming.SelectorContext.lookup(SelectorContext.java:137) at javax.naming.InitialContext.lookup(InitialContext.java:351) at ch.qos.logback.core.db.JNDIConnectionSource.lookupDataSource(JNDIConnectionSource.java:104) at ch.qos.logback.core.db.JNDIConnectionSource.getConnection(JNDIConnectionSource.java:62) at ch.qos.logback.core.db.ConnectionSourceBase.discoverConnnectionProperties(ConnectionSourceBase.java:42) at ch.qos.logback.core.db.JNDIConnectionSource.start(JNDIConnectionSource.java:50) at ch.qos.logback.core.joran.action.NestedComponentIA.end(NestedComponentIA.java:142) at ch.qos.logback.core.joran.spi.Interpreter.callEndAction(Interpreter.java:310) at ch.qos.logback.core.joran.spi.Interpreter.endElement(Interpreter.java:173) at ch.qos.logback.core.joran.spi.Interpreter.endElement(Interpreter.java:154) at ch.qos.logback.core.joran.spi.EventPlayer.play(EventPlayer.java:48) at ch.qos.logback.core.joran.spi.Interpreter.play(Interpreter.java:336) at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:96) at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:74) at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:38) at ch.qos.logback.classic.util.ContextInitializer.configureByResource(ContextInitializer.java:23) at ch.qos.logback.classic.util.ContextInitializer.autoConfig(ContextInitializer.java:34) at ch.qos.logback.classic.util.ContextInitializer.autoConfig(ContextInitializer.java:43) at org.slf4j.impl.StaticLoggerBinder.initialize(StaticLoggerBinder.java:62) at org.slf4j.impl.StaticLoggerBinder.<init>(StaticLoggerBinder.java:53) at org.slf4j.impl.StaticLoggerBinder.<clinit>(StaticLoggerBinder.java:48) at org.slf4j.LoggerFactory.<clinit>(LoggerFactory.java:60) at org.apache.commons.logging.impl.SLF4JLogFactory.getInstance(SLF4JLogFactory.java:155) at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:704) at org.apache.catalina.core.ContainerBase.getLogger(ContainerBase.java:381) at org.apache.catalina.core.StandardContext.start(StandardContext.java:4119) at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:760) at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:740) at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:544) at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:626) at org.apache.catalina.startup.HostConfig.deployDescriptors(HostConfig.java:553) at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:488) at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1149) As you can see, tomcat references a JCL logger in "org.apache.catalina.core.ContainerBase.getLogger" just when first loading the webapp. This triggers logback initialization, and when the JNDIConnectionSource tries to get a reference to the DataSource, it finds nothing. I found a reference to a similar problem with JNDI contexts in the old bugzilla tracker (http://marc.info/?l=logback-dev&m=119005116732589&w=2) but although I tried locating this bug report in the new JIRA tracker I could not find it. Maybe it got lost during the migration? At the moment I'm still trying to fully understand the codebase. Do you figure out any solution to the problem? I'm sorry for such a lengthy message. Hope this info helps the project. Regards and keep up the good work. Diego. _______________________________________________ Logback-user mailing list [email protected] http://qos.ch/mailman/listinfo/logback-user
