自定義Log4j2的Appender


Appender是Log4j2的日志輸出方式,Log4j2提供了很多自帶的Appender,包括控制台的、文件的、郵件的,kafka的等等。

但是這樣也是不能覆蓋全部需求的,需要提供自定義的Appender。
 
考慮我們需要提供的Appender所需要的功能:
1、分析Perf4j的日志
2、將分析的日志按時間間隔輸出到指定文件
 
提供自定義的Appender,需繼承AbstractAppender類,實現append方法。
考慮繼承FileAppender的實現將日志輸出到指定的文件之中。 
GenericAsyncCoalescingStatisticsAppender 提供一個無參的構造方法,可以提供分析日志的功能。
 
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
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.AbstractOutputStreamAppender;
import org.apache.logging.log4j.core.appender.FileManager;
import org.apache.logging.log4j.core.config.Configuration;
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.PluginConfiguration;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.core.net.Advertiser;
import org.apache.logging.log4j.core.util.Booleans;
import org.apache.logging.log4j.core.util.Integers;
import org.apache.logging.log4j.message.ObjectMessage;
import org.perf4j.GroupedTimingStatistics;
import org.perf4j.helpers.GenericAsyncCoalescingStatisticsAppender;
/**
 * 適配Perf4j的Appender
 * 
 * 解決目前Log4j2 沒有提供支持Perf4j的Appender的問題
 * <p>
 * PerfjFileAppender提供類似{@link org.apache.logging.log4j.core.appender.FileAppender FileAppender}的實現
 *,並且內置Perfj的{@link org.perf4j.helpers.GenericAsyncCoalescingStatisticsAppender GenericAsyncCoalescingStatisticsAppender}對象
 * </p>
 * PerfjFileAppender的append方法,將日志信息提供到<code>GenericAsyncCoalescingStatisticsAppender</code>對象baseImplementation處理
 * 等到<code>GenericAsyncCoalescingStatisticsAppender</code>的silenceTime周期到了之后,就將<code>GenericAsyncCoalescingStatisticsAppender</code>
 * 的處理結果輸出到PerfjFileAppender的父類<code>AbstractOutputStreamAppender</code>的append方法處理,該Appender將負責把統計結果輸出到目標文件
 * 
 * <p>
 * <b>注意:</b>
 * <p>
 * 該實現中,始終會keep着第一次接收到的Perf4j的日志對象,
 * 並在每個輸出的時機將GenericAsyncCoalescingStatisticsAppender輸出的信息設置到日志對象,
 * 這就導致在應用啟動之后,需要觸發Perf4j才能輸出信息,不然連空統計信息也不會輸出
 * </p>
 * </p>
 * @author sunwei3
 * 
 */
@Plugin(name = "Perf4jAppender", category = "Core", elementType = "appender", printObject = true)
public class Perf4jFileAppender extends AbstractOutputStreamAppender<FileManager> {
    private final String fileName;
    private final Advertiser advertiser;
    private Object advertisement;
	private static final int DEFAULT_BUFFER_SIZE = 8192;
	private static final long serialVersionUID = 1L;
	private Log4jLogEvent result = null;
    private final GenericAsyncCoalescingStatisticsAppender baseImplementation =
    		new GenericAsyncCoalescingStatisticsAppender();
    
