我遇到一种情况,当调用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
...
在记录的同时,我还希望通过另一个代码路径发送异常。
我该怎么做?我有点卡住,有人知道这方面有什么好的指南吗?
答案 0 :(得分:1)
我不认为您真的要在这里Appender
。改写Filter
会更简单。作为参考,您可以在Extending Log4j2 page of the manual
在下面的示例中,我创建了一个简单的过滤器,该过滤器在日志事件具有关联的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/>
希望这对您有所帮助。