如何在log4j2中创建自定义追加器?
正如在此链接中讨论的那样:如何在log4j中创建自己的追加器?
为了在log4j 1.x中创建自定义追加器,我们必须扩展 AppenderSkeleton 类并实现其 append 方法。
类似地,我们如何在log4j2中创建自定义追加器,因为我们没有ExpenderSkelton类来扩展和所有其他追加器扩展 AppenderBase类。
正如在此链接中讨论的那样:如何在log4j中创建自己的追加器?
为了在log4j 1.x中创建自定义追加器,我们必须扩展 AppenderSkeleton 类并实现其 append 方法。
类似地,我们如何在log4j2中创建自定义追加器,因为我们没有ExpenderSkelton类来扩展和所有其他追加器扩展 AppenderBase类。
这在 log4j2 中的工作方式与在 log4j-1.2 中的工作方式完全不同。
在log4j2中,您将为此创建一个插件。该手册有一个解释,其中包含自定义附加器的示例:http://logging.apache.org/log4j/2.x/manual/extending.html#Appenders
扩展可能很方便,但这不是必需的。org.apache.logging.log4j.core.appender.AbstractAppender
当您使用 注释自定义 Appender 类时,插件名称将成为配置元素名称,因此使用自定义追加器的配置将如下所示:@Plugin(name="MyCustomAppender", ....
<Configuration packages="com.yourcompany.yourcustomappenderpackage">
<Appenders>
<MyCustomAppender name="ABC" otherAttribute="...">
...
</Appenders>
<Loggers><Root><AppenderRef ref="ABC" /></Root></Loggers>
</Configuration>
请注意,配置上的属性是具有自定义log4j2插件的所有软件包的逗号分隔列表。Log4j2 将在类路径中搜索这些包,以查找带有 @Plugin 注释的类。packages
下面是打印到控制台的示例自定义追加器:
package com.yourcompany.yourcustomappenderpackage;
import java.io.Serializable;
import java.util.concurrent.locks.*;
import org.apache.logging.log4j.core.*;
import org.apache.logging.log4j.core.config.plugins.*;
import org.apache.logging.log4j.core.layout.PatternLayout;
// note: class name need not match the @Plugin name.
@Plugin(name="MyCustomAppender", category="Core", elementType="appender", printObject=true)
public final class MyCustomAppenderImpl extends AbstractAppender {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
protected MyCustomAppenderImpl(String name, Filter filter,
Layout<? extends Serializable> layout, final boolean ignoreExceptions) {
super(name, filter, layout, ignoreExceptions);
}
// The append method is where the appender does the work.
// Given a log event, you are free to do with it what you want.
// This example demonstrates:
// 1. Concurrency: this method may be called by multiple threads concurrently
// 2. How to use layouts
// 3. Error handling
@Override
public void append(LogEvent event) {
readLock.lock();
try {
final byte[] bytes = getLayout().toByteArray(event);
System.out.write(bytes);
} catch (Exception ex) {
if (!ignoreExceptions()) {
throw new AppenderLoggingException(ex);
}
} finally {
readLock.unlock();
}
}
// Your custom appender needs to declare a factory method
// annotated with `@PluginFactory`. Log4j will parse the configuration
// and call this factory method to construct an appender instance with
// the configured attributes.
@PluginFactory
public static MyCustomAppenderImpl createAppender(
@PluginAttribute("name") String name,
@PluginElement("Layout") Layout<? extends Serializable> layout,
@PluginElement("Filter") final Filter filter,
@PluginAttribute("otherAttribute") String otherAttribute) {
if (name == null) {
LOGGER.error("No name provided for MyCustomAppenderImpl");
return null;
}
if (layout == null) {
layout = PatternLayout.createDefaultLayout();
}
return new MyCustomAppenderImpl(name, filter, layout, true);
}
}
有关插件的更多详细信息:http://logging.apache.org/log4j/2.x/manual/plugins.html
如果手册还不够,查看log4j-core中内置附加器的源代码可能会很有用。
正如您所指出的,AppenderSkeleton不再可用,因此如何在log4j中创建我自己的追加器?中的解决方案将不起作用。
如果您期望多个日志记录消息,则使用 Mockito 或类似库创建具有 ArgumentCaptor 的追加器将不起作用,因为 MutableLogEvent 在多个日志消息上重复使用。
我为log4j2找到的最通用的解决方案是提供一个记录所有消息的模拟实现。它不需要任何额外的库,如Mockito或JMockit。
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.appender.AbstractAppender;
private static MockedAppender mockedAppender;
private static Logger logger;
@Before
public void setup() {
mockedAppender.message.clear();
}
/**
* For some reason mvn test will not work if this is @Before, but in eclipse it works! As a
* result, we use @BeforeClass.
*/
@BeforeClass
public static void setupClass() {
mockedAppender = new MockedAppender();
logger = (Logger)LogManager.getLogger(ClassWithLoggingToTest.class);
logger.addAppender(mockedAppender);
logger.setLevel(Level.INFO);
}
@AfterClass
public static void teardown() {
logger.removeAppender(mockedAppender);
}
@Test
public void test() {
// do something that causes logs
for (String e : mockedAppender.message) {
// add asserts for the log messages
}
}
private static class MockedAppender extends AbstractAppender {
List<String> message = new ArrayList<>();
protected MockedAppender() {
super("MockedAppender", null, null);
}
@Override
public void append(LogEvent event) {
message.add(event.getMessage().getFormattedMessage());
}
}