## 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

### Introduction

When working with multiple Web applications, all running on the same server, the multiplication of `LoggerContext` objects can be tricky.

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 `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.