Chapter 8: Context Selector
![]() |
Copyright © 2000-2006, QOS.ch This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License . |
Introduction
When working with several Web applications, all running on one server, the
multiplications of LoggerContext
objects might reveal itself
a tricky issue.
Logback provides a simple yet powerful way of dealing with multiple contexts, without corruption of data, nor collusion between context instances.
One thing we know is that JNDI environments are independant. 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
LoggerContext
instance.
The component that manages the different contexts is a ContextSelector implementation. The JNDI-specific implementation is called ContextJNDISelector.
Each Web application provides two environment variables. One that specifies
the application's LoggerContext
name, and one that provides the
path to the xml file that will be used to configure the context.
The server side
Configuring Tomcat
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:
-Dlogback.ContextSelector=JNDI
Configuring Jetty
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 following form:
<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 from the ContextSelector
and shut it down.
The
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
Using the 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 LoggerContextFilter
component. This filter is a javax.servlet.Filter
implementation
that gets the environment-specific LoggerContext
and sets it
in a ThreadLocal
variable. Each time
the ContextSelector
will be called to provide the
Web application's own LoggerContext
, it 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 LoggerContext
.
The 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>
Some recommandations
To avoid confusion, it is prudent to name each Web application in the web.xml file, as in:
<display-name>Name_Of_My_WebApp</display-name>
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.