A project of mine is packed as an EAR file which contains SLF4J API (1.7.5) as well as the logback libraries as its implementation (logback-core 1.0.13
and logback-classic 1.0.13
).
When I (used to) deploy my project SLF4J's LoggerFactory finds logback as possible binding and the correct logger (i.e. logback) is used.
Now I have a resource connector (activemq-rar-5.8.0.rar
) which is deployed before my own EAR file (as the EAR files requires the RAR). Unfortunately this RAR contains its own SLF4J implementation (slf4j-api-1.6.6.jar
slf4j-log4j12-1.6.6.jar
log4j-1.2.17.jar
).
The RAR files uses the log4j implementation.
When I deploy my EAR file the LoggerFactory inside my application's code suddenly uses the log4j implementation (org.slf4j.impl.Log4jLoggerAdapter
) - even though I expected the classpath to be separated from the RAR.
This doesn't seem to be the case - so what am I doing wrong (the RAR should use log4j, my EAR should use logback)?
Update 1: It doesn't look as if I am alone, but unfortunately an answer is missing..
Update 2:
According to this table, GlassFish loads the connector module before the EAR/WAR libs (which are the last libs to be loaded).
Update 3:
I managed to fix the "binding": If I put the slf4j-api-1.7.5.jar
and the logback implementation (logback-core-1.0.13.jar
and logback-classic-1.0.13.jar
) inside the domains/<myDomain>/lib
folder in GlassFish, logback will be used as logging implementation (see Update 2 - "Common Classloader" comes before "Connector Classloader").
Unfortunately my configuration files aren't found anymore as they are inside the WAR/EAR - which will be loaded later by a different classloader ("Archive Classloader").
So this is not really a solution for me as I would like to keep the logback configuration files inside the EAR/WAR (as every application uses a different configuration).
Kind regards
stupidSheep
I finally figured out an acceptable solution.
GlassFish loads the Connector Module before the EAR/WAR, see "Update 2". By providing the SLF4J implementation BEFORE the connector module is being loaded, my provided SLF4J implementation is used.
To do this I've copied the following JARS to the directory domains/<myDomain>/lib
(see "Update 3").
logback-core-1.0.13.jar
logback-classic-1.0.13.jar
slf4j-api-1.7.5.jar
Unfortunately logback didn't find its own configuration file (logback.xml
) anymore, which must be on the classpath (which it is, as I packed it inside a JAR).
The solution is to manually configure logback. I've done this using the following CDI producer:
package com.example;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
@ApplicationScoped
public class LoggerProducer {
@PostConstruct
public void initialize() {
// The following is logback specific. Unfortunately logback doesn't find its XML configuration
// as the logback implementation gets loaded by a different ClassLoader than this code.
// See http://docs.oracle.com/cd/E19226-01/820-7695/6niugesfp/index.html#indexterm-28
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator joranConfigurator = new JoranConfigurator();
joranConfigurator.setContext(lc);
lc.reset();
try {
// The logback configuration is now being loaded from the classpath (by the "Archive Classloader")
joranConfigurator.doConfigure(this.getClass().getClassLoader().getResource("logback.xml"));
} catch (JoranException e) {
e.printStackTrace();
}
}
@Produces
@ApplicationLogger
Logger createLogger(InjectionPoint injectionPoint) {
return LoggerFactory.getLogger(injectionPoint.getMember().getDeclaringClass());
}
}
This will configure logback. Please note that I've used logback specific code to do that, so if you change the SLF4J implementation you must change the LoggerProducer
as well.
I think logback doesn't find its configuration file as the "Common Classloader" doesn't have the EAR/WAR on its classpath. But later, when the application is loaded, the "Archive Classloader" has the logback.xml
on its classpath (as it's provided by the EAR/WAR file), so it's possible to configure logback once everything is in place.
Regards stupidSheep
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With