The version of Apache log4j used by SoundHelix.

[[ 🗃 ^aEp6o apache-log4j ]] :: [📥 Inbox] [📤 Outbox] [🐤 Followers] [🤝 Collaborators] [🛠 Commits]

Clone

HTTPS: git clone https://vervis.peers.community/repos/aEp6o

SSH: git clone USERNAME@vervis.peers.community:aEp6o

Branches

Tags

v1_2_alpha0 :: docs /

proposal.html


<html>
<header>
<title>Revised log4j interfaces</title>
<header>
<body>

<center>
<h1>Revised log4j interfaces</h1>
Ceki G&uuml;lc&uuml
</center>

<p>In the current log4j code, the <code>Category</code> class is
polluted with static methods such as <code>getInstance()</code>,
<code>getRoot()</code>, <code>exists</code>() etc. These methods
essentially wrap method invocations on the default hierarchy. Although
a single hierarchy may be suitable in stand alone applications, it is
inadequate when used within Servlet containers or Application
Servers. Moreover, a number of users have asked for the simplication
of the <code>Category</code> class.

<p>At this juncture it also seems beneficial to adopt some of the
component names as proposed in the upcoming JSR47 API, namely,
<code>Level</code> instead of <code>Priority</code> and
<code>Logger</code> instead of <code>Category</code>.

<h1>Levels, Loggers and the LogManager</h1>

<p>It is proposed to rename the <code>Priority</code> class to
<code>Level</code>.  To preserve backward compatibility a Priority
class will be included which will extend <code>Level</code> without
changing or adding any functionality.  Classes that contain methods
involving priorities, say <code>setPriority</code>, will contain a
method called <code>setLevel</code> and also a method called
<code>setPriority</code> (to preserve backward compatibility).

<p>The user will only deal with a minimal <code>Logger</code> class
offering few essential methods. More concretely:

<p><table bgcolor="CCCCCC">
<tr><td>
<pre>
public <b>abstract class</b> Logger {

  protected final String  name;  
  
  protected Logger(String name) {
    this.name = name;
  }

  public final String getName() {
    return name;
  }

  public abstract boolean isDebugEnabled(); 
  public abstract void debug(Object message);  
  public abstract void debug(Object message, Throwable t);  

  public abstract boolean isInfoEnabled(); 
  public abstract void info(Object message);  
  public abstract void info(Object message, Throwable t);

  public abstract boolean isWarnEnabled(); 
  public abstract void warn(Object message);  
  public abstract void warn(Object message, Throwable t);

  public abstract boolean isErrorEnabled(); 
  public abstract void error(Object message);  
  public abstract void error(Object message, Throwable t);

  public abstract boolean isFatalEnabled(); 
  public abstract void fatal(Object message);  
  public abstract void fatal(Object message, Throwable t);

  public abstract boolean isEnabledFor(Level level);  
  public abstract void log(Level level, Object message, Throwable t);  
  public abstract void log(Level level, Object message);

}
</pre>
</td></tr>
</table>

<p>Here are a few remarks on the <code>Logger</code> class.

<ul>

<p><li>For performance reasons, <code>Logger</code> is an abstract
class and not an interface.

<p><li>Knowledge about the <code>debug</code>, <code>info</code>,
warn, <code>error</code>, <code>fatal</code> levels is hardcoded in
<code>Logger</code> methods. This seems appropriate because, as far as
I am aware, these levels are not disputed.

<p><li>There are no methods for localized (internationalized)
logging. There are already plenty of facilities supporting
internationalization. It does not seem appropriate for log4j to
re-invent the wheel.

<p><li>There are no methods for attaching appenders. 

</ul>

<h2>LogManager</h2>

<p><b>Intent</b>

<p>The <code>LogManager</code> provides a flexible method for
retrieving <code>Logger</code> instances of <em>varying types</em>
held in <em>context-dependent repositories</em>.

<p><b>Motivation</b>

