Preparing for log4j version 1.3

Ceki Gülcü, October 2004, © All rights reserved

Logger replaces Category

Around the time JDK 1.4 was released, it was decided that log4j would rename its Category class as Logger. We of course wanted to preserve compatibility with existing client code, so several migration strategies were studied.

The obvious strategy of Category extending Logger was not an option because it could not guarantee compatibility with existing code invoking log4j methods returning a Category instance. For example,

Category cat = Category.getInstance();

would either not compile or throw a ClassCastException because if Category extends Logger, then an instance of Logger is not an instance of Category.

Nicholas Wolff suggested a less intuitive alternative whereby Logger would extend Category and we would make sure that log4j only produced Logger instances. This is the alternative we adopted.

There are two inconveniences with Nicholas' method. First, since Logger extends Category, Category could not be marked as @deprecated because that would cause derived classes, in particular the Logger class to also generate deprecation warnings rendering the whole exercise rather pointless and even counterproductive. Second, although Nicholas' strategy solves compile time issues, it still leaves room for binary (or runtime) compatibility problems. These problems are fortunately rare and are being addressed on a case by case basis..

As of log4j version 1.2, users should never refer to the Category class directly, but use the Logger class instead.

In log4j 1.2, released on May 2002, that is almost 3 years ago, the Category class has been advertised as being deprecated. It has been replaced by the Logger class. As of log4j version 1.2, users should never refer to the Category class directly, but use the Logger class instead as advertised in the Category class javadocs.

For the reasons mentioned previously, in log4j version 1.2.x, the Category class was not tagged with the @deprecated javadoc tag in the source code. This voluntary omission led to a situation where users could still refer to the Category class without deprecation warnings being generated by the java compiler. Consequently, some users unfortunately still continue to refer to the Category class.

We have marked in red and in bold that users should drop references to the Category class and use Logger instead. However, us shouting about this important point does not tell the java compiler to enforce it. Since we cannot mark the Category class as @deprecated in the javadocs, the only compiler-enforced way of pushing users to migrate to the Logger class is to tag those methods in the Category class that have Category as their return value in their signature. These methods are:

Unfortunately, log4j versions up to and including 1.2.8 did not mark these methods as deprecated. However, log4j 1.2.9 will mark them as deprecated. Note that the release of log4j 1.2.9 has not been yet been approved by the PMC of the Logging Services project.

Assuming log4j version 1.2.9 is approved, then we can assume that users of 1.2.9 will only refer to the Logger class. If that is the case, their code will compile and run without change with log4j 1.3 even when the Category class is removed.

Level replaces Priority

As of log4j version 1.2, users should never refer to the Priority class directly, but use the Level class instead.

In log4j 1.2, the Level class replaced the Priority class. We adopted the same migration strategy as for the Category/Logger case. In other words, the Level class extends the Priority class. The client code should not use or refer to the Priority class but use Level class instead.

Since the Priority class could not be tagged as deprecated, we deprecated all the methods returning a Priority instance. These are:

Moreover, all the static member fields of type Priority in the Priority class have been deprecated. They have been replaced by equivalent fields of the same name in the Logger class. These deprecated fields in the Priority class are listed below:

Appender instances must be activated before use

For all appenders except trivial ones, the activateOptions() method must be called before an appender can be used. This is true for both log4j versions 1.2 as well 1.3.

However, in log4j 1.3, this contract is enforced by run-time checks. Thus, if you forget to call activateOptions() method, AppenderSkeleton will refuse to invoke the append() method of its derived classes.

Users who programmatically instantiate appenders, even appenders maintained outside the log4j project, need to call the activateOptions() method after they instantiate and set its options. This will ensures run-time compatibility for log4j 1.2 as well as 1.3.

Recipe for preparing your code for log4j 1.3

Even if you are still using log4j version 1.2.x, we highly recommend that you follow the following recipe to prepare your code for 1.3, the next version of log4j.

  1. Never refer to the Category class directly, refer to Logger instead.
  2. Do not invoke the deprecated Category.getRoot method. Invoke Logger.getRootLogger method instead.

  3. Do not invoke the deprecated Category.getInstance(String) method. Invoke Logger.getLogger(String) method instead.

  4. Do not invoke the deprecated Category.getInstance(Class) method. Invoke Logger.getLogger(Class) method instead.
  5. Never refer to the Priority class, refer to Level class instead.

  6. Do not invoke the deprecated Category.setPriority(Priority) method. Invoke Logger.setLevel(Level) method instead.

  7. Do not invoke the deprecated Priority.toPriority(int) method. Invoke Level.toLevel(int) method instead. The same holds true for the other variants of the Priority.toPriority method.

  8. Never refer to the deprecated Priority.FATAL, Priority.ERROR, Priority.WARN, Priority.INFO, Priority.DEBUG fields. Refer to the Level.FATAL, Level.ERROR, Level.WARN, Level.INFO, Level.DEBUG fields instead.

  9. If you confiugure appenders programmatically, do not forget to invoke the activateOptions method of an appender after you have instantiated it and set its options.

Thus, instead of writing:

Category root = Category.getRoot();
root.debug("hello");

Category cat = Category.getInstance(Some.class);
cat.debug("hello");

cat.setPriority(Priority.INFO);

do write:

Logger root = Logger.getRootLogger();
root.debug("hello");

Logger logger = Logger.getLogger(Some.class);
logger.debug("hello");

logger.setLevel(LEVEL.INFO);

For 99.99% of users, this translates to the following string find-and-replace operations:

  1. Replace the string "Category.getInstance" with the string "Logger.getLogger".
  2. Replace the string "Category.getRoot" with the string "Logger.getRootLogger".
  3. Replace the string "Category" with the string "Logger".
  4. Replace the string "Priority" with the string "Level".