Introduction

Find Avalon Excalibur's Log Management System in the org.apache.avalon.excalibur.logger package. Excalibur-Logger integrates neatly into the Avalon ECM and Fortress. The main goal is to be able to define the log categories on a component basis by specifying a 'logger' attribute which denotes the log category to use for a particular component (given the component is LogEnabled or Loggable).

Not just for logkit

Though the documentation doesn't reflect this accurately yet (patches welcome), excalibur-logger has been updated to support other logging toolkits like jdk1.4 logging and log4j as well.

Not just for ECM

Though the documentation doesn't reflect this accurately yet (patches welcome), excalibur-logger is not just used by (or usable by) ECM. Other containers can and do make use of logger, too.

Example configurations

Look at this example of a component definition:

                
<component role="my.component.role" class="my.component.roleImpl"
    logger="category.subcategory"/>
              
            

And now let's have a look at a hypothetical Excalibur-Logger configuration using Logkit:

                
  <?xml version="1.0"?>

  <logkit>
    <factories>
      <factory type="file"
          class="org.apache.avalon.excalibur.logger.factory.FileTargetFactory"/>
      <factory type="priority-filter"
          class="org.apache.avalon.excalibur.logger.factory.PriorityFilterTargetFactory"/>
    </factories>

    <targets>
      <file id="root">
        <filename>lolo/${current-dir}/lala/${foo}/logs/main.log</filename>
        <format type="extended">
%7.7{priority} %5.5{time}   [%8.8{category}] (%{context}): %{message}\n%{throwable}
        </format>
        <append>true</append>
      </file>
      <file id="classloader">
        <filename>logs/classloader.log</filename>
        <format type="raw"/>
      </file>
      <priority-filter id="foo" log-level="WARN">
        <file>
          <filename>logs/foo.log</filename>
          <format type="extended">
%7.7{priority} %5.5{time}: %{message}\n%{throwable}
          </format>
        </file>
      </priority-filter>
    </targets>

    <categories>
      <category name="cocoon" log-level="INFO">
        <log-target id-ref="root"/>

        <category name="classloader" log-level="DEBUG">
          <log-target id-ref="classloader"/>
        </category>
      </category>
      <category name="foo" log-level="DEBUG">
        <log-target id-ref="foo"/>
      </category>
    </categories>
  </logkit>
              
            

As you've seen the configuration file for excalibur-logger when used with logkit has three sections (beside the root element).

<factories>
The factories section defines the LogTargetFactorys that are used to create the needed LogTargets. You'll find the factories available in the org.apache.avalon.excalibur.logger.factory package. You can write your own factories which only needs to implement the org.apache.avalon.excalibur.logger.LogTargetFactory interface or you extend one of the available factories in the mentioned package.
<targets>
The targets section defines the individual LogTargets. The element name of a target definition corresponds to a type attribute of a <factory> element. You'll probably need to consult the javadocs of the corresponding factory to get familiar with the configuration options available for a particular target.
<categories>
The categories section finally assembles all together. The name attribute of a category gives the reference used in logger attribute in the components configuration files. The log-level attribute gives the logging priority to the Logger of that category. <category> elements have <log-targets> children which, you already guessed, defines the LogTargets for a particular logging category. You'll also see in the sample above that category elements can be nested to define sub-categories.

Design principles and implementation details of Excalibur-Logger

The first abstraction is the LogKitManager:

        public interface LogKitManager
        {
            Logger getLogger( String categoryName );
        }
            

There is a implementation named DefaultLogKitManager which is the only class exposed to clients. As a convenient a additional interface is introduced for the ComponentManager (stolen from the role management system) which states that a class is willing to get a LogKitManager:

        public interface LogKitManageable
        {
            void setLogKitManager( LogKitManager logmanager );
        }
            

This method has to be called before the configure method but after the contextualize method.

The DefaultLogKitManager is Configurable (as well as Loggable [the initial default logger] and Contextualizable [to pass along for ie. ServletOutputLogTarget]) and gets a Configuration object as expressed in the logkit xml syntax above. This DefaultLogKitManager then uses a object of type

        public interface LogTargetFactoryManager
        {
            LogTargetFactory getLogTargetFactory( String factoryName );
        }
            

The DefaultLogTargetFactoryManager is Configurable (as well as Loggable and Contextualizable) and gets the Configuration object located at the <factories> element. It will instanciate the concrete factories into a map keyed by the type attribute. So we are at the LogTargetFactory abstraction which is:

        public interface LogTargetFactory
        {
            LogTarget createTarget( Configuration configuration )
                throws ConfigurationException;
        }
            

It may happen that a LogTargetFactory needs to create LogTargets they don't know in advance and thus an additional interface is needed:

        public interface LogTargetFactoryManageable
        {
            void setLogTargetFactoryManager(
                LogTargetFactoryManager logTargetFactoryManager );
        }
            

This eases writing factories which acts like decorators ( AsyncLogTarget, PriorityFilter) and thus need a LogTargetFactoryManager to create the decorated LogTargets which are embeded in the configuration of them (see <priority-filter> above).

After initializing the LogTargetFactoryManager a LogTargetManager

        public interface LogTargetManager
        {
            LogTarget getLogTarget( String targetId );
        }
            

is created. The implementation DefaultLogTargetManager is, you guess it, Configurable (as well as Loggable and Contextualizable). The Configuration object is the <targets> element in the xml syntax and is put into a map keyed by the id attribute of the target element. It is also LogTargetFactoryManageable tob e able to create the LogTargets.

The last step of the DefaultLogKitManagers configure method is to create the actual categories based on the categories elements content. It does it as the syntax will show in a recursive way populating the Loggers retrieved by Hierarchy.getDefaultHierarchy().getLoggerFor( full_category ) with the denoted LogTargets from the LogTargetManager.

After that the LogKitManager is ready to be asked for Loggers.

Now ECM is aware of a "magic attributes" named logger and used like logger="category" on the component definition syntax. The classes building up ECM are made LogTargetFactoryManageable. If you pass along a LogKitManager to the ExcaliburComponentManager it will retrieve the denoted logger category specified with the logger attribute from the LogKitManager and pass it to Components implementing Loggable.