<p>Log4j is a a low level API used in a variety of projects.
Consequently, it is hard to make a priori assumptions about the
environment where log4j will run. The problem is particularly acute in
embedded components (e.g. libraries) which rely on log4j for their
logging. The author of embedded component can rarely afford to make
restrictive assumptions about the surrounding environment, a fortiori
assumtions about logging. It might be the case that the end user is
not interested in logging at all. It would be a total waste to
generate logging output for a user who will never look at them. Under
such circumstances, the embedded component will want to use a
<code>NullLogger</code> which would not genereate any log output at
all.

<p>Logging in Application Servers and Servlet Containers also create
unique problems. It is often desirable to separate logging output
originating from different applications (or web-application in the
case of Servlet Containers).  In the current version of log4j it is
possible to have different applications live in their own parallel
universe by using a different hierarchy for each application. For more
details, refer to my <a
href="http://apache.org/~ceki/multHierarchy.tar.gz"><b>multiple
hierarchy tutorial</b></a> for Servlet Containers.

<p>Using multiple hierarchies works well with code that is designed to
use them. However, it does not entend to a library which uses log4j
but is unaware of multiple hierarchies.

<p>LogManager should allow us to vary <code>Logger</code>
implementation depending on the circumstances. Moreover, we would like
to be able to control the logging repository (or hierarchy) where
loggers are held depending on the application context.

<p><b>Related Patterns</b> 

<p>LogManager is related to the <a
href="http://c2.com/cgi/wiki?AbstractFactoryPattern">AbstractFactory</a>
design pattern. It is largely based on the
<code>PluggableFactory</code> pattern.  Refer to John Vlissides' two
articles <a
href="http://www.research.ibm.com/designpatterns/pubs/ph-nov-dec98.pdf">Pluggable
Factory, Part I</a> and <a
href="http://www.research.ibm.com/designpatterns/pubs/ph-feb99.pdf">Pluggable
Factory, Part II</a> to gain more insight on the problem.

<p><b>Public interface</b>

<p>The public interface of the LogManager class consists of a few
<em>static</em> methods.

<p><table bgcolor="CCCCCC">
<tr><td>
<pre>
public class LogManager {

  public <b>static</b> Logger getRootLogger() {
     // return the appropriate root Logger 
  }

  public <b>static</b> Logger getLogger(String name) {
     // return the appropriate Logger instance
  }

  // The actual task of manufacturing loggers is delegated to a <code>LoggerRepository</code>
  public <b>static</b> void setLoggerRepository(LoggerRepository repository) {
    
  }  
}
</pre>
</td></tr>
</table>

<p>Note that altough the methods are static there is a lot of
flexibility in the underlying implementation. See the implementation
section below.

<p>Typical usage:

<p><table bgcolor="CCCCCC">
<tr><td>
<pre>

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

public class Foo {

 final static Logger logger = LogManager.getLogger(Foo.class.getName());

 void bar() {
   logger.debug("hello world.");
 }
}
</pre>
</td></tr>
</table>

<p>One of the advantages of the <code>LogManager</code> approach is
that the <code>getInstance</code> method can return totally different
<code>Logger</code> implementations. For example, under JDK 1.2 we can
return a Logger implementation that is aware of the Java2 security
model whereas under JDK 1.1 the returned logger implementation may be
oblivious to Java2 security checks. In shipped code, LogManager may be
configured to return a <code>NullLogger</code> which could implement
the <code>Logger</code> (abstract) class with empty method bodies.

<p><b>Impelementation</b>

<p>The behavior of <code>LogManager</code> is mostly determined by the
<code>LoggerRepository</code> it uses.  However, it may also be influenced
by system properties or by parameters passed as a result of
<code>LogManager</code> method invocations.

<p>Here is a tentative implementation.

<p><table bgcolor="CCCCCC">
<tr><td>
<pre>
package org.apache.log4j;