	protected Perf4jFileAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
            final FileManager manager, final String filename, final boolean ignoreExceptions,
            final boolean immediateFlush, final Advertiser advertiser,long timeSlice) {
		super(name, layout, filter, ignoreExceptions, immediateFlush, manager);
		baseImplementation.setTimeSlice(timeSlice);
		
        if (advertiser != null) {
            final Map<String, String> configuration = new HashMap<>(layout.getContentFormat());
            configuration.putAll(manager.getContentFormat());
            configuration.put("contentType", layout.getContentType());
            configuration.put("name", name);
            advertisement = advertiser.advertise(configuration);
        }
        this.fileName = filename;
        this.advertiser = advertiser;
		
		baseImplementation.start(new GenericAsyncCoalescingStatisticsAppender.GroupedTimingStatisticsHandler() {
            public void handle(GroupedTimingStatistics statistics) {
            	if(result == null)
            	{
            		return;
            	}
            	//已經處理完timeSlice時間段的數據,需要把統計結果輸出到文件
            	fileAppend(result.asBuilder().setMessage(new ObjectMessage(statistics.toString())).build());
            }
            public void error(String errorMessage) {
                getHandler().error(errorMessage);
            }
        });
	}
	
	 @Override
	    public void stop() {
		 //先結束GenericAsyncCoalescingStatisticsAppender
		 	baseImplementation.stop();
	        super.stop();
	        if (advertiser != null) {
	            advertiser.unadvertise(advertisement);
	        }
	    }
	public void append(LogEvent event) {
		//仿照GenericAsyncCoalescingStatisticsAppender的實現,標准的Perf4j的輸出是以start開頭的
			if(result == null && String.valueOf(event.getMessage().getFormattedMessage()).startsWith("start")){
				//為了避免每次構造Log4jLogEvent,記錄下第一次的Log4jLogEvent,每次設置其Message就可以輸出到文件了
			result = new Log4jLogEvent.Builder(event).build();
			}
			//將日志交與GenericAsyncCoalescingStatisticsAppender處理,符合條件的日志將會被處理
			baseImplementation.append(String.valueOf(event.getMessage().getFormattedMessage()));
	}
	
	public void fileAppend(LogEvent event) {
		super.append(event);
	}
	
    public String getFileName() {
        return this.fileName;
    }
	@PluginFactory
	public static Perf4jFileAppender createAppender(
            // @formatter:off
            @PluginAttribute("fileName") final String fileName,
            @PluginAttribute("append") final String append,
            @PluginAttribute("locking") final String locking,
            @PluginAttribute("name") final String name,
            @PluginAttribute("immediateFlush") final String immediateFlush,
            @PluginAttribute("ignoreExceptions") final String ignore,
            @PluginAttribute("bufferedIo") final String bufferedIo,
            @PluginAttribute("bufferSize") final String bufferSizeStr,
            @PluginElement("Layout") Layout<? extends Serializable> layout,
            @PluginElement("Filter") final Filter filter,
            @PluginAttribute("advertise") final String advertise,
            @PluginAttribute("advertiseUri") final String advertiseUri,
            @PluginConfiguration final Configuration config,
            @PluginAttribute("timeSlice") final long timeSlice
			) {
        // @formatter:on
        final boolean isAppend = Booleans.parseBoolean(append, true);
        final boolean isLocking = Boolean.parseBoolean(locking);
        boolean isBuffered = Booleans.parseBoolean(bufferedIo, true);
        final boolean isAdvertise = Boolean.parseBoolean(advertise);
        if (isLocking && isBuffered) {
            if (bufferedIo != null) {
                LOGGER.warn("Locking and buffering are mutually exclusive. No buffering will occur for " + fileName);
            }
            isBuffered = false;
        }
        final int bufferSize = Integers.parseInt(bufferSizeStr, DEFAULT_BUFFER_SIZE);
        if (!isBuffered && bufferSize > 0) {
            LOGGER.warn("The bufferSize is set to {} but bufferedIO is not true: {}", bufferSize, bufferedIo);
        }
        final boolean isFlush = Booleans.parseBoolean(immediateFlush, true);
        final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
        if (name == null) {
            LOGGER.error("No name provided for FileAppender");
            return null;
        }
        if (fileName == null) {
            LOGGER.error("No filename provided for FileAppender with name "  + name);
            return null;
        }
        if (layout == null) {
            layout = PatternLayout.createDefaultLayout();
        }
        final FileManager manager = FileManager.getFileManager(fileName, isAppend, isLocking, isBuffered, advertiseUri,
            layout, bufferSize);
        if (manager == null) {
            return null;
        }
        return new Perf4jFileAppender(name, layout, filter, manager, fileName, ignoreExceptions, isFlush,
                isAdvertise ? config.getAdvertiser() : null,timeSlice);
    }
}

  log4j2的配置文件為:

<Perf4jAppender name="perfsStatistics" TimeSlice="1800000"
            fileName="${profile.log.root.path}/${profile.log.name.web}/perfStats.log">
            <PatternLayout>
                <Pattern>%m%n</Pattern>
            </PatternLayout>
</Perf4jAppender>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM