Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Jakarta-commons Wiki" 
for change notification.

The following page has been changed by SimonKitching:
http://wiki.apache.org/jakarta-commons/Logging/StaticLog

The comment on the change is:
New page about static Log issue

New page:
== When Static References to Log objects can be used ==

There are two very common patterns used with logging:
{{{
public class Foo {
  private Log log = LogFactory.getLog(Foo.class);
  ....
}
}}}
and 
{{{
public class Foo {
  private static Log log = LogFactory.getLog(Foo.class);
  ....
}
}}}

The use of the static qualifier can be beneficial in some circumstances. 
However in others it is a very bad idea indeed,
and can have unexpected consequences. This page describes when each solution is 
appropriate.

== The Static Problem ==

The technical result of using static is obvious: there is only one Log 
reference shared across all instances of the class.
This is clearly memory efficient; only one reference(4 or 8 bytes) is needed no 
matter how many instances are created. It is also
CPU-efficient; the lookup required to find the Log instance is only done once, 
when the class is first referenced.

When writing stand-alone application code the use of static is a good idea.

However when the java code concerned is a '''library''' that may be deployed 
within a '''container''' of some sort (such as a J2EE server)
then problems can occur. It is common for containers to create a hierarchy of 
java !ClassLoader objects such that each "application"
deployed within the container has its own !ClassLoader but where there is some 
'''shared classloader''' that is in the ancestry of all
the deployed "applications". In this case, when a class holding a static 
reference to a Log instance is deployed at the
"application" level (eg at the "webapp" level in a servlet or j2ee container) 
there is also no problem; if multiple applications
deploy the class in question then they each have their own copy of the class 
and there are no interactions with other applications. 

However consider the case when a class using "private static Log log = ..." is 
deployed via a !ClassLoader that is in the ancestry of ''multiple''
supposedly independent "applications". In this case, the log member is 
initialised only once, because there is only one copy of the class. That 
initialisation (typically) occurs the first time any code tries to instantiate 
that class or call a static method on it. When initialisation of
the class occurs, what should the log member be set to? The options are:
 * To reference an underlying Log object which is part of a hierarchy 
configured from information at the "container" level, ie not associated with 
any particular "application"
 * To reference an underlying Log object which is part of a hierarchy 
configured from information somehow related to the current application 
 * To reference a "proxy" object which will determine what the "current 
application" is each time a log method is invoked, and delegate to the 
appropriate underlying Log object at that time.

The first option means that logging cannot be configured at a per-application 
level. Whatever logging configuration is set up
will apply equally to every application within the container, and all the 
output from those applications will be mixed together.
This is a major issue.

The second option means that the Log object will be configured according to the 
''first'' application that called it. Other
applications within the container will have their output sent to the 
destination configured for that first application. Obviously
this is a major issue.

The third option allows per-application logging configuration, and correctly 
filters and outputs the required log messages. 
However the performance penalty paid is very large; this isn't an acceptable 
solution either.

Note that this discussion hasn't talked about "Thread Context !ClassLoaders" or 
other technical points. The problem is not with
the details but with the general concept: when the Log object is shared among 
multiple applications it isn't possible to
have per-application configuration '''and''' reasonable performance.

The real root cause of this problem is that SHARED data (static members on a 
class) is being shared across supposedly
independent "applications". If no classes are "shared" between applications 
then the problem does not exist. However it
appears that (to my personal frustration) container vendors continue to 
encourage the use of shared classes, and 
application developers continue to use it.

The alternative solution is: avoid the use of "static" references to Log 
objects in any code that might be deployed into
a shared classpath.

== Does SLF4J have this problem? ==

Yes. SLF4J does suffer from exactly the issues listed above, and the 
recommendation is the same:
avoid static references to log objects from code that might be deployed in a 
shared classpath.

There are a number of possible scenarios here. First however we need to define 
a term "TCCL-aware".
A logging library is "TCCL-aware" when its initialisation code uses the Thread 
Context !ClassLoader
to attempt to locate its configuration files, rather than the !ClassLoader of 
the classes in the
logging library itself. As an example, in its standard form, log4j is not 
"TCCL-aware". However it
can be made "TCCL-aware" via a "Contextual !RepositorySelector" as described 
here:
  http://www.qos.ch/logging/sc.jsp

Now onto some possible scenarios:

