Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Logging: Log4j Version2.x: show the method of an end-client caller (not an intermediate logging helper method)

The following 3 posts offer answers for how to use an intermediate logging helper and still get the underlying logger to report from the method of a client to that logging helper (rather than reporting the logging helper method as the source):

  • Java Logging: show the source line number of the caller (not the logging helper method)

  • Calling log4j's log methods indirectly (from a helper method)

  • Printing the "source" class in a log statement with a log4j wrapper

But the seem to only offer answers for Log4j 1.2, which offers the now-defunct:

 Category.log(String callerFQCN, Priority level, Object message, Throwable t). 

There does not seem to be an obvious equivalent for Logger in the log4J 2.5 API.

Can anybody offer an answer compatible with direct use of Log4J 2.x ?

like image 412
Webel IT Australia - upvoter Avatar asked Feb 12 '16 10:02

Webel IT Australia - upvoter


People also ask

How does log4j logging work?

Log4j allows logged messages to contain format strings that reference external information through the Java Naming and Directory Interface (JNDI). This allows information to be remotely retrieved across a variety of protocols, including the Lightweight Directory Access Protocol (LDAP).

What is the difference between log4j and log4j2?

Community support: Log4j 1. x is not actively maintained, whereas Log4j 2 has an active community where questions are answered, features are added and bugs are fixed. Automatically reload its configuration upon modification without losing log events while reconfiguring.

Does Java Util logging use log4j?

The JDK Logging Adapter is a custom implementation of java. util. logging. LogManager that uses Log4j.

Which logging framework is best for Java?

One of the most popular solutions for the Java world is the Apache Log4j 2 framework.


1 Answers

For Log4j2 the answer is provided completely by the use of logger wrappers as described in the Log4j2 manual under Example Usage of a Generated Logger Wrapper. One can simply generate (using the org.apache.logging.log4j.core.tools.Generate$ExtendedLogger tools illustrated there) a logger wrapper with a single STUB level, and then adapt that to create custom logging methods mimicking the use of the logIfEnabled(FQCN, LEVEL, Marker, message, Throwable) - possibly ignoring the STUB level and using the regular ones - then if desired, deleting or commenting out the STUB level and its methods). For this purpose the FormattedMessage can be helpful.

Example:

java -cp log4j-core-2.5.jar org.apache.logging.log4j.core.tools.Generate\$ExtendedLogger com.mycomp.ExtLogger STUB=350 > com/mycomp/ExtLogger.java

Then adapt the generated class (most support methods omitted):

public final class ExtLogger extends ExtendedLoggerWrapper {
    ...
    private final ExtendedLoggerWrapper logger;

    private static final String FQCN = ExtLogger.class.getName();
    private static final Level STUB = Level.forName("STUB", 350);
    //Delete this afterwards if level not used.

    private ExtLogger(final Logger logger) {
        super((AbstractLogger) logger, logger.getName(), logger.getMessageFactory());
        this.logger = this;
    }

    /**
     * Returns a custom Logger with the name of the calling class.
     * 
     * @return The custom Logger for the calling class.
     */
    public static ExtLogger create() {
        final Logger wrapped = LogManager.getLogger();
        return new ExtLogger(wrapped);
    }

    /**
     * Returns a custom Logger using the fully qualified name of the Class as
     * the Logger name.
     * 
     * @param loggerName The Class whose name should be used as the Logger name.
     *            If null it will default to the calling class.
     * @return The custom Logger.
     */
    public static ExtLogger create(final Class<?> loggerName) {
        final Logger wrapped = LogManager.getLogger(loggerName);
        return new ExtLogger(wrapped);
    }

    ...

    /**
     * Logs a message object with the {@code STUB} level.
     * 
     * @param message the message object to log.
     */
    public void stub(final String message) {
        logger.logIfEnabled(FQCN, STUB, null, message, (Throwable) null);
    }


    /**
     * Example: Adapt with custom formatting.
     * Here DEBUG level is used just as an example.
     *
     * @param name
     * @param value 
     */
    public void echo(final String name, Object value) {
        Message m = new FormattedMessage("echo: %s(%s)",name,value);
        logger.logIfEnabled(FQCN, Level.DEBUG, null, m, (Throwable) null);
    }
    ...
}

Then in a client class it will now log "on behalf" of that client correctly via the logger's helper methods, in this case the formatting example echo(name,value):

public class TestLog4j {

    private static final ExtLogger extLogger = ExtLogger.create(TestLog4j.class);

    public static void elseWhere() {
        extLogger.echo("aVariableName", 4);
    }

    public static void main(String[] args) {
            extLogger.echo("aStringVariableName","from main");
            elseWhere();
    }
}

Simple PatternLayout:

  <PatternLayout pattern=" %-5level [%C{1}::%M(%L)] %logger{36} - %msg%n"/>

Output:

 DEBUG [TestLog4j::main(63)] testlogging.TestLog4j - echo: aStringVariableName(from main)
 DEBUG [TestLog4j::elseWhere(42)] testlogging.TestLog4j - echo: aVariableName(4)

Once you've got the hang of using logger.logIfEnabled(FQCN,...) with the FQCN (which log4j searches for in the stack trace) you may wish to delete or comment out the stub(..) methods and STUB level if you don't use an additional level.

like image 160
Webel IT Australia - upvoter Avatar answered Oct 11 '22 16:10

Webel IT Australia - upvoter