【问题标题】:How to Create a Custom Appender in log4j2?如何在 log4j2 中创建自定义 Appender?
【发布时间】:2014-08-03 23:59:42
【问题描述】:

正如在此链接中讨论的那样:How to create a own Appender in log4j?

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

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

【问题讨论】:

    标签: java logging log4j log4j2


    【解决方案1】:

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

    在 log4j2 中,您将为此创建一个插件。该手册在这里有一个自定义附加程序示例的解释:http://logging.apache.org/log4j/2.x/manual/extending.html#Appenders

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

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

    <Configuration packages="com.yourcompany.yourcustomappenderpackage">
      <Appenders>
        <MyCustomAppender name="ABC" otherAttribute="...">
        ...
      </Appenders>
      <Loggers><Root><AppenderRef ref="ABC" /></Root></Loggers>
    </Configuration>
    

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

    这是一个打印到控制台的示例自定义附加程序:

    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中内置appender的源码。

    【讨论】:

    • 看起来插件附加程序在启动时被扫描,并且在运行时无法添加。真的吗?如果是这样,这并不能回答如何以编程方式改变 Log4J 2 行为的问题。
    • @ingyhere 以编程方式配置 Log4j2 确实是一个单独的问题。此 log4j2 手册页可能是了解更多信息的良好起点:logging.apache.org/log4j/2.x/manual/… 否则您可能想提出一个新问题。
    • Log4j2 的 PatternLayout 是线程安全的,不需要锁定。是的,锁的使用使 appender 同步。如果您的 appender 将生成的字节写入某个线程安全的目的地,那么您的 appender 不需要进行锁定。
    • @Aman 允许多个线程获取同一个读锁。由于没有任何东西获得相关的写锁,那些lock / unlock 调用不会做任何事情。此代码不是线程安全的,多个线程可以同时执行受保护的代码。
    【解决方案2】:

    看起来插件附加程序在启动时被扫描并且无法在运行时添加。这是真的吗?

    要在运行时添加新的附加程序,您可以使用 monitorInterval 属性更新日志配置,即每 60 秒:

        <Configuration monitorInterval="60">
    

    【讨论】:

    • 您可以通过编程方式添加 Appender,但如果设置了 monitorInterval,那么如果对配置文件进行更改,添加的 appender 将丢失。
    【解决方案3】:

    正如您指出的,AppenderSkeleton 不再可用,因此How to create my own Appender in log4j? 中的解决方案将不起作用。

    如果您期望多条日志消息,则使用 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());
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-26
      • 1970-01-01
      • 2013-06-28
      • 2018-06-20
      • 1970-01-01
      • 1970-01-01
      • 2015-05-13
      相关资源
      最近更新 更多