Log4j/Log4j2自定义Appender来实现日志级别计数统计及监控

Log4j/Log4j2自定义Appender来实现日志级别计数统计及监控

一、简述

本文主要讲如何基于Log4j2来实现自定义的Appender。一般用途是用于Log4j2自带的Appender不足以满足我们的需求,或者需要我们对日志进行拦截统计等操作时,需要我们自定义Appender。

二、自定义Appender

方法:实现一个类,让它继承自Log4j2的AbstractAppender,然后你重写其append方法,并添加一个@PluginFactory标记的createAppender方法。

举例:例如,我们要实现一个通过日志输出的Level来统计计数来实现监控的一个Appender,并需要在配置日志时,实现对一些附加属性的配置,可以如下实现。

package com.test.utils.logs;

import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;

import java.io.Serializable;

@Plugin(name = "Statistics", category = "Core", elementType = "appender", printObject = true)
public class StatisticsAppender extends AbstractAppender {
    /** 日志级别大于等于此级别及以上会进行判断错误。默认:ERROR */
    private String failedOnLogLevel;
    /** 指定时间内,出现多少次该日志级别,会被认为是错误。默认:10 */
    private Integer failedOnLogLevelCount;
    /** 该日志级别以上持续出现多长时间,会被认为是错误。默认:30000 */
    private Integer failedOnLogLevelInMisSecond;
    /** 当连续小于该日志级别多长时间后,恢复为正常状态。默认:120000 */
    private Integer recoveryOnLessLogLevelInMisSecond;

    protected StatisticsAppender(String name, Filter filter, Layout<? extends Serializable> layout,
                                 String failedOnLogLevel,
                                 Integer failedOnLogLevelCount,
                                 Integer failedOnLogLevelInMisSecond,
                                 Integer recoveryOnLessLogLevelInMisSecond) {
        super(name, filter, layout);
        this.failedOnLogLevel = failedOnLogLevel;
        this.failedOnLogLevelCount = failedOnLogLevelCount;
        this.failedOnLogLevelInMisSecond = failedOnLogLevelInMisSecond;
        this.recoveryOnLessLogLevelInMisSecond = recoveryOnLessLogLevelInMisSecond;
    }


    @Override
    public void append(LogEvent logEvent) {
        //此处省略告警过滤统计代码。
        // .....
        String msg = logEvent.getMessage().getFormattedMessage();
        System.out.println(msg);
    }

    @PluginFactory
    public static StatisticsAppender createAppender(@PluginAttribute("name") String name,
                                                    @PluginElement("Filter") final Filter filter,
                                                    @PluginElement("Layout") Layout<? extends Serializable> layout,
                                                    @PluginAttribute("failedOnLogLevel") String failedOnLogLevel,
                                                    @PluginAttribute("failedOnLogLevelCount") Integer failedOnLogLevelCount,
                                                    @PluginAttribute("failedOnLogLevelInMisSecond") Integer failedOnLogLevelInMisSecond,
                                                    @PluginAttribute("recoveryOnLessLogLevelInMisSecond") Integer recoveryOnLessLogLevelInMisSecond) {
        if (name == null) {
            LOGGER.error("No name provided for MyCustomAppenderImpl");
            return null;
        }
        if (layout == null) {
            layout = PatternLayout.createDefaultLayout();
        }
        if (failedOnLogLevel == null) {
            failedOnLogLevel = "ERROR";
        }
        if (failedOnLogLevelCount == null) {
            failedOnLogLevelCount = 10;
        }
        if (failedOnLogLevelInMisSecond == null) {
            failedOnLogLevelInMisSecond = 30000;
        }
        if (recoveryOnLessLogLevelInMisSecond == null) {
            recoveryOnLessLogLevelInMisSecond = 120000;
        }
        return new StatisticsAppender(name, filter, layout, failedOnLogLevel, failedOnLogLevelCount, failedOnLogLevelInMisSecond, recoveryOnLessLogLevelInMisSecond);
    }

}

说明:注意黄色区域的地方,另外createAppender方法中,红色的参数表示XML中要配置的Appender属性。

三、配置log4j2.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration status="ON" packages="org.apache.logging.log4j.core,io.sentry.log4j2,com.test.utils.logs">
    <appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
        <RollingFile name="RollingFile" fileName="datamerge-logs/app.log"
                     filePattern="datamerge-logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
            <PatternLayout pattern="%d{yyyy.MM.dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
            <SizeBasedTriggeringPolicy size="300 MB"/>
        </RollingFile>
        <Sentry name="Sentry"/>
        <Statistics name="StatisticsMonitor" failedOnLogLevel="ERROR" failedOnLogLevelCount="10"
                    failedOnLogLevelInMisSecond="30000" recoveryOnLessLogLevelInMisSecond="120000"/>
    </appenders>
    <loggers>
        <root level="INFO">
            <appender-ref ref="Console"/>
            <!--<appender-ref ref="RollingFile"/>-->
            <!--<appender-ref ref="Sentry" level="ERROR" />-->
            <appender-ref ref="StatisticsMonitor"/>
        </root>
    </loggers>
</configuration>

说明,请仔细阅读黄色区域的内容。packages中要标注Appender所在的包名(不同的包名请使用”,”分隔)

四、调用

正常调用日志即可。需要说明的是,Appender会在程序启动后第一次调用日志时,实例化一次Appender,之后就不会再实例化了。之后会多次调用append方法。


Log4j/Log4j2自定义Appender来实现日志级别计数统计及监控
https://www.dearcloud.cn/2019/02/26/20200310-cnblogs-old-posts/20190226-Log4jLog4j2自定义Appender来实现日志级别计数统计及监控/
作者
宋兴柱
发布于
2019年2月26日
许可协议