如何在log4j2中创建自定义Appender?

sau*_*yal 48 java logging log4j log4j2

正如在这个链接中所讨论的:如何在log4j中创建自己的Appender?

为了在log4j 1.x中创建自定义appender,我们必须扩展AppenderSkeleton类并实现其append方法.

类似地我们如何在log4j2中创建自定义appender,因为我们没有扩展AppenderSkelton类,所有其他appender扩展AppenderBase类.

Rem*_*pma 78

这在log4j2中的工作方式与在log4j-1.2中的工作方式完全不同.

在log4j2中,您将为此创建一个插件.本手册中有一个自定义appender示例的说明:http://logging.apache.org/log4j/2.x/manual/extending.html#Appenders

扩展可能很方便org.apache.logging.log4j.core.appender.AbstractAppender,但这不是必需的.

当您使用自定义Appender类进行注释时@Plugin(name="MyCustomAppender", ....,插件名称将成为配置元素名称,因此使用自定义appender的配置将如下所示:

<Configuration packages="com.yourcompany.yourcustomappenderpackage">
  <Appenders>
    <MyCustomAppender name="ABC" otherAttribute="...">
    ...
  </Appenders>
  <Loggers><Root><AppenderRef ref="ABC" /></Root></Loggers>
</Configuration>
Run Code Online (Sandbox Code Playgroud)

请注意,packages配置上的属性是包含自定义log4j2插件的所有包的逗号分隔列表.Log4j2将在类路径中搜索这些包,注释用@Plugin注释的类.

这是一个打印到控制台的示例自定义appender:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

有关插件的更多详细信息,请访问:http: //logging.apache.org/log4j/2.x/manual/plugins.html

如果手册不够,那么查看log4j-core中内置appender的源代码可能会很有用.

  • 看起来插件appender在启动时被扫描,*不能在运行时添加*.真的吗?如果是这样,这不回答如何以编程方式更改Log4J 2行为的问题. (3认同)
  • Log4j2的PatternLayout是线程安全的,不需要锁定。是的,锁的使用使得appender同步。如果您的附加程序将结果字节写入某个线程安全的目的地,那么您的附加程序不需要进行锁定。 (2认同)

jos*_*eph 5

正如您所指出的 AppenderSkeleton 不再可用,因此如何在 log4j 中创建我自己的 Appender?中的解决方案 不管用。

如果您期望多个日志消息,则使用 Mockito 或类似的库创建带有 ArgumentCaptor 的 Appender 将不起作用,因为 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());
    }
}
Run Code Online (Sandbox Code Playgroud)