如何编写仅处理异常并且仍然​​通过root ConsoleAppender正常记录所有其他日志的附加程序

时间:2019-03-05 21:46:59

标签: log4j2

我遇到一种情况,当调用log.error("message", exception);时,我希望在将异常发送到外部工具的同时发生一些逻辑,同时仍保持对到根附加程序的行的常规日志记录。

例如,下面是一些代码和预期结果:

try {
   ...
} catch (Exception ex) {
   LOG.info("abcd");
   LOG.error("failed to XYZ", ex);
}

粗略结果:

2019-03-05 13:00:20 INFO  Main:75 - abcd
2019-03-05 13:00:20 ERROR  Main:76 - failed to XYZ - 
Exception: exception message
  stacktrace
  stacktrace
  ...

在记录的同时,我还希望通过另一个代码路径发送异常。

我该怎么做?我有点卡住,有人知道这方面有什么好的指南吗?

1 个答案:

答案 0 :(得分:1)

我不认为您真的要在这里Appender。改写Filter会更简单。作为参考,您可以在Extending Log4j2 page of the manual

上找到有关为log4j2创建扩展名的信息。

在下面的示例中,我创建了一个简单的过滤器,该过滤器在日志事件具有关联的Throwable时匹配,而在没有Throwable时不匹配(即Throwable为null或该日志是通过不包含Throwable参数的方法调用生成的)。

在该示例中,我将所有匹配的日志事件发送到一个简单的文件追加器,以说明实际上它仅捕获带有Throwable的事件。您可以修改此过滤器以执行所需的任何操作。您可以将其更改为始终对每个事件都是NEUTRAL,但是当发现非空Throwable时,它将触发一些特殊的逻辑将该Throwable发送到您的外部工具。基本上,您将过滤器更多地用作拦截器。我将在最后描述这种变化。

首先,这是一些基本的Java代码,用于生成一些日志记录,包括具有Throwable的日志事件。

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class SomeClass {

    private static final Logger log = LogManager.getLogger();   

    public static void main(String[] args){

        if(log.isDebugEnabled())
            log.debug("This is some debug!");
        log.info("Here's some info!");
        log.error("Some error happened!");

        try{
            specialLogic();
        }catch(RuntimeException e){
            log.error("Woops, an exception was detected.", e);
        }
    }

    public static void specialLogic(){
        throw new RuntimeException("Hey an exception happened! Oh no!");
    }
}

接下来,这是我称为ThrowableFilter的班级:

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.apache.logging.log4j.message.Message;

@Plugin(name = "ThrowableFilter", category = "Core", elementType = "filter", printObject = true)
public final class ThrowableFilter extends AbstractFilter {


    private ThrowableFilter(Result onMatch, Result onMismatch) {
        super(onMatch, onMismatch);
    }

    public Result filter(Logger logger, Level level, Marker marker, String msg, Object[] params) {
        return onMismatch;
    }

    public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
        return filter(t);
    }

    public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
        return filter(t);
    }

    @Override
    public Result filter(LogEvent event) {
        return filter(event.getThrown());
    }

    private Result filter(Throwable t) {
        return t != null ? onMatch : onMismatch;
    }

    /**
     * Create a ThrowableFilter.
     * @param match The action to take on a match.
     * @param mismatch The action to take on a mismatch.
     * @return The created ThrowableFilter.
     */
    @PluginFactory
    public static ThrowableFilter createFilter(@PluginAttribute(value = "onMatch", defaultString = "NEUTRAL") Result onMatch,
                                               @PluginAttribute(value = "onMismatch", defaultString = "DENY") Result onMismatch) {
        return new ThrowableFilter(onMatch, onMismatch);
    }
}

最后,这是我用来测试它的log4j2.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </Console>

        <File name="ExceptionFile" fileName="logs/exception.log" immediateFlush="true"
            append="true">
            <ThrowableFilter onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout
                pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </File>
    </Appenders>

    <Loggers>
        <Root level="debug">
            <AppenderRef ref="Console" />
            <AppenderRef ref="ExceptionFile" />
        </Root>
    </Loggers>
</Configuration>

运行SomeClass中的逻辑将产生以下输出:

在控制台上:

23:23:25.931 [main] DEBUG example.SomeClass - This is some debug!
23:23:25.946 [main] INFO  example.SomeClass - Here's some info!
23:23:25.946 [main] ERROR example.SomeClass - Some error happened!
23:23:25.946 [main] ERROR example.SomeClass - Woops, an exception was detected.
java.lang.RuntimeException: Hey an exception happened! Oh no!
    at example.SomeClass.specialLogic(SomeClass.java:25) ~[classes/:?]
    at example.SomeClass.main(SomeClass.java:18) [classes/:?]

在logs / exception.log文件中:

2019-03-06 23:23:25.946 [main] ERROR example.SomeClass - Woops, an exception was detected.
java.lang.RuntimeException: Hey an exception happened! Oh no!
    at example.SomeClass.specialLogic(SomeClass.java:25) ~[classes/:?]
    at example.SomeClass.main(SomeClass.java:18) [classes/:?]

现在要更改过滤器以使其更多地充当拦截器,您可以更改以下方法:

//Remove parameters from constructor as they will not be used.
private ThrowableFilter() {
    super();
}
...

public Result filter(Logger logger, Level level, Marker marker, String msg, Object[] params) {
    //Changed to always return NEUTRAL result
    return Result.NEUTRAL;
    //old logic: return onMismatch;
}

...

private Result filter(Throwable t) {
    //TODO: trigger the external tool here when t != null, pass t if needed.

    //Changed to always return NEUTRAL result
    return Result.NEUTRAL;
    //old logic: return t != null ? onMatch : onMismatch;
}

/**
 * Create a ThrowableFilter.
 * @return The created ThrowableFilter.
 */
@PluginFactory
public static ThrowableFilter createFilter() {
    return new ThrowableFilter();
}

然后在配置中,将参数删除到过滤器中。现在看起来像这样:

<ThrowableFilter/>

希望这对您有所帮助。