Java Logging: Log4j Version2.x: show the method of

2019-01-26 09:27发布

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):

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 ?

2条回答
仙女界的扛把子
2楼-- · 2019-01-26 10:01

If you are building a new system, stick with Webel's answer.

If you have an existing system you are migrating to log4j2, you should still probably run the generate methods (but I include a minimal working class below), and you can add this function which provides the old 1.2 callerFQCN way of doing things (sort of):

public void log(Class ignoreClassFQCN, Level level, Marker marker, String msg, Throwable throwable){    
    logger.logIfEnabled(ignoreClassFQCN.getName(), level, marker, msg, throwable);
}

Then, from your existing log-wrapping classes, you can do something like this:

// inside ThisClass.java, denoting an old logger such as one that used log4j 1.2 with the callerFQCN parameter, or just an old logger that's naive and custom built.
private static final MyLog4j2WrapperClass newLogger = MyLog4j2WrapperClass.create();

public static void debug(String message) {
    // basic example
    newLogger.log(ThisClass.class, Level.DEBUG, null, message, null);
}

public static void logFailure(String message) {
    // example of using a custom log level
    newLogger.log(ThisClass.class, MyLog4j2WrapperClass.FAILURE, null, message, null);
}

I happened to delete a bunch of other generated functions I am not using, since I planned to do a simple shim for a poorly designed (custom) logging system. I deleted a bunch of create() methods I didn't plan to use. So here is a working class (the parts worth sharing):

public final class MyLog4j2WrapperClass extends ExtendedLoggerWrapper {
    private static final long serialVersionUID = 1L;
    private final ExtendedLoggerWrapper logger;

    private static final String FQCN = MyLog4j2WrapperClass.class.getName();
    public static final Level FAILURE = Level.forName("FAILURE", 150);

    public void log(Class ignoreClass, Level level, Marker marker, String msg, Throwable throwable){
        logger.logIfEnabled(ignoreClass.getName(), level, marker, msg, throwable);
    }

    private MyLog4j2WrapperClass(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 MyLog4j2WrapperClass create() {
        final Logger wrapped = LogManager.getLogger();
        return new MyLog4j2WrapperClass(wrapped);
    }

I should note that I consider this a shim--it's something to get you by, but I would strongly recommend continuing to move away from the old logging system once you have the shim in place. This will let you do more incremental code changes without having to accomplish a full migration right away.

查看更多
我只想做你的唯一
3楼-- · 2019-01-26 10:10

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.

查看更多
登录 后发表回答