JDK-6569068 : Make java.util.logging.LogRecord easier to extend
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.util.logging
  • Affected Version: 6
  • Priority: P5
  • Status: Open
  • Resolution: Unresolved
  • OS: linux
  • CPU: x86
  • Submitted: 2007-06-13
  • Updated: 2012-01-24
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Description
A DESCRIPTION OF THE REQUEST :
The request could take one of several forms:

1. Make LogRecord.inferCaller protected (it's currently private.) 2. Provide an api change to LogRecord to support a parameterized version of inferCaller; the parameter representing the String "java.util.logging.Logger"  inside that method. The parameter wouldn't need to be a String, it would be better if it were a class.

JUSTIFICATION :
When logging a message using the traditional logger, say, like this,

public class SampleApp {
public static void main(String[] args) {
  Logger logger = Logger.getLogger("foo");
  logger.info ("Hello");
}
}

here's what the output looks like:

Jun 12, 2007 2:53:00 PM SampleApp main
INFO: Hello
--
Notice that it displays the method name that is the call to logger.

It does this by walking up the stack trace of an exception generated in java.util.logging.LogRecord.inferCaller. A sample stack trace may look like this (and in this case, the call to the logger API comes from Database.initialize()):

1 MyApp.main()
2 MyApp.run()
3 MyApp.initalize()
4 Database.initialize()
5 Logger.info()
6 Logger.log()
7 Logger.doLog()
8 Logger.log()
9 ConsoleHandler.publish()
...
n LogRecord.inferCaller().

  To display "Database.initialize" as the source of the log message, it searches up the stack trace, starting from the nth element, to find a StackTraceElement from java.util.logging.Logger (found on line 8). Then it moves up the stack trace to find the first element *not* from java.util.logging.Logger(found on line 4.) And so, the 'caller' is Database.initialize.

Okay. Now let's say that you want to write a logging API that proxies calls to java.util.logging.Logger. (Let's call it MyLogger.) Not necessarily *extend* logger, but just provide a facade or adapter -- the key is that you encapsulate the Logger object and provide your own API. (Let's say that you want to provide some sort of feature that java.util.logging.Logger doesn't provide (shock!))

In this case, what does the call stack look like?

...
3 MyApp.initalize()
4 Database.initialize()
5 MyLogger.log()
6 Logger.info()
7 Logger.log ()
...

What will the logger infer as the caller for all my logging messages? MyLogger.log() of course! That's just wrong. The actual caller is still Database.initialize.

Now, this is what happened with a custom Logging API I wrote.

What I had to do was extend LogRecord, and provide my own implementation for a variety of methods, including providing similar behavior to inferCaller. Since inferCaller is private, I wasn't overriding it, which means that I couldn't just write

class MyRecord extends LogRecord() {
  protected void inferCaller() {
  }
}

but provide implementations for public or protected methods that call inferCaller.

This wouldn't be a problem if LogRecord was designed for extension.

Note that if Logger were designed for extension, this would also help, but that's another thought.


---------- BEGIN SOURCE ----------
import java.util.logging.*;

public class LoggerExample {
  static class MyLogger {
    Logger logger;
    public MyLogger(String name) {
      this.logger = Logger.getLogger(name);
    }
    public void log(Level level, String fmt, Object... args) {
      if (logger.isLoggable(level)) {
        logger.log(level, String.format(fmt ,args));
      }
    }
  }

  static MyLogger logger = new MyLogger("LoggerExample");
  public LoggerExample() {
    logger.log(Level.INFO, "Starting %s", LoggerExample.class.getName());
  }

  public void foo(int x) {
    logger.log(Level.INFO, "foo: %d", x);
  }
  
  public static void main(String[] args) {
    new LoggerExample().foo(123);
  }
}

// Output from MyLogger always references MyLogger as the caller. e.g.

Jun 12, 2007 11:28:08 PM LoggerExample$MyLogger log
INFO: Starting LoggerExample
Jun 12, 2007 11:28:09 PM LoggerExample$MyLogger log
INFO: foo: 123

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Extend LogRecord as demonstrated in the Justification.