| <html> |
| <head> |
| <title>Extending Log4J</title> |
| </head> |
| |
| <body bgcolor="white"> |
| |
| <center> |
| <h1>Adding Conversion Characters to PatternLayout</h1> |
| |
| "Paul Glezen" |
| |
| January 2001 <br><br> |
| </center> |
| |
| <hr> |
| <h2>Abstract</h2> |
| |
| <p> |
| This article describes a systematic way to extend the |
| <a href="http://jakarta.apache.org/log4j">log4j</a> API to include |
| additional attributes formatted using the |
| <code><a href="../docs/api/org/apache/log4j/PatternLayout.html">PatternLayout</a></code> |
| class. |
| <p> |
| <hr> |
| <h2>Contents</h2> |
| <ul> |
| <li><a href="#intro">Introduction</a> |
| <ul> |
| <li><a href=#case>The Case Study</a> |
| <li><a href=#peek>A Peek Under the Hood</a> |
| </ul> |
| <li><a href="#process">The Process</a> |
| <ul> |
| <li><a href="#LoggingEvent">Extending <code>LoggingEvent</code></a> |
| <li><a href="#PatternLayout">Extending <code>PatternLayout</code></a> |
| <li><a href="#PatternParser">Extending <code>PatternParser</code> and |
| <code>PatternConverter</code></a> |
| <li><a href="#Category">Extending <code>Category</code></a> |
| <li><a href="#CategoryFactory">Extending <code>CategoryFactory</code></a> |
| </ul> |
| <li><a href="#usage">Usage</a> |
| <ul> |
| <li><a href="#configurators">Configurators</a> |
| </ul> |
| <li><a href="#enhancements">Further Enhancements</a> |
| </ul> |
| <p> |
| <hr> |
| <a name="intro"><h2>Introduction</h2></a> |
| |
| <p> |
| This article assumes familiarity with the log4j |
| <a href="manual.html">User Manual</a>. It builds on fundamental |
| classes described in both the User Manual and the |
| <a href="api/index.html" target="_top">Javadoc API</a>. To assist in illustrating the |
| concepts, a simple case study will be developed along side the |
| explanations. The resulting classes may be used as a template |
| for your own extensions. Condensed (i.e. statements compressed, |
| comments removed) snippets of the case study code |
| are included in this document. |
| |
| <a name="case"><h3>The Case Study</h3></a> |
| The case study was developed in a CORBA environment in which the |
| following information for each log entry was needed. The letters in |
| parenthesis represent the corresponding character to be used by the |
| <code><a href="api/org/apache/log4j/PatternLayout.html">PatternLayout</a></code> |
| class for formatting. |
| |
| <p> |
| <ul> |
| <li><b>Host Name (h)</b> - the IP address or hostname of the physical machine |
| on which the Category was running. |
| <li><b>Server Name (s)</b> - The name of the application server process. In |
| this context, a <i>server</i> refers to a process that accepts requests |
| rather than referring to a machine. The term <i>host</i> refers to a |
| physical machine. Several servers may run on the same host. |
| <li><b>Component Name (b)</b> - Rather than getting bogged down on what |
| constitutes a component, let's assume for the case study that a component |
| is a unit of software worth denoting in the logs. |
| <li><b>Version (v)</b> - the version of the component from which the log entry |
| originated. |
| </ul> |
| <p> |
| It seems odd to use "b" for the component name. Presently |
| <code><a href="api/org/apache/log4j/PatternLayout.html">PatternLayout</a></code> |
| already defines both "C" and "c" for class name and category name respectively. |
| |
| <h3><a name="peek">A Peek Under the Hood</a></h3> |
| <p> |
| In principle, if the steps described below are followed closely, there is |
| not a need to understand how the extended classes will be used by log4j. |
| But sometimes software development can be entirely unprincipled. You may |
| wish to extend log4j in a different manner than describe here or you may |
| make a mistake that requires knowledge of what is really going on. (Heaven |
| forbid there be a mistake in this document). In any case, it doesn't hurt |
| to get an idea of what's going on. |
| <p> |
| The following describes a "typical" logging scenario in the un-extended log4j |
| case. |
| <p> |
| <ol> |
| <li>Application code invokes a log request on a |
| <code><a href="api/org/apache/log4j/Category.html">Category</a></code> |
| object. Let's say the <code>info</code> method was invoked. |
| <p> |
| <li>The first thing <code>info</code> does is to check if logging has |
| been turned off entirely for the info level. If so, it returns |
| immediately. We'll assume for this scenario that logging has not |
| been turned off for the info level. |
| <p> |
| <li>Next <code>info</code> compares the |
| <code><a href="api/org/apache/log4j/Priority.html">Priority</a></code> |
| level for this category against <code>Priority.INFO</code>. Assuming |
| the priority warrants logging the message, the category instantiates a |
| <code><a href="api/org/apache/log4j/spi/LoggingEvent.html">LoggingEvent</a></code> |
| object populated with information available for logging. |
| <p> |
| <li>The <code>Category</code> instance passes the <code>LoggingEvent</code> |
| instance to all its |
| <code><a href="api/org/apache/log4j/Appender.html">Appender</a></code> |
| implementations. |
| <p> |
| <li>Most (but not all) <code>Appender</code> implementations should have |
| an associated subclass of |
| <code><a href="api/org/apache/log4j/Layout.html">Layout</a></code>. |
| The <code>Layout</code> subclass is passed the <code>LoggingEvent</code> |
| instance and returns the event's information formatted in a |
| <code>String</code> according to the configuration of the <code>Layout</code>. |
| <p> |
| When the <code>Layout</code> subclass is |
| <code><a href="api/org/apache/log4j/PatternLayout.html">PatternLayout</a></code>, |
| the format of the event's information is determined by a character sequence |
| similar to the <b>C</b> language library's <code>printf</code> routine. |
| <code>PatternLayout</code> delegates the parsing of this character sequence to a |
| <code><a href="api/org/apache/log4j/helpers/PatternParser.html">PatternParser</a></code> |
| instance. |
| <p> |
| When the <code>PatternLayout</code> was constructed, it created a |
| <code>PatternParser</code> to tokenize the character sequence. Upon |
| recognizing a token, the <code>PatternParser</code> constructs an appropriate |
| <code><a href="api/org/apache/log4j/helpers/PatternConverter.html">PatternConverter</a></code> |
| subclass, passing it formatting information from the token. Often the |
| <code>PatternConverter</code> subclasses are implemented as static inner |
| classes of <code>PatternParser</code>. The <code>parse</code> method of |
| the <code>PatternParser</code> returns a linked list of these |
| <code>PatternConverter</code> subclasses. |
| <p> |
| <li><code>PatternLayout.format()</code> passes the <code>LoggingEvent</code> |
| to each <code>PatternConverter</code> subclass in the linked list. Each link |
| in the list selects a particular item from the <code>LoggingEvent</code>, |
| converts this item to a <code>String</code> in the proper format and appends |
| it to a <code>StringBuffer</code>. |
| <p> |
| <li>The <code>format</code> method returns the resulting <code>String</code> |
| to the <code>Appender</code> for output. |
| </ol> |
| <p> |
| The above discussing involved most of the classes that we must extend or |
| implement. |
| <p> |
| <ul> |
| <li><code><a href="api/org/apache/log4j/PatternLayout.html">org.apache.log4j.PatternLayout</a></code> |
| <li><code><a href="api/org/apache/log4j/Category.html">org.apache.log4j.Category</a></code> |
| <li><code><a href="api/org/apache/log4j/spi/LoggerFactory.html">org.apache.log4j.spi.CategoryFactory</a></code> |
| <li><code><a href="api/org/apache/log4j/spi/LoggingEvent.html">org.apache.log4j.spi.LoggingEvent</a></code> |
| <li><code><a href="api/org/apache/log4j/helpers/PatternParser.html">org.apache.log4j.helpers.PatternParser</a></code> |
| <li><code><a href="api/org/apache/log4j/helpers/PatternConverter.html">org.apache.log4j.helpers.PatternConverter</a></code> |
| </ul> |
| <p> |
| <hr> |
| <a name="process"><h2>The Process</h2></a> |
| Below are steps required to add additional attributes available |
| for logging by extending log4j. This will allow you to specify |
| their output formats in the same manner as those provided by the |
| <code>PatternLayout</code> class. The steps are numbered for |
| reference only. It makes no difference in which order they are |
| followed. |
| <p> |
| It's helpful if you know the attributes you wish to add and a |
| <code>PatternLayout</code> symbol for each one before you begin. Be |
| sure to consult the <code>PatternLayout</code> documentation to ensure |
| the symbols you select are not already in use. |
| <p> |
| Before we dig in, I should give the standard lecture on comments. |
| If the log4j library were not well documented, it would be useless |
| to everyone but the log4j creators; likewise with your extensions. |
| Much like eating vegetables and saving the environment, we all agree |
| commenting code properly should be done. Yet it is often sacrificed |
| for more immediate pleasures. We all write code faster without |
| comments; especially those pesky Javadoc comments. But the reality |
| is that the utility of undocumented code fades exponentially with time. |
| <p> |
| Since the log4j product comes with Javadoc comments together with |
| the documentation it produces, it makes sense to include Javadoc |
| comments in your extensions. By their very nature, logging tools |
| are strong candidates for reuse. They can only be independently |
| re-used if they are supported by strong documentation component. |
| <p> |
| This all having been said, I have elected to remove most comments from |
| examples in the interest of space rather than including them to serve |
| as a nagging reminder. The reader is referred to the case study source |
| code files for a Javadoc version and a |
| <a href="http://java.sun.com/javadoc">Javadoc website</a> |
| for more information on Javadoc conventions. |
| |
| <a name="LoggingEvent"><h3>1. Extending <code>LoggingEvent</code></h3></a> |
| |
| Extending the <code>LoggingEvent</code> class should be one of the |
| trivial steps. All that is needed in the extension is the addition |
| of public data members representing the new attributes and a new |
| constructor to populate them. |
| <p> |
| <table border=1> |
| <tr><td> |
| <pre> |
| import org.apache.log4j.Category; |
| import org.apache.log4j.Priority; |
| import org.apache.log4j.spi.LoggingEvent; |
| |
| public class AppServerLoggingEvent extends LoggingEvent |
| implements java.io.Serializable |
| { |
| public String hostname; |
| public String component; |
| public String server; |
| public String version; |
| |
| public AppServerLoggingEvent( String fqnOfCategoryClass, |
| AppServerCategory category, |
| Priority priority, |
| Object message, |
| Throwable throwable) |
| { |
| super( fqnOfCategoryClass, |
| category, |
| priority, |
| message, |
| throwable ); |
| |
| hostname = category.getHostname(); |
| component = category.getComponent(); |
| server = category.getServer(); |
| version = category.getVersion(); |
| } |
| } |
| </pre> |
| </table> |
| <p> |
| The constructor demonstrates that in most cases, the <code>Category</code> |
| subclass will contain most of the information necessary to populate |
| the attributes of the <code>LoggingEvent</code> subclass. Extensions |
| to <code>LoggingEvent</code> seem no more than a collection of strings |
| with a constructor. Most of the work is done by the super class. |
| |
| <a name="PatternLayout"><h3>2. Extending <code>PatternLayout</code></h3></a> |
| |
| Extending the <code>PatternLayout</code> class should be another |
| simple matter. The extension to <code>PatternLayout</code> should |
| differ from its parent only in the creation of a |
| <code>PatternParser</code> instance. The extended |
| <code>PatternLayout</code> should create an extended |
| <code>PatternParser</code> class. Fortunately, this task in |
| <code>PatternLayout</code> is encapsulated within a single method. |
| <p> |
| <table border=1> <tr><td> |
| <pre> |
| import org.apache.log4j.PatternParser; |
| import org.apache.log4j.PatternLayout; |
| |
| public class AppServerPatternLayout extends PatternLayout |
| { |
| public AppServerPatternLayout() |
| { |
| this(DEFAULT_CONVERSION_PATTERN); |
| } |
| |
| public MyPatternLayout(String pattern) |
| { |
| super(pattern); |
| } |
| |
| public PatternParser createPatternParser(String pattern) |
| { |
| PatternParser result; |
| if ( pattern == null ) |
| result = new AppserverPatternParser( DEFAULT_CONVERSION_PATTERN ); |
| else |
| result = new AppServerPatternParser ( pattern ); |
| |
| return result; |
| } |
| } |
| </pre> |
| </table> |
| |
| <a name="PatternParser"><h3>3. Extend <code>PatternParser</code> and <code>PatternConverter</code></h3></a> |
| |
| Recall from our <a href="#peek">peek under the hood</a> that the |
| <code>PatternParser</code> does much of its work in its |
| <code>parse</code> method. The <code>PatternLayout</code> object |
| instantiates a <code>PatternParser</code> object by passing it |
| the pattern string. The PatternLayout then invokes the |
| <code>parse</code> method of <code>PatternParser</code> to produce |
| a linked list of <code>PatternConverter</code> subclass instances. |
| It is this linked list of converters that is used to convert an |
| event instance into a string used by appenders. |
| <p> |
| Our job will be to subclass <code>PatternParser</code> to properly |
| interpret formatting characters we wish to add. Fortunately, |
| <code>PatternParser</code> has been designed so that only the one |
| step in the parsing process differing for each formatting character |
| has to be overridden. The grunt work of parsing is still performed |
| by the <code>PatternParser.parse()</code> method. Only the |
| <code>PatternParser.finalizeConverter</code> method has to be |
| overridden. This is the method that decides which |
| <code>PatternConverter</code> to create based on a formatting |
| character. |
| <p> |
| The extension to <code>PatternParser</code>, |
| <code>AppServerPatternParser</code>, is similar to its super class. |
| It uses |
| <ul> |
| <li>constants to identify the various formats |
| <li>a converter defined as private static inner-classes of |
| <code>AppServerPatternParser</code>. |
| <li>a <code>finalizeConverter</code> method which instantiates |
| the appropriate converter for a given format character. |
| </ul> |
| <p> |
| <code>AppServerPatternParser</code> differs principally by |
| dedicating a separate converter type for each logging |
| attribute to be formatted. |
| Rather than placing switch logic in the converter, like its |
| parent class, each converter only converts one format character. |
| This means the decision of which converter subclass |
| to instantiate is made at layout instantiation time rather |
| than in a switch statement at logging time. |
| <p> |
| It also differs in that the format constants are characters |
| rather than integers. |
| <p> |
| <table border=1> |
| <tr><td> |
| <pre> |
| import org.apache.log4j.*; |
| import org.apache.log4j.helpers.FormattingInfo; |
| import org.apache.log4j.helpers.PatternConverter; |
| import org.apache.log4j.helpers.PatternParser; |
| import org.apache.log4j.spi.LoggingEvent; |
| |
| public class AppServerPatternParser extends PatternParser |
| { |
| static final char HOSTNAME_CHAR = 'h'; |
| static final char SERVER_CHAR = 's'; |
| static final char COMPONENT_CHAR = 'b'; |
| static final char VERSION_CHAR = 'v'; |
| |
| public AppServerPatternParser(String pattern) |
| { |
| super(pattern); |
| } |
| |
| public void finalizeConverter(char formatChar) |
| { |
| PatternConverter pc = null; |
| switch( formatChar ) |
| { |
| case HOSTNAME_CHAR: |
| pc = new HostnamePatternConverter( formattingInfo ); |
| currentLiteral.setLength(0); |
| addConverter( pc ); |
| break; |
| case SERVER_CHAR: |
| pc = new ServerPatternConverter( formattingInfo ); |
| currentLiteral.setLength(0); |
| addConverter( pc ); |
| break; |
| case COMPONENT_CHAR: |
| pc = new ComponentPatternConverter( formattingInfo ); |
| currentLiteral.setLength(0); |
| addConverter( pc ); |
| break; |
| case VERSION_CHAR: |
| pc = new VersionPatternConverter( formattingInfo ); |
| currentLiteral.setLength(0); |
| addConverter( pc ); |
| break; |
| default: |
| super.finalizeConverter( formatChar ); |
| } |
| } |
| |
| private static abstract class AppServerPatternConverter extends PatternConverter |
| { |
| AppServerPatternConverter(FormattingInfo formattingInfo) |
| { |
| super(formattingInfo); |
| } |
| |
| public String convert(LoggingEvent event) |
| { |
| String result = null; |
| AppServerLoggingEvent appEvent = null; |
| |
| if ( event instanceof AppServerLoggingEvent ) |
| { |
| appEvent = (AppServerLoggingEvent) event; |
| result = convert( appEvent ); |
| } |
| return result; |
| } |
| |
| public abstract String convert( AppServerLoggingEvent event ); |
| } |
| |
| private static class HostnamePatternConverter extends AppServerPatternConverter |
| { |
| HostnamePatternConverter( FormattingInfo formatInfo ) |
| { super( formatInfo ); } |
| |
| public String convert( AppServerLoggingEvent event ) |
| { return event.hostname; } |
| } |
| |
| private static class ServerPatternConverter extends AppServerPatternConverter |
| { |
| ServerPatternConverter( FormattingInfo formatInfo ) |
| { super( formatInfo ); } |
| |
| public String convert( AppServerLoggingEvent event ) |
| { return event.server; } |
| } |
| |
| private static class ComponentPatternConverter extends AppServerPatternConverter |
| { |
| ComponentPatternConverter( FormattingInfo formatInfo ) |
| { super( formatInfo ); } |
| |
| public String convert( AppServerLoggingEvent event ) |
| { return event.component; } |
| } |
| |
| private static class VersionPatternConverter extends AppServerPatternConverter |
| { |
| VersionPatternConverter( FormattingInfo formatInfo ) |
| { super( formatInfo ); } |
| |
| public String convert( AppServerLoggingEvent event ) |
| { return event.version; } |
| } |
| } |
| </pre> |
| </table> |
| <p> |
| <a name="Category"><h3>4. Extending <code>Category</code></h3></a> |
| Extending <code>Category</code> and its factory will be more straight |
| forward than extending <code>PatternParser</code> and the converters. |
| The following tasks are involved in overridding |
| <code>Category</code> for our purposes. |
| <p> |
| <ul> |
| <li>Add fields corresponding to the new attributes with their |
| corresponding getters and setters. |
| <p> |
| <li>Override the constructor to accept or acquire information |
| about new attributes. |
| <p> |
| <li>Override the <code>forcedLog</code> method to ensure that a |
| correctly populated instance of |
| <code>AppServerLoggingEvent</code> is instantiated rather than |
| the default <code>LoggingEvent</code>. |
| <p> |
| <li>Override the <code>getInstance</code> method to use our |
| <code>CategoryFactory</code> (described in the next step). This will |
| require that we hold a static reference to our factory and provide a |
| way to initialize it. |
| </ul> |
| <p> |
| Most of the code below is standard getter/setter verbage which has been |
| somewhat abbreviated. The notable parts are in bold. We add five more |
| attributes to <code>Category</code>: the four new logging attributes |
| plus a static <code>AppServerCategoryFactory</code> reference. This is |
| pre-initialized to an instance with attributes set to null as a |
| precautionary measure. Otherwise the <code>getInstance</code> method |
| will result in a null pointer exception if invoked before the |
| <code>setFactory</code> method. |
| <p> |
| The <code>getInstance</code> method simply invokes its parent class |
| method that accepts a <code>CategoryFactory</code> reference in |
| addition to the category name. |
| <p> |
| The <code>forcedLog</code> method follows closely the corresponding |
| parent class method. The most important difference is the instantiation |
| of the <code>AppServerLoggingEvent</code>. A minor yet necessary |
| difference is the use of the <code>getRendererMap()</code> method rather |
| than accessing the data member directory as in <code>Category</code>. |
| <code>Category</code> can do this because the rendererMap |
| is package level accessible. |
| <p> |
| The <code>setFactory</code> method is provided to allow application code |
| to set the factory used in the <code>getInstance</code> method. |
| <p> |
| <table border=1> |
| <tr><td> |
| <pre> |
| import org.apache.log4j.Priority; |
| import org.apache.log4j.Category; |
| import org.apache.log4j.spi.CategoryFactory; |
| import org.apache.log4j.spi.LoggingEvent; |
| |
| public class AppServerCategory extends Category |
| { |
| protected String <b>component</b>; |
| protected String <b>hostname</b>; |
| protected String <b>server</b>; |
| protected String <b>version</b>; |
| private static CategoryFactory <b>factory = |
| new AppServerCategoryFactory(null, null, null)</b>; |
| |
| protected AppServerCategory( String categoryName, |
| String hostname, |
| String server, |
| String component, |
| String version ) |
| { |
| super( categoryName ); |
| <b>instanceFQN = "org.apache.log4j.examples.appserver.AppServerCategory";</b> |
| |
| this.hostname = hostname; |
| this.server = server; |
| this.component = component; |
| this.version = version; |
| } |
| |
| public String getComponent() |
| { return (component == null ) ? "" : result; } |
| |
| public String getHostname() |
| { return ( hostname == null ) ? "" : hostname; } |
| |
| public static Category <b>getInstance</b>(String name) |
| { |
| return Category.getInstance(name, factory); |
| } |
| |
| public String getServer() |
| { return ( server == null ) ? "" : server; } |
| |
| public String getVersion() |
| { return ( version == null ) ? "" : version; } |
| |
| protected void <b>forcedLog</b>( String fqn, |
| Priority priority, |
| Object message, |
| Throwable t) |
| { |
| <b> LoggingEvent event = new AppServerLoggingEvent(fqn, this, priority, message, t);</b> |
| callAppenders( event ); |
| } |
| |
| public void setComponent(String componentName) |
| { component = componentName; } |
| |
| public static void <b>setFactory</b>(CategoryFactory factory) |
| { AppServerCategory.factory = factory; } |
| |
| public void setHostname(String hostname) |
| { this.hostname = hostname; } |
| |
| public void setServer(String serverName) |
| { server = serverName; } |
| |
| public void setVersion(String versionName) |
| { version = versionName; } |
| } |
| </pre> |
| </table> |
| <p> |
| |
| <a name="CategoryFactory"><h3>5. Extending <code>CategoryFactory</code></h3></a> |
| |
| The last step is to provide an implementation of the |
| <code>CategoryFactory</code> interface that will correctly |
| instantiate our <code>AppServerCategory</code> objects. It |
| will obtain the hostname of the machine on which it runs using the |
| <code>java.net</code> API. Aside from providing getters and |
| setters for the attributes introduced, the only method to |
| be implemented is the <code>makeNewCategoryInstance</code>. |
| <p> |
| Below is a snippet from <code>AppServerCategoryFactory</code> |
| with getters, setters and comments removed. |
| <p> |
| <table border=1> |
| <tr><td> |
| <pre> |
| import org.apache.log4j.Category; |
| import org.apache.log4j.spi.CategoryFactory; |
| import java.net.InetAddress; |
| import java.net.UnknownHostException; |
| |
| public class AppServerCategoryFactory implements CategoryFactory |
| { |
| protected String hostname; |
| protected String server; |
| protected String component; |
| protected String version; |
| protected ResourceBundle messageBundle; |
| |
| protected AppServerCategoryFactory( String serverName, |
| String componentName, |
| String versionName ) |
| { |
| try |
| { |
| hostname = java.net.InetAddress.getLocalHost().getHostName(); |
| } |
| catch ( java.net.UnknownHostException uhe ) |
| { |
| System.err.println("Could not determine local hostname."); |
| } |
| |
| server = serverName; |
| component = componentName; |
| version = versionName; |
| } |
| |
| public Category makeNewCategoryInstance(String name) |
| { |
| Category result = new AppServerCategory( name, |
| hostname, |
| server, |
| component, |
| version); |
| return result; |
| } |
| } |
| </pre> |
| </table> |
| <p> |
| <hr> |
| <a name="usage"><h2>Usage</h2></a> |
| We now arrive at how to use what we have created. We must remember to |
| initialize log4j by creating an instance of |
| <code>AppServerCategoryFactory</code> and passing it to |
| <code>AppServerCategory</code>. Once done, we can obtain a |
| <code>AppServerCategoryInstance</code> anytime by using the static |
| <code>getInstance</code> method of <code>AppServerCategory</code>. |
| This will ensure that <code>AppServerLoggingEvent</code> instances |
| are generated by the category logging methods. |
| <p> |
| <table border=1> |
| <tr><td> |
| <pre> |
| import org.apache.log4j.*; |
| import org.apache.log4j.appserver.AppServerCategory; |
| import org.apache.log4j.appserver.AppServerCategoryFactory; |
| import org.apache.log4j.appserver.AppServerPatternLayout; |
| |
| public class test |
| { |
| private static String formatString = |
| "---------------------------------------------------%n" + |
| "Time: %d%n" + |
| "Host: %h%n" + |
| "Server: %s%n" + |
| "Component: %b%n" + |
| "Version: %v%n" + |
| "Priority: %p%n" + |
| "Thread Id: %t%n" + |
| "Context: %x%n" + |
| "Message: %m%n"; |
| |
| public static void main(String[] args) |
| { |
| AppServerCategoryFactory factory; |
| factory = new AppServerCategoryFactory("MyServer", "MyComponent", "1.0"); |
| AppServerCategory.setFactory( factory ); |
| Category cat = AppServerCategory.getInstance("some.cat"); |
| |
| PatternLayout layout = new AppServerPatternLayout( formatString ); |
| cat.addAppender( new FileAppender( layout, System.out) ); |
| |
| cat.debug("This is a debug statement."); |
| cat.info("This is an info statement."); |
| cat.warn("This is a warning statement."); |
| cat.error("This is an error statement."); |
| cat.fatal("This is a fatal statement."); |
| } |
| } |
| </pre> |
| </table> |
| <a name="configurators"><h3>Configurators</h3></a> |
| There is one a word of caution concerning the use of configurators that |
| may create <code>Category</code> instances (such as |
| <a href="api/org/apache/log4j/PropertyConfigurator.html"> |
| <code>PropertyConfigurator</code></a> |
| and |
| <a href="api/org/apache/log4j/xml/DOMConfigurator.html"> |
| <code>DOMConfigurator</code></a>). Since these configurators do not |
| know about our extensions, any <code>Category</code> instances they |
| create will not be <code>AppServerCategory</code> instances. To |
| prevent this problem, any <code>AppServerCategory</code> that one |
| might want to be configured through a configurator should be |
| instantiated before the configure method is invoked. In this way, |
| the configurator will configure the <code>AppServerCategory</code> |
| that already exists rather than creating an instance of its super |
| class. |
| <p> |
| The consequence of a configurator creating the super class by |
| mistake is merely that the extra attributes will not appear in |
| the log output. All other attributes are conveyed properly. |
| <p> |
| <hr> |
| <a name="enhancements"><h2>Further Enhancements</h2></a> |
| There are some other directions in which this log4j extension |
| may be enhanced: |
| <p> |
| <ol> |
| <li>The hostname attribute could incorportate a formatting convention |
| similar to that of class and category names whereby only a certain |
| number of the more significant components are displayed. But |
| whereas with class and category names, the most significant component |
| is on the right, with host names, it is on the left. |
| <p> |
| <li>Specifying a version number could be dangerous since programmers |
| are apt to change versions of the code without changing the |
| string constant in the code which specifies the version. Some |
| source control programs provide for insertion of a version number |
| into source. For those that don't, including the version number as |
| a constant is likely to lead to confusion later on. It would be |
| nice to see this short-coming addressed. |
| </body> |
| </html> |