Chapter 9: Context Selectors
It is not knowledge, but the act of learning, not possession but the act of getting there, which grants the greatest enjoyment. When I have clarified and exhausted a subject, then I turn away from it, in order to go into darkness again; the never-satisfied man is so strange if he has completed a structure, then it is not in order to dwell in it peacefully, but in order to begin another. I imagine the world conqueror must feel thus, who, after one kingdom is scarcely conquered, stretches out his arms for others.
—KARL FRIEDRICH GAUSS, Letter to Bolyai, 1808.
Style, like sheer silk, too often hides eczema.
—ALBERT CAMUS, The Fall
The problem: Logging Separation
The chapter deals with a relatively difficult problem of providing a separate logging environment for multiple applications running on the same web or EJB container. In the remainder of this chapter the term "application" will be used to refer either a web-application or a J2EE application interchangeably. In a separated logging environment, each application can configure logback in different ways such that the settings of one application does not interfere with the settings of another. A variant of this problem is the separation of application logging and the logging of the container itself.
A simple and easy approach
Assuming you container supports child first class loading, separation of logging can be accomplished by embedding a copy of slf4j and logback jar files in each application. For web-applications, placing slf4j and logback jar files under the WEB-INF/lib directory of the web-application is sufficient to endow each web-application with a separate logging environment.
Logback provides a mechanism for dealing with multiple contexts, without corruption of data, nor collision between logger context instances.
One thing we know is that JNDI environments are
independent. Thus, setting environment variables in each
application will allow a given component to know which application
it is dealing with at the moment. This is basically the mechanism
that uses logback to provide easy access to the right
Each Web application provides two environment variables. One
that specifies the application's
and one that provides the path to the xml file that will be used
to configure the context.
The server side
First, place the logback jars (that is logback-classic-VERSION.jar, logback-core-VERSION.jar and slf4j-api-VERSION.jar) in the server's shared class directory. In Tomcat, this directory is TOMCAT_HOME/common/lib/.
The next step is to let logback know that it will have to use JNDI to manage the context instances. This is done thanks to a System Property. When launching Tomcat, make sure that the logback.ContextSelector property is set with the JNDI value. This can be done by editing the TOMCAT_HOME/bin/catalina.sh or TOMCAT_HOME/bin/catalina.bat file, and adding the following line to the java options:
Configuring Jetty requires first to enable the use of JNDI. This is not a big deal, since the Jetty distribution provides the configuration files needed to achieve this task. The only thing to do is launch Jetty with the following command:
java -jar start.jar etc/jetty.xml etc/jetty-plus.xml
Note that you will need to install your appplications in the JETTY_HOME/webapps-plus directory.
In Jetty, the server shared class directory is JETTY_HOME/lib/. This is where you will need to place the logback jars (that is logback-classic-VERSION.jar, logback-core-VERSION.jar and slf4j-api-VERSION.jar).
The next step is to let logback know that it will have to use JNDI to manage the context instances. This is done thanks to a System Property. In Jetty, adding an environment variable is done by adding the following xml element in the JETTY_HOME/etc/jetty.xml configuration file, nested in a Configuration element:
<Call class="java.lang.System" name="setProperty"> <Arg>logback.ContextSelector</Arg> <Arg>JNDI</Arg> </Call>
Be aware that adding a -Dlogback.ContextSelector=JNDI
to the java command when starting the server will not work. By
doing this, the
LoggerFactory instanciated by the
server for its internal logging will try to use JNDI, when only the
Web applications should attempt to retrieve their
LoggerContext this way.
Configuring each Web application
While each Web application will need the logback jars to compile, they need not nor should be placed within the Web application's WAR file, except if you are using Jetty.
This is due to Jetty's internal Classloading mechanism. Consequently, the logback-classic-VERSION.jar and slf4j-api-VERSION.jar files should also be placed in the WEB-INF/lib/ directory of your webapps when running Jetty.
In each Web application's web.xml file, two JNDI
environment entries are needed. The first one specifies the desired
name of the application's
LoggerContext. It takes the
<env-entry> <description>JNDI logging context for this app</description> <env-entry-name>logback/context-name</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>ContextApp-A</env-entry-value> </env-entry>
The second JNDI entry will lead logback to the application's own xml configuration file. It can be declared as shown below:
<env-entry> <description>URL for configuring logback context</description> <env-entry-name>logback/configuration-resource</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>logback-app-A.xml</env-entry-value> </env-entry>
Specifying only the name of the file will lead logback to search for it in the Web application's WEB-INF/classes/ directory.
When the Web application is recycled or shutdown, it is very
often useful to recycle the associated
LoggerContext. This can be done by installing a
ServletContextListener which will detach the context
ContextSelector and shut it down.
ContextDetachingSCL class which ships with logback
does exactly that. To use it, add the following lines to your Web
application's web.xml file.
<listener> <listener-class>ch.qos.logback.classic.selector.servlet.ContextDetachingSCL</listener-class> </listener>
ContextJNDISelector might slow down your
application, because of the JNDI call that is issued each time a
LoggerContext is required. To prevent the cost of this
call, logback ships with a
component. This filter is a
implementation that gets the environment-specific
LoggerContext and sets it in a
variable. Each time the
ContextSelector will be called
to provide the Web application's own
will first check if the
ThreadLocal variable is set. If
it is, then the call to the JNDI environment will not be issued. The
LoggerContextFilter class increases the performances by
a wide margin.
Like all servlet filters, the
LoggerContextFilter can act before and after the
Web application's process. This allows the filter to set the
ThreadLocal variable at the beginning of the process
and to remove it once the Web application has finished processing
the request. This behaviour permits the thread to be recycled for
use by another Web application and still provide the correct
LoggerContextFilter can be used by adding the
following lines to your Web application's web.xml file.
<filter> <filter-name>LoggerContextFilter</filter-name> <filter-class>ch.qos.logback.classic.selector.servlet.LoggerContextFilter</filter-class> </filter> <filter-mapping> <filter-name>LoggerContextFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
To avoid confusion, it is prudent to name each Web application in the web.xml file, as in:
We recommend that you name logback configuration resources uniquely. In particualar, avoid naming the logback configuration resource as logback.xml for a non-default logger context.
While trying to configure the Web application logback would search for the resource logback.xml using the thread context classloader. Thus, it would first attempt to locate logback.xml file using the classloader specific to the Web application. However, if the file logback.xml did not exist there (if you forgot to put a custom one in WEB-INF/classes), and if the file logback.xml existed higher up in the classloader tree, we could end up in a situation where the logger context for your Web application would be configured using the same file as that used to configure the default context. Such involuntary sharing of the same configuration by multiple repositories will result in corrupt log output.