/**
  Use the <code>LogManager</code> to retreive instances of {@link Logger}.
*/
public <b>final</b> class <font color="0000AA"><b>LogManager</b></font> {

  static <b>private</b> Object guard = null;
  static <b>private</b> LoggerRepository loggerRepository = null;

  // By default the logger repository is set to a platform dependent
  // default logger repository.
  <b>static</b> {
    if(java14) {
      loggerRepository = new DefaultLoggerRepository14();
    } else if (java2) {
      loggerRepository = new DefaultLoggerRepository2();
    } else {
      loggerRepository = new DefaultLoggerRepository11();
    }
  }


  /**
     Sets <code>LoggerRepository</code> but only if the correct
     <em>guard</em> is passed as parameter.
     
     Initally the guard is null.  If the guard is
     <code>null</code>, then invoking this method sets the logger
     repository and the guard. Following invocations will throw a {@link
     IllegalArgumentException}, unless the previously set
     <code>guard</code> is passed as the second parameter.

     This allows a high-level component to set the logger repository to
     be used, thus, fixing the log4j environment.
     
     For example, when tomcat starts it will be able to install its
     own logger repository. However, if and when Tomcat is embedded
     within JBoss, then JBoss will install its loggger repository and
     Tomcat will use the repository set by its container, JBoss.
  */
  public
  <b>static</b>
  void  <font color="#0000AA"><b>setLoggerRepository</b></font>(LoggerRepository repository, Object guard) 
                                              throws IllegalArgumentException {
    if((LogManager.guard != null) && (LogManager.guard != guard)) {
      throw new IllegalArgumentException(
           "Attempted to reset the LoggerRepository without possessing the guard.");
    }

    if(repository == null) {
      throw new IllegalArgumentException("LoggerRepository must be non-null.");
    }

    LogManager.guard = guard;
    LogManager.loggerRepository = repository;

  }

  /**
     Retrieve the appropriate root logger.
   */
  public
  <b>static</b>
  Logger <font color="#0000AA"><b>getRootLogger()</b></font> {
     // Delegate the choice of the root logger to the logger repository
    return loggerRepository.getRootLogger();
  }

  /**
     Retrieve the appropriate {@link Logger} instance.  
  */
  public
  <b>static</b>
  Logger <font color="#0000AA"><b>getLogger</b></font>(String name) {
     // Delegate the actual manufacturing of the logger to the logger repository.
    return loggerRepository.getLogger(name);
  }
}
</pre>
</td></tr>
<table>

<h2>LoggerRepository</h2>

<p><b>Intent</b>

Serve as a repository of logger objects offering primitives for
retreiving individual loggers or acting on collections of loggers. In
special cases a <code>LoggerRepository</code> can be itself composed
of <code>LoggerRepositories</code>.

<p><b>Motivation</b>

One of the distinctive feature of log4j is its ability to arrange
loggers (categories) in a hierarchy. See the <a
href="api/org/apache/log4j/Hierarchy.html">Hierarchy class</a> for
further detail.  A hierarchy is merely a repository of categories
which happens to arrange them in a tree like structure. The log4j
hiearchy as its stands today (August 2001) can only deal with
<code>Category</code> objects which are heavier than loggers as
defined previously.


<p><table bgcolor="CCCCCC">
<tr><td>
<pre>
public interface LoggerRepository {

  // return the appropriate root Logger
  public Logger getRootLogger();

  // return an appropriate Logger instance
  public Logger getLogger(String name);

  // 
  public void enable(Level level);
  
  
}
</pre>
</td></tr>
</table>





<p>
<h2>Backward compatibility</h2>

<p>Existing users of log4j need not worry about the compatibility of
their code with future versions of log4j. We will make sure that
client code runs seamlessly with log4j versions based on the revised
interfaces proposed in this document.

<h2>Contributors</h2>

The present propposal is based on discussions that were held on the
jakarta-commons mailing list. The contributors listed below are not
known to endorse this proposal in any way. 

<ul>

<li>Costin Manolache 

<li>Peter Donald (author of <a
href="http://jakarta.apache.org/avalon/logkit/index.html">jakarta-avalon-logkit</a>)

<li>Rodney Waldhoff 

<li>Morgan Delagrange

</ul>

</body>

</html>
[See repo JSON]