Suppose SLF4J is deployed at a shared level, that there is a class in the 
shared classpath with a static Logger
reference, and that there is a class in the application classpath with a static 
logger. When the underlying
logging library is not TCCL-aware then logging configuration is only read from 
the "shared" classpath, ie applies
globally to all output from all applications deployed within the container. 
Output is correct, but there is no
ability to enable debug logging for just one application, and all output from 
all applications is mixed together.
When the underlying logging library is TCCL-aware, then there ''is'' 
per-application logging configuration. In
addition, output from those classes deployed at the application level is 
correct. However the class deployed
at the shared level will have its Log object initialised using the 
configuration for the very first application
that calls that class; when other applications call that same class the logging 
output will go to the destination
of the ''first'' application. Not good.

If SLF4J is deployed at both the shared and the application level, and 
parent-first classloading is selected for
an application then behaviour is identical to the above. Even when child-first 
classloading is selected, if
the underlying logging library is TCCL-aware then things also go poorly. 
However if child-first classloading is
selected AND the underlying logging library is not TCCL-aware then the results 
are almost bearable: output from
the shared class can only be configured globally, ie all output goes to the 
same destination regardless of which
application made the call.

Unfortunately in most cases the authors of library code are not in the position 
of being able to specify
to the library users where the code will be deployed, what classloading order 
will be selected, or what the
behaviour of the underlying logging library should be. Therefore it ''is'' 
necessary to avoid the use of
static references to SLF4J loggers for exactly the same reasons as 
commons-logging.

SLF4J ''does'' have less probability of encountering problems than 
commons-logging when used incorrectly,
however. The reasons are:
 * Currently not a lot of logging libraries are "TCCL-aware", and
 * Very little library code of the type deployed into shared classpath 
locations currently uses SLF4J.

Commons-logging is itself TCCL-aware specifically to enable application-level 
configuration of logging
libraries that are not TCCL-aware; effectively therefore every logging library 
qualifies as "TCCL-aware"
when used with commons logging. Commons-logging is also very widely used by 
many libraries that ''are''
frequently deployed via shared classloaders.

Note that this is section is NOT intended as criticism of SLF4J. As described 
earlier, the problem is due to the
fundamental conflict between isolating logging between applications while 
sharing a static Log reference,
and therefore SLF4J faces the same constraints as commons-logging in these 
scenarios.

== Alternatives to static loggers ==

In most cases, simply leaving out the "static" qualifier from the Log reference 
is the correct solution.
Even when a Class is shared between supposedly-independent applications 
(because the .class file is deployed
via a shared classloader), instances of that class are not shared. The TCCL 
active when the instance is created 
and its Log member initialised will therefore be the same TCCL active when any 
of its methods which generate
log output are called. Note that in the case of commons-logging this does NOT 
increase the number of Log objects
created; each instance will have a reference to the same Log object. 

However the "lookup" of that Log object does need to be performed each time an 
instance is created; for objects
which have short lifetimes this may be undesirable. In this situation, if the 
short-lived object has a reference
to some longer-lived one then the longer-lived one can host the Log object, eg
{{{
  public ShortLiver {
    private Owner owner;
    ShortLiver(Owner owner) {
        this.owner = owner;
    }
    public void doSomething() {
      owner.getShortLiverLog().debug("doSomething called");
    }
  }
}}}

Alternatively, the Log object can be retrieved when necessary:
{{{
  public ShortLiver {
    public void doSomething() {
      Log log = LogFactory.getLog(ShortLiver.class);
      log.debug("doSomething called");
    }
  }
}}}

Note that this applies only to code that may be deployed in a shared 
!ClassLoader. Normal application code
need not be concerned with this issue at all.

== Container-assisted logging ==

A few containers have tried to work around the problems listed above. In 
particular, JBoss provides
a custom log4j "filter" that uses internal knowledge about the container to 
make configuration at
the container level work in a somewhat acceptable manner. As described above, 
deploying commons-logging
(or SLF4J) at the shared level, and deploying a non-TCCL-aware underlying 
library has the undesirable
effect of imposing a single logging configuration on all contained 
applications. However the jboss
custom filter then allows log messages flowing through that single logging 
instance to be separated
out again into different destinations according to the originating application. 
This has the desired
effect in the end, although it is somewhat inefficient; turning up logging to 
debug on one application
actually turns up logging to debug on ''all'' applications, though output from 
applications other than
the one of interest is then suppressed again before being output.

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to