8

I want to retrofit slf4j with Logback into a legacy application. Good thing is, the legacy application has its own logging framework. So all I had to do is alter the logging framework to log to slf4j instead of log4j.

It worked like a dream. I was happy, until I noticed the location Logback logged for each and every log event:

Logger.java:...

Yikes! That wasn't going to help my fellow developers much when trying to figure out where a log event came from.

How can I tell Logback to look a few levels up in the stack for the actual location to log?

The logger class is a utility class with methods like this:

public static void debug(String clazz, String message) {
    org.slf4j.Logger logger = LoggerFactory.getLogger(clazz);
    logger.debug(message);
}
Stijn Van Bael
  • 4,830
  • 2
  • 32
  • 38
  • 1
    similar question: http://stackoverflow.com/questions/1486233/java-logging-show-the-source-line-number-of-the-caller-not-the-logging-helper-m – Thilo Aug 16 '10 at 09:05
  • did this work when it was using log4j? I'd think since you updated an existing logging class, you did not add an extra frame to the call stack, so the log4j version should have exhibited the same problem. – Thilo Aug 16 '10 at 09:07
  • 1
    log4j exposes a log method in its public API that looks an extra frame up the stack while slf4j does not. So yes, it worked with log4j. – Stijn Van Bael Aug 16 '10 at 09:24

2 Answers2

13

Found the solution looking at the source of jcl-over-slf4j. Most implementations of slf4j (including logback) use loggers that implement LocationAwareLogger, which has a log method that expects the fully qualified class name of the wrapping logger class as one of it's arguments:

private static final String FQCN = Logger.class.getName();


public static void debug(String clazz, String message) {
    org.slf4j.Logger logger = LoggerFactory.getLogger(clazz);
    if (logger instanceof LocationAwareLogger) {
        ((LocationAwareLogger) logger).log(null, FQCN, LocationAwareLogger.DEBUG_INT, message, null, null);
    } else {
        logger.debug(message);
    }
}
Stijn Van Bael
  • 4,830
  • 2
  • 32
  • 38
  • 1
    Great answer. I was looking for a solution for the log4j binding and found the Log4jLoggerAdapter. Too bad the wrapper does not support message formatting. – b0gusb Nov 02 '18 at 08:30
5

See the various XXX-over-slf4j implementations for how to do it.

Basically you want to replace your current logger framework completely. Not wrap slf4j.

Edit:

Another approach could be writing your own layout subclassing the one you use now, which has a revised meaning of the %m, %l etc fields which skips the extra stack frame.

Thorbjørn Ravn Andersen
  • 73,784
  • 33
  • 194
  • 347
  • 2
    I considered that solution, but currently there is no budget to replace 10000+ log statements scattered over 50+ projects. – Stijn Van Bael Aug 16 '10 at 09:17
  • You do not need to change the log statements, but the code it calls (which hopefully is in a library) – Thorbjørn Ravn Andersen Aug 16 '10 at 09:24
  • 1
    The logger framework has 10000+ method calls from all projects to it. So if I replaced the logger framework, wouldn't I have to replace all method calls to it as well? – Stijn Van Bael Aug 16 '10 at 09:35
  • 2
    If you replace the logger framework with another which behaves identically (provides the same interfaces, classes and methods) but instead behaves like one of the other emulator frameworks for slf4j it should give you the desired behaviour. Otherwise create your own layout - see updated answer. – Thorbjørn Ravn Andersen Aug 16 '10 at 09:43