In the forthcoming, still unpublished draft of JSR47, the
java.util.logging
API will resemble log4j even more than
is the case now. The way the two APIs name their components may
differ but otherwise their degree of resemblance is quite striking.
Changes introduced in the latest draft include configuration order independence, appender inheritance, resource bundle inheritance, error handlers and lazy inference of caller information. In other words, even if the priority levels remain unchanged and equally bogus, the majority of the points raised in my previous critique of JSR47 are now obsolete.
Consequently, it is fair to say that our campaign to influence the JSR47 API handsomely bore fruit. I wish to thank the hundreds of concerned users who have expressed their support for log4j. My gratitude goes to Jason Hunter for arranging the appropriate communication channel to Sun. Graham Hamilton, the JSR47 specification lead, was very open and receptive during our exchanges.
As one of its authors, I still think that at log4j core API is better in some small ways. These differences are explained in the next section.
There remain two critical differences however. First, JSR47 requires JDK 1.4 whereas log4j is compatible with JDK 1.1 and later. Second, log4j offers much more functionality. It supports a rich configuration language, at least a dozen appenders and layouts as well as many other useful features.
Efforts to backport JSR47 to earlier JDKs are doomed to fail
because the java.util.logging
package is located under
the java
namespace. This will cause backported code to
systematically throw a SecurityException
under JDK
1.3. Moreover, Java is a trademark owned by Sun Microsystems. As such,
the backported code will be under the threat of litigation as long as
Sun can be expected to defend its trademark.
Even without the SecurityException
and the trademark
issue, you would need an additional logging library to fill in the
functionality gap, the JDK compatibility gap, or both. If you are
going to install one or more logging APIs, then why not install log4j
which offers a lot more than JSR47 and is backward compatible with JDK
1.1?
Log4j is the de facto standard logging API in Java. It has been ported to Python, C++ and the much maligned C#. By adopting log4j, you simultaneously benefit from much richer functionality and wider JDK compatibility.
In any case, the log4j project will continue to innovate and lead the way. As such, many of the features that you need or will need in the future will be first available in log4j.
As in Linux ipchains, log4j filters use ternary logic. In JSR47, filter logic is binary. Mathematically the two logics are equivalent except that it is much easier to combine generic filters in log4j's ternary logic than in JSR47's binary logic. For example, if you would like to reject a log message if it contains the string "Microsoft" or the string "proprietary code", accept messages having the info priority and only the info priority and reject everything else, you would write:
<filter class="org.apache.log4j.varia.StringMatchFilter"> <param name="StringToMatch" value="Microsoft" /> <param name="AcceptOnMatch" value="false" /> </filter> <filter class="org.apache.log4j.varia.StringMatchFilter"> <param name="StringToMatch" value="proprietary code" /> <param name="AcceptOnMatch" value="false" /> </filter> <filter class="org.apache.log4j.varia.PriorityMatchFilter"> <param name="PriorityToMatch" value="INFO" /> <param name="AcceptOnMatch" value="true" /> </filter> <filter class="org.apache.log4j.varia.DenyAllFilter"/>You cannot express this policy with JSR47 filters without writing a filter for exactly this policy.
JSR47 allows filters to be attached to loggers (categories in log4j speak) and also to handlers (appenders in in log4j speak). Log4j allows filters to be attached to appenders but not to categories.
In short, attaching filters to loggers is a feature that JSR47 offers but log4j does not. However, because arbitrary logic cannot be meaningfully composed, filters cannot be inherited. Thus, you would need to attach a filter to every single logger where you would like it to apply. A clear waste of your time.
JSR 47 defines the levels ALL
, SEVERE
,
WARNING
, INFO
, CONFIG
,
FINE
, FINER
, FINEST
and
OFF
.
Having three debugging levels FINE
,
FINER
, FINEST
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.
Arguing about priority levels is a bit like arguing about your favorite color. I will thus stop here.
The set of printing methods in the Logger
class is
confusing. It is all there -- just not where you would expect them to
be. For example, you cannot log an exception with the
warning
or severe
methods. You will need to
use the log
method instead.
JSR47 emits sequence numbers in each log record it creates. The
sequence number is synchronized using the LogRecord
class
itself at the cost of a small but measurable performance penalty. This
variable is not likely to be very meaningful if the logging output is
split between different handlers as the output of each handler will
contain holes in the sequence numbers.
Err... not exactly a useful feature nor consistent with the rest of the JSR47 API.