
manual.contextSelector.html Maven / Gradle / Ivy
Chapter 8: Context Selector
Chapter 8: 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
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
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
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.