如何在log4j中创建自己的Appender?

62

我是log4j的新手。有人可以解释一下如何创建自己的Appender吗?即如何实现类和接口以及如何覆盖它?


4
您需要实现 appender接口。但是建议您继承其中一个现有的 appender 并覆盖其方法,例如doAppend(LoggingEvent),以满足您的需求。 - happymeal
1
如果现有的appender是final的,怎么办? - Gaurav
4个回答

80
更新:所提供的解决方案适用于Log4J 1.x版本。如果您正在寻找2.x版本,请参阅此文章:如何在log4j2中创建自定义appender 您应该扩展AppenderSkeleton类,它(引用javadoc)“提供了通用功能的代码,例如支持阈值过滤和一般的过滤器支持。”
如果您阅读AppenderSkeleton的代码,您会发现它处理了几乎所有的内容,只留下以下几个需要您实现:
  1. protected void append(LoggingEvent event)
  2. public void close()
  3. public boolean requiresLayout()
核心方法是append。请记住,您不需要在其中实现过滤逻辑,因为它已经在doAppend中实现,而doAppend又调用append。 这里我编写了一个(相当无用的)类,它将日志条目存储在一个ArrayList中,仅作为演示。
public /*static*/ class MyAppender extends AppenderSkeleton {
    ArrayList<LoggingEvent> eventsList = new ArrayList();

    @Override
    protected void append(LoggingEvent event) {
        eventsList.add(event);
    }

    public void close() {
    }

    public boolean requiresLayout() {
        return false;
    }

}

好的,让我们来测试一下:

public static void main (String [] args) {

    Logger l = Logger.getLogger("test");

    MyAppender app = new MyAppender();

    l.addAppender(app);

    l.warn("first");
    l.warn("second");
    l.warn("third");

    l.trace("fourth shouldn't be printed");

    for (LoggingEvent le: app.eventsList) {
        System.out.println("***" + le.getMessage());
    }
} 

您应该会看到“first”、“second”、“third”的输出;由于根记录器的日志级别为debug而事件级别为trace,所以不应该打印第四个消息。这证明AbstractSkeleton已经为我们正确实现了“级别管理”。因此,这绝对是前进的正确方向...那么问题来了:既然有许多内置的记录器可以记录到几乎任何目的地,为什么需要自定义的附加器呢?(顺便说一句,log4j的一个好起点:http://logging.apache.org/log4j/1.2/manual.html

3
从http://logging.apache.org/log4j/1.2/manual.html得知,“如果记录器的级别是q,则级别为p的日志请求将被启用(无论是分配的还是继承的),只要p≥q”。该日志请求的级别为“trace”,而记录器的级别设置为比“trace”高的“debug”,因此不会被记录。请记住,记录器没有分配级别,因此它继承根记录器的级别。为了使记录器也记录trace级别请求(最高详细程度!),您可以通过编程方式(l.setLevel(Level.TRACE);)或更好地通过属性文件设置“logger”或根记录器的级别。 - AgostinoX
1
我不知道你使用的具体Web服务器。你尝试过http://logging.apache.org/log4j/1.2/manual.html中提到的“Tomcat下的默认初始化”建议了吗? - AgostinoX
1
这不是你在最初的问题中所问的。如果你已经创建了自己的Appender子类,你应该提供写入磁盘的代码。也许你并不需要一个定制的Appender类,你只需要一个FileAppender,它可以直接将日志记录到磁盘上。你只需要提供文件名即可。 - AgostinoX
1
@Elakkiya:不用谢。你解决了log4j的问题吗?你扩展了AppenderSkeleton还是使用了一些提供的Appender? - AgostinoX
你好,你知道如何在新版本中做这个吗?我问这个问题是因为这对我现在不起作用。我收到一个错误,指出记录器没有 "addAppender" 函数。 - Tomer
显示剩余7条评论

9
如果您想进行一些操作或决策,可以像这样进行:
@Override
protected void append(LoggingEvent event) {
        String message = null;
        if(event.locationInformationExists()){
            StringBuilder formatedMessage = new StringBuilder();
            formatedMessage.append(event.getLocationInformation().getClassName());
            formatedMessage.append(".");
            formatedMessage.append(event.getLocationInformation().getMethodName());
            formatedMessage.append(":");
            formatedMessage.append(event.getLocationInformation().getLineNumber());
            formatedMessage.append(" - ");
            formatedMessage.append(event.getMessage().toString());
            message = formatedMessage.toString();
        }else{
            message = event.getMessage().toString();
        }

        switch(event.getLevel().toInt()){
        case Level.INFO_INT:
            //your decision
            break;
        case Level.DEBUG_INT: 
            //your decision
            break;
        case Level.ERROR_INT:
            //your decision
            break;
        case Level.WARN_INT:
            //your decision
            break;
        case Level.TRACE_INT:
            //your decision
            break;
        default:
            //your decision
            break;
        }
}

5
我想扩展@AgostinoX的回答,支持配置文件和启动/停止日志捕获的功能:
public class StringBufferAppender extends org.apache.log4j.AppenderSkeleton {

    StringBuffer logs = new StringBuffer();
    AtomicBoolean captureMode = new AtomicBoolean(false);

    public void close() {
        // TODO Auto-generated method stub

    }

    public boolean requiresLayout() {
        // TODO Auto-generated method stub
        return false;
    }


    @Override
    protected void append(LoggingEvent event) {
        if(captureMode.get())
            logs.append(event.getMessage());
    }

    public void start()
    {
        //System.out.println("[StringBufferAppender|start] - Start capturing logs");
        StringBuffer logs = new StringBuffer();
        captureMode.set(true);
    }

    public StringBuffer stop()
    {
        //System.out.println("[StringBufferAppender|start] - Stop capturing logs");
        captureMode.set(false);
        StringBuffer data = new StringBuffer(logs);
        logs = null;
        return data;
    }


}

现在,你需要在log4j.property文件中定义它。
log4j.rootLogger=...., myAppender  # here you adding your appendr name
log4j.appender.myAppender=com.roi.log.StringBufferAppender # pointing it to the implementation

如果您想在运行时启用它:

Logger logger = Logger.getRootLogger();
        StringBufferAppender appender = (StringBufferAppender)logger.getAppender("myAppender");
        appender.start();

当您想要停止它时:

StringBuffer sb = appender.stop();

2
错误:从以下内容中移除“StringBuffer”:StringBuffer logs = new StringBuffer(); - Mr. Developerdude
@LennartRolland 这不是一个 bug。 - USer22999299
当我覆盖append(LoggingEvent event)方法时,出现错误 - MyCustomAppender类型的append(LoggingEvent)方法必须覆盖或实现超类型方法。 - Chetan Oswal

2
要创建自己的Appender,只需实现Appender接口并覆盖它即可。 同时,还要学习这个链接 开始 日志

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接