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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24205093/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 10:49:08  来源:igfitidea点击:

How to Create a Custom Appender in log4j2?

javalogginglog4jlog4j2

提问by saurabh goyal

As disscussed in this link : How to create a own Appender in log4j?

正如此链接中所述:How to create a own Appender in log4j?

For creating a custom appender in log4j 1.x we have to extend the AppenderSkeleton class and implements its append method.

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

Similarly How we can create a custom appender in log4j2 as we dont have AppenderSkelton class to extend and all other appender extend AppenderBase class .

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

采纳答案by Remko Popma

This works quite differently in log4j2 than in log4j-1.2.

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

In log4j2, you would create a plugin for this. The manual has an explanation with an example for a custom appender here: http://logging.apache.org/log4j/2.x/manual/extending.html#Appenders

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

It may be convenient to extend org.apache.logging.log4j.core.appender.AbstractAppender, but this is not required.

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

When you annotate your custom Appender class with @Plugin(name="MyCustomAppender", ...., the plugin name becomes the configuration element name, so a configuration with your custom appender would then look like this:

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

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

Note that the packagesattribute on the configuration is a comma-separated list of all the packages with custom log4j2 plugins. Log4j2 will search these packages in the classpath for classes annotated with @Plugin.

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

Here is a sample custom appender that prints to the console:

这是打印到控制台的示例自定义 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);
    }
}

For more details on plugins: http://logging.apache.org/log4j/2.x/manual/plugins.html

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

If the manual is not enough, it may be useful to look at the source code for the built-in appenders in log4j-core.

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

回答by Javoslaw

It looks like plugin appenders are scanned at startup and cannot be added during runtime. Is that true?

看起来插件 appender 在启动时被扫描,不能在运行时添加。真的吗?

to add new appender while running you can use monitorInterval property to update log configuration i.e. every 60 sec:

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

    <Configuration monitorInterval="60">

回答by pppk520

For people need to output to TextArea, here is a working tweak

对于需要输出到 TextArea 的人,这是一个有效的调整

Make the TextArea static

使 TextArea 为静态

NetBeans Swing TextArea is not static, causes trouble

NetBeans Swing TextArea 不是静态的,会引起麻烦

Add static method in your Frame

在您的框架中添加静态方法

public class MyFrame extends javax.swing.JFrame {
    ...
    public static void outputToTextArea(String message) {
        jTextArea.append(message);
    }

Call in Appender's append

调用 Appender 的 append

@Override
public void append(LogEvent event) {
    final byte[] bytes = getLayout().toByteArray(event);
    MyFrame.outputToTextArea(new String(bytes));
}

回答by joseph

As you pointed out AppenderSkeleton is not available anymore so the solutions in How to create my own Appender in log4j?will not work.

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

Using Mockito, or similar library to create an Appender with an ArgumentCaptor will not work if you're expecting multiple logging messages because the MutableLogEvent is reused over multiple log messages.

如果您期望多个日志消息,则使用 Mockito 或类似库创建带有 ArgumentCaptor 的 Appender 将不起作用,因为 MutableLogEvent 在多个日志消息上重用。

The most generic solution I found for log4j2 is to provide a mock implementation that records all the messages. It does not require any additional libraries like Mockito or JMockit.

我为 log4j2 找到的最通用的解决方案是提供一个记录所有消息的模拟实现。它不需要任何额外的库,如 Mockito 或 JMockit。

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());
    }
}