| <HTML> |
| <document> |
| |
| <HEAD> |
| <title>JSR47 vs. log4j</title> |
| </HEAD> |
| |
| <body> |
| |
| <CENTER> |
| <H1>JSR47 vs. log4j</H1> |
| </CENTER> |
| |
| <hr> |
| <p>I consider it quite distasteful to criticize other people's work, |
| especially in public. However, since the logging API included in JDK |
| 1.4 will be considered by many as the "standard", I feel compelled to |
| react. I am not alone in my criticism of JSR47, Greg Davis has his own |
| set of <a |
| href="http://www.swzoo.org/documents/miscellaneous/jsr047/">comments</a>. |
| |
| <p>The JDK 1.4 logging API is a result of the <a |
| href="http://jcp.org/aboutJava/communityprocess/review/jsr047/index.html">JSR47 |
| effort</a>, led by Graham Hamilton. |
| |
| <p>Before delving into the details, some historical perspective is in |
| order. I am the founder of the log4j project. I participated in the |
| specification of the JSR47 API, although not as an expert. In 1999, I |
| was still working for IBM and could not join the experts group because |
| big blue had already Chris Barlock as a member in the JSR47 experts |
| group. Chris is the author of <a |
| href="http://www.alphaworks.ibm.com/tech/loggingtoolkit4j">IBM's |
| logging toolkit for Java</a>. |
| |
| <p>On the surface, his toolkit has heavily influenced the JSR47 |
| API. In particular, the two share the same basic components, namely |
| loggers, levels, handlers and formatters. In log4j, these components |
| are called categories, priorities, appenders and layouts |
| respectively. Pairwise, they are identical in purpose. As such, the |
| terms logger and category, level and priority, handler and appender, |
| formatter and layout will be used interchangeably in the remainder of |
| this document. |
| |
| <p>Even after a casual review it should be apparent that the log4j and |
| JSR47 APIs are <em>very</em> similar. For one, they are the only |
| logging APIs which are based on a named hierarchy. If you understand |
| one API, then understanding the concepts of the other should be a |
| breeze. There are differences however. |
| |
| <h2>On Parents and Children</h2> |
| |
| <p>In JSR47, a parent logger knows about its children but not the |
| other way around. Children do not have links to their parent. For |
| example, the logger named <code>"foo"</code> knows about |
| <code>"foo.bar1"</code> and <code>"foo.bar2"</code>. However, |
| <code>"foo.bar1"</code> has no links to its parent <code>"foo"</code>. |
| |
| <p>In log4j, it is exactly the other way around. A log4j category |
| contains a link to its parent but a parent does not have links to its |
| children. |
| |
| <p>At first glance, this might look like a mundane implementation |
| detail but it is actually quite fundamental. |
| |
| <ol> |
| <p><b><li>Configuration order matters</b> |
| |
| <p>In JSR47, when you set the level of a logger, say |
| <code>wombat</code>, JSR47 traverses the tree below |
| <code>wombat</code>. In other words, the levels for all the loggers |
| descending from <code>wombat</code> are overwritten. This can be a |
| very expensive operation for large trees. In particular, for the most |
| common case where one sets the level of the root logger. However, |
| performance is not the point I am trying to make. |
| |
| <p>In log4j, changing the priority of a category involves the change |
| of a single field. Children categories dynamically inherit the |
| priority of their parent by traversing the hierarchy tree upwards. |
| |
| <p>It follows that with JSR47 if you configure the level for logger |
| "foo.bar1" before configuring the level for "foo", then the latter |
| instruction will overwrite the first exactly as if the first |
| instruction for configuring "foo.bar1" had never |
| existed. <em>Configuration order dependence is not a show stopper but |
| it is something that will bite you time and again.</em> |
| |
| <p>In contrast, in log4j categories can be configured in any |
| order. You never have to worry about configuration order. |
| |
| <p><b><li>Limited inheritance</b> |
| |
| <p>In JSR47, a logger does not walk the hierarchy to inherit its level |
| but possesses a copy of it. |
| |
| <p>Unfortunately, in the JSR47 API, handlers cannot be inherited |
| because it would be prohibitively expensive to let each logger to |
| contain a distinct Vector of all inherited handlers, especially in |
| large trees. |
| |
| <p>To circumvent this problem by JSR47 defines global handlers. A |
| logger logs to global handlers and to the handlers attached to itself |
| directly. <em>It does not inherit any handlers from the |
| hierarchy.</em> |
| |
| <p>In log4j, appenders are inherited additively from the hierarchy. A |
| category will log to the appenders attached to itself as well as the |
| appenders attached to its ancestors. This might not seem like much |
| until the day you need handler inheritance; probably a week after you |
| decide to adopt a logging API. |
| |
| <p>Similarly, in log4j resource bundles are inherited from the |
| hierarchy. In JSR47, a resource bundle <em>must</em> be attached to |
| each logger individually. There is no resource bundle inheritance in |
| JSR47. In practice, this means that you have to choose between |
| internationalization and the benefits of the named logger |
| hierarchy. It's one or the other. This limitation is particularly |
| surprising because support for internationalization is advocated as |
| one of the primary advantages of the JSR47 API. |
| |
| </ol> |
| |
| <h2>Bogus Levels</h2> |
| |
| <p>JSR 47 defines the levels <code>ALL</code>, <code>SEVERE</code>, |
| <code>WARNING</code>, <code>INFO</code>, <code>CONFIG</code>, |
| <code>FINE</code>, <code>FINER</code>, <code>FINEST</code> and |
| <code>OFF</code>. Experience shows that the levels <code>ALL</code> |
| and <code>OFF</code> are never needed. The <code>SEVERE</code> and |
| <code>CONFIG</code> levels are unique to JSR47. |
| |
| <p>Having three debugging levels <code>FINE</code>, |
| <code>FINER</code>, <code>FINEST</code> could seem like a good |
| idea. However, you will soon discover that even when by yourself, it |
| is hard to decide when to use which level. It is plain impossible in |
| groups. |
| |
| <p>Log4j in contrast has a limited but self-evident set of priorities: |
| <code>FATAL</code>, <code>ERROR</code>, <code>WARN</code>, |
| <code>INFO</code> and <code>DEBUG</code>. |
| |
| <p>Both JSR47 and log4j allow the user to extend the set of |
| priorities. Log4j supports subclasses of priorities in configuration |
| files as well as across the wire. JSR47 does not. |
| |
| <h2>Limited functionality</h2> |
| |
| <p>Log4j has appenders capable of logging to the console, to files, to |
| Unix Syslog daemons, to Microsoft NT EventLoggers, remote servers, to |
| JMS channels, automatically generate email etc. It can roll log files |
| by size or date and log asynchronously. |
| |
| <p>JSR47 can log to the console, to files, to sockets and to a memory |
| buffer. |
| |
| <p>Log4j has an extensible and powerful layout called the |
| <code>PatternLayout</code>. JSR47 offers the much weaker |
| <code>SimpleFormatter</code> as an alternative. |
| |
| <p>Log4j supports configuration through property files as well as XML |
| documents. JSR47 currently admits only property files. Moreover, the |
| language of JSR47 configuration files is very weak. In particular, you |
| can only configure one instance of a given handler class. <em>This |
| means that you can log to just one file at a time.</em> |
| |
| <h2>Other differences</h2> |
| |
| <p>There are many other details in which log4j differs from |
| JSR47. Even if the log4j core is small, the project contains a total |
| of over 30'000 lines of well-tested code. JSR47 contains about 5'000 |
| lines of code. |
| |
| <p>Log4j has been around for a number of years, enjoys the support of |
| five active developers (committers) and is being used in thousands of |
| projects. Our site gets over 500 downloads each and every day, and the |
| numbers are on the rise. Log4j has been ported to C++ and |
| Python. Companies are also offering commercial products extending |
| log4j. |
| |
| <p>Here is a short list of opensource projects or sites that are known |
| to use log4j. |
| |
| <ul> |
| <li><a href="http://www.cryptix.org/products/sasl/">Cryptix</a> |
| <li><a href="http://www.jcorporate.com/html/products/expresso/logging.html">Expresso Framework</a> |
| <li><a href="http://www.free-project.org/">Free E-Democracy Project</a> |
| <li><a href="http://java.freehep.org">FreeHEP</a> |
| <li><a href="http://www.jboss.org">JBoss</a> |
| |
| <li><a href="http://www.opensymphony.com/guidelines/logging.jsp">OpenSymphony</a> |
| <li><a href="http://theserverside.com">TheServerSide</a> |
| <li><a href="http://jakarta.apache.org/turbine/index.html">Turbine</a> |
| <li><a href="http://jakarta.apache.org/velocity/index.html">Velocity</a> |
| <li><a href="http://wired2.web.cern.ch/wired2/">WIRED</a> |
| |
| </ul> |
| |
| |
| <p>By the way, log4j runs fine under JDK 1.1 and above. JSR 47 will |
| run under JDK 1.4 and only under JDK 1.4. Interestingly enough, no |
| package shipped with JDK 1.4 is using the JSR47 API. |
| |
| <p>Brian R. Gilstrap has <A |
| href="http://javalogging.sourceforge.net/">re-written</a> JSR47 API to |
| run under JDK 1.2 and 1.3. He has also published an <a |
| href="http://www.ociweb.com/jnb/archive/jnbJun2001.html">article</a> |
| in JavaWorld. This is all very promising but since |
| <code>java.util.logging</code> is under the <code>java.*</code> |
| namespace, when running under JDK 1.3, you will systematically |
| encounter: |
| <pre> |
| Exception in thread "main" java.lang.ExceptionInInitializerError: java.lang.SecurityException: Prohibited package name: java.util.logging |
| at java.lang.ClassLoader.defineClass(ClassLoader.java:477) |
| at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:111) |
| at java.net.URLClassLoader.defineClass(URLClassLoader.java:248) |
| at java.net.URLClassLoader.access$100(URLClassLoader.java:56) |
| at java.net.URLClassLoader$1.run(URLClassLoader.java:195) |
| at java.security.AccessController.doPrivileged(Native Method) |
| at java.net.URLClassLoader.findClass(URLClassLoader.java:188) |
| at java.lang.ClassLoader.loadClass(ClassLoader.java:297) |
| at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:286) |
| at java.lang.ClassLoader.loadClass(ClassLoader.java:253) |
| at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313) |
| </pre> |
| |
| <p>Jochen Hiller had observed this problem in early 2001 when he |
| implemented the JSR47 API by wrapping log4j. |
| |
| <p>Note that any third-party implementation using the |
| <code>java.*</code> or <code>javax.*</code> namespaces violates Sun's |
| trademark on Java. Without explicit permission from Sun, such software |
| remains under the threat of litigation. |
| |
| <h2>Error Handling</h2> |
| |
| In JSR 47 when an error occurs then either a |
| <code>RunTimeException</code> is thrown to the user or (in handlers |
| only) an internal field is set. In the first case, the |
| <code>RunTimeException</code> will cause your application to crash. In |
| the latter case, you can retrieve the last caught exception in the |
| handler by querying the <code>getException</code> method of that |
| handler. <em>The former is totally unacceptable while the latter is |
| impractical.</em> |
| |
| <p>In log4j, under no circumstances are exceptions thrown at the |
| user. However, all appenders have an associated |
| <code>ErrorHandler</code>. This <code>ErrorHandler</code> is invoked |
| by the appender whenever a handler-specific error occurs. By default, |
| log4j appenders are associated with an |
| <code>OnlyOnceErrorHandler</code> which emits a message on the console |
| for the first error in an appender and ignoring all following errors. |
| |
| <p>An <code>ErrorHandler</code> can implement an arbitrary error |
| handling policy. For example, after a failure to write to a database a |
| <code>JDBCAppender</code> can be redirected to fall back on a |
| <code>FileAppender</code>. This functionality is supported in XML |
| configuration files. You do not need to change a single line of client |
| code. |
| |
| <p>But again who cares about errors, right? |
| |
| <h2>Performance</h2> |
| |
| <p>Logging performance must be studied in three distinct cases: when |
| logging is turned off, when turned on but due to priority comparison |
| logic not enabled, and when actually logging. Please refer to the <a |
| href="manual.html#performance">log4j manual</a> for a more detailed |
| discussion of logging performance. |
| |
| <p>When logging is turned on, log4j will be about two to three times |
| slower to decide whether a log statement is enabled or not. This is |
| due to the dynamic nature of log4j which requires it to walk the |
| hierarchy. To give you an idea about the figures involved, under JDK |
| 1.4 beta, we are talking about 90 <em>nanoseconds</em> instead of 30 |
| <em>nanoseconds</em> on a 800Mhz Intel processor. In other words, one |
| million disabled logging requests will cost under a second in both |
| environments. |
| |
| <p>In a shipped binary, you can turn off logging entirely and both |
| APIs will perform identically. Note that if one is not careful, the |
| cost of parameter construction before invoking a disabled log |
| statement will overwhelm any other performance consideration. |
| Regardless of the API you decide to use, logging statements should |
| never be placed in tight loops, for example, before or after an |
| element swap instruction in a sort algorithm. |
| |
| <p>In log4j, caller localization information is optional whereas in |
| JSR47 it is always extracted. Since the extraction of caller |
| localization is a very slow operation, in the common case where caller |
| information is not needed, log4j will log the same information 4 to |
| 100 times faster. |
| |
| |
| </body> |
| </HTML> |