1. 程式人生 > >log4j2實現自定義Appender(輸出到檔案/RPC服務中)

log4j2實現自定義Appender(輸出到檔案/RPC服務中)

1、背景

雖然log4j很強大,可以將日誌輸出到檔案、DB、ES等。但是有時候確難免完全適合自己,此時我們就需要自定義Appender,使日誌輸出到指定的位置上。

本文,將通過兩個例子說明自定義APPender,一個是將日誌寫入檔案中,另一個是將日誌傳送到遠端Thrift服務中。

2、自定義檔案Appender

2.1 定義檔案Appender

先上程式碼:

@Plugin(name = "FileAppender", category = "Core", elementType = "appender", printObject = true)
public class FileAppender extends AbstractAppender {
    private String fileName;

    /* 建構函式 */
    public FileAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, String fileName) {
        super(name, filter, layout, ignoreExceptions);
        this.fileName = fileName;
    }

    @Override
    public void append(LogEvent event) {
        final byte[] bytes = getLayout().toByteArray(event);
        writerFile(bytes);

    }

    /*  接收配置檔案中的引數 */
    @PluginFactory
    public static FileAppender createAppender(@PluginAttribute("name") String name,
                                              @PluginAttribute("fileName") String fileName,
                                              @PluginElement("Filter") final Filter filter,
                                              @PluginElement("Layout") Layout<? extends Serializable> layout,
                                              @PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {
        if (name == null) {
            LOGGER.error("no name defined in conf.");
            return null;
        }
        if (layout == null) {
            layout = PatternLayout.createDefaultLayout();
        }
        // 建立檔案
        if (!createFile(fileName)) {
            return null;
        }
        return new FileAppender(name, filter, layout, ignoreExceptions, fileName);
    }

    private static boolean createFile(String fileName) {
        Path filePath = Paths.get(fileName);
        try {
            // 每次都重新寫檔案,不追加
            if (Files.exists(filePath)) {
                Files.delete(filePath);
            }
            Files.createFile(filePath);
        } catch (IOException e) {
            LOGGER.error("create file exception", e);
            return false;
        }
        return true;
    }

    private void writerFile(byte[] log) {
        try {
            Files.write(Paths.get(fileName), log, StandardOpenOption.APPEND);
        } catch (IOException e) {
            LOGGER.error("write file exception", e);
        }
    }
}

上面程式碼有幾個需要注意的地方:

  • @Plugin..註解:這個註解,是為了在之後配置log4j2.xml時,指定的Appender Tag。
  • 建構函式:除了使用父類的以外,也可以增加一些自己的配置。
  • 重寫append()方法:這裡面需要實現具體的邏輯,日誌的去向。
  • createAppender()方法:主要是接收log4j2.xml中的配置項。

2.2 新增log4j2.xml配置

<?xml version="1.0" encoding="UTF-8"?>

<configuration status="INFO" monitorInterval="30">
    <appenders>
        <!--這個輸出控制檯的配置-->
        <console name="Console" target="SYSTEM_OUT">
            <!--輸出日誌的格式-->
            <PatternLayout pattern="%highlight{[ %p ] [%-d{yyyy-MM-dd HH:mm:ss}] [%l] %m%n}"/>
        </console>

        <!-- 這個就是自定義的Appender -->
        <FileAppender name="File" fileName="log.log">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5p] {%F:%L} - %m%n" />
        </FileAppender>

    </appenders>

    <loggers>
        <!--過濾掉spring和mybatis的一些無用的DEBUG資訊-->
        <logger name="org.springframework" level="INFO"></logger>
        <logger name="org.mybatis" level="INFO"></logger>
        <root level="all">
            <appender-ref ref="Console"/>
            <appender-ref ref="File"/>
        </root>
    </loggers>
</configuration>

備註:

  • 上面的log配置,一共配了2個輸出。一個是終端輸出,一個是採用自定義的FileAppender輸出到檔案中。
  • <FileAppender>標籤要與自定義Appender中的類註解保持一致。

2.3 測試

public class TestLogFile {
    private static final Logger logger = LogManager.getLogger(TestLogFile.class);

    public static void main(String[] args) {
        logger.info("1");
        logger.info("2");
        logger.info("3");
    }
}

可以看到,日誌一共輸出了2份,一份到終端中,一份到log.log中(具體的檔案路徑可在log4j2.xml中配置)。

 注意:

       編碼完執行測試,在log4j2.xml中新增自定義的appender後執行可能會發現出現如下誤: 

                  main ERROR Error processing element XXX ([Appenders: null]): CLASS_NOT_FOUND 

2016-08-02 22:46:30,693 main ERROR Error processing element XXX ([Appenders: null]): CLASS_NOT_FOUND
       這是由於log4j2沒有載入自定義的類而丟擲異常。可以在log4j2.xml中加上 :

                    <Configuration status="info" packages="com.custom.log.log4j2plugin">  

<Configuration status="info" packages="com.zero.scribe.log4j2plugin">

       這裡的packages是自定義的Appender類所在包的包名,這會告訴log4j2去載入這個包下的類,而不會丟擲CLASS_NOT_FOUND異常。
       倘若不想多加packages這個欄位,則需要預先build一下project,我使用的是gradle構建專案,執行gradle build後在classes中會額外生成META-INF,META-INF中會有org.apache.logging.log4j.core.config.plugins目錄,目錄會有Log4j2Plugins.dat,該二進位制檔案則會告訴log4j2需要載入自定義的Appender類。

3、自定義Thrift Appender

上一節,主要是日誌的檔案輸出。有時我們需要將日誌傳送給日誌收集服務,常見的方法可以寫一個日誌收集Agent,收集日誌;或者將日誌輸出方當成客戶端直接傳送到遠端。

下文,通過自定義Appender的方式,將日誌輸出到遠端的RPC服務中。

3.1 Thrift RPC服務

假設現在有一個Thrift RPC服務,實時接收日誌訊息。它的定義是下面的樣子:

namespace java thrift

service LogServer {
    string getLogRes(1:string log);
}

服務很簡單,入參是log,返回值是String。
Thrift相關知識可以檢視,Thrift RPC服務10分鐘上手

3.2 定義ThriftAppender

@Plugin(name = "ThriftAppender", category = "Core", elementType = "appender", printObject = true)
public class ThriftAppender extends AbstractAppender {

    private LogServer.Client client;
    private TTransport transport;

    /* 建構函式 */
    public ThriftAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, String host) {
        super(name, filter, layout, ignoreExceptions);
        // 建立客戶端
        createThriftClient(host);
    }

    @Override
    public void append(LogEvent event) {
        final byte[] bytes = getLayout().toByteArray(event);
        try {
            String response = client.getLogRes(new String(bytes));
            System.out.println(response);
        } catch (TException e) {
            e.printStackTrace();
        }
    }

    /*  接收配置檔案中的引數 */
    @PluginFactory
    public static ThriftAppender createAppender(@PluginAttribute("name") String name,
                                              @PluginAttribute("host") String host,
                                              @PluginElement("Filter") final Filter filter,
                                              @PluginElement("Layout") Layout<? extends Serializable> layout,
                                              @PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {
        if (name == null) {
            LOGGER.error("no name defined in conf.");
            return null;
        }
        if (layout == null) {
            layout = PatternLayout.createDefaultLayout();
        }
        return new ThriftAppender(name, filter, layout, ignoreExceptions, host);
    }

    @Override
    public void stop() {
        if (transport != null) {
            transport.close();
        }
    }

    private void createThriftClient(String host) {
        try {
            transport = new TFramedTransport(new TSocket(host, 9000));
            transport.open();
            TProtocol protocol = new TBinaryProtocol(transport);
            client = new LogServer.Client(protocol);
            LOGGER.info("create client success");
        } catch (Exception e) {
            LOGGER.error("create file exception", e);
        }
    }
}

備註:
除了和檔案Appender相同的外,這裡需要注意兩個地方。一個是Thrift Client的建立,另一個Thrift傳送log。
具體的傳送邏輯,在append()方法中實現。

3.3 新增log4j2.xml配置

<?xml version="1.0" encoding="UTF-8"?>

<configuration status="INFO" monitorInterval="30">
    <appenders>
        <!--這個輸出控制檯的配置-->
        <console name="Console" target="SYSTEM_OUT">
            <!--輸出日誌的格式-->
            <PatternLayout pattern="%highlight{[ %p ] [%-d{yyyy-MM-dd HH:mm:ss}] [%l] %m%n}"/>
        </console>

        <!-- 這個就是自定義的Appender -->
        <ThriftAppender name="Thrift" host="127.0.0.1">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5p] {%F:%L} - %m%n" />
        </ThriftAppender>
    </appenders>

    <loggers>
        <!--過濾掉spring和mybatis的一些無用的DEBUG資訊-->
        <logger name="org.springframework" level="INFO"></logger>
        <logger name="org.mybatis" level="INFO"></logger>
        <root level="all">
            <appender-ref ref="Console"/>
            <appender-ref ref="Thrift"/>

        </root>
    </loggers>
</configuration>

這裡同樣是定義了兩個輸出路徑,一個是終端,一個是Thrift服務。

3.4 測試

public class TestThriftFile {
    private static final Logger logger = LogManager.getLogger(TestThriftFile.class);

    public static void main(String[] args) {
        logger.info("a");
        logger.info("b");
        logger.info("c");
    }
}

測試可以看出,Server端成功接收到了log。

相關推薦

log4j2實現定義Appender輸出檔案/RPC服務

1、背景 雖然log4j很強大,可以將日誌輸出到檔案、DB、ES等。但是有時候確難免完全適合自己,此時我們就需要自定義Appender,使日誌輸出到指定的位置上。 本文,將通過兩個例子說明自定義APPender,一個是將日誌寫入檔案中,另一個是將日誌傳送到遠端Thrift服

log4j2定義Appender輸出檔案/RPC服務

1、背景 雖然log4j很強大,可以將日誌輸出到檔案、DB、ES等。但是有時候確難免完全適合自己,此時我們就需要自定義Appender,使日誌輸出到指定的位置上。 本文,將通過兩個例子說明自定義APPender,一個是將日誌寫入檔案中,另一個是將日誌傳送到遠

thinkphp 5.0如何實現定義404異常處理頁面

錯誤頁 自定義異常 異常錯誤 錯誤 load php 錯誤信息 art 正常 404頁面是客戶端在瀏覽網頁時,由於服務器無法正常提供信息,或是服務器無法回應,且不知道原因所返回的頁面。404承載著用戶體驗與SEO優化的重任。404頁面通常為用戶訪問了網站上不存在或已刪除的

java實現定義佇列先進先出

題目:java實現自定義佇列(先進先出) 主類: import java.util.LinkedList; public class Main { public static void main(String[] args) { DuiLie dl

實現定義註解程式碼篇

第一:定義三個自定義的註解類 第二:建立一個實體 第三:建立一箇中間類 第四:測試 註解處理的基礎知識 方法1:<T extends Annotation> T getAnnotation(Class<T> annotationClass

定義RatingBar控制元件,實現定義星星或專案所需圖片的寬高,告別使用warp_content導致控制元件大小不可控

之前做過的一款app中涉及到RatingBar控制元件,基本上來說都會要求用圖片來替代, 即使同樣是星星(可能是覺得系統自帶的比較醜吧,不過我覺得還好啊)。 當時就覺得很難去做適配,UI給的圖片沒法去控制寬高,只能使用warp_content來做, 可是這樣會導

Ultra-Pull-To-Refresh超簡單實現定義動畫

前言 接上一篇部落格,Ultra-Pull-To-Refresh超簡單終極實現下拉重新整理、上拉載入 入門實現(一) 上一篇是實現了簡單的自帶的重新整理和載入功能。但是這樣簡單的動畫往往不能滿足我們的需求,所以這一片用一個京東重新整理的例子來實現自定義,就

java實現定義排序實現Comparable介面

        排序的演算法,大家都不陌生,有氣泡排序、選擇排序、插入排序,快速排序等等。如果現在有學生類,學校添加了很多學生,要你給學生按照學號大小排序,你會怎麼排? 學生類   Student {int stuID;    String stuName;   int sc

Spark2.x 如何實現定義排序利用元組,類--隱式轉換Ordering,Ordered等實現

交流QQ: 824203453 需求: 對於有複雜排序條件的需求,可以利用自定義排序來實現,同時可以使用多種方案實現自定義排序需求。 對指定的資料(欄位分別為:名稱 年齡 顏值,資料以空格分割),按照指定的要求排序,排序要求為:根據顏值降序,如果顏值

TypeScript 總實現定義事件Event

TypeScript有更好的面向物件性,實現事件只要繼承一個類EventEmitter 定義引發事件 //引入EventEmi

死磕Spring之IoC篇 - 解析定義標籤XML 檔案

> 該系列文章是本人在學習 Spring 的過程中總結下來的,裡面涉及到相關原始碼,可能對讀者不太友好,請結合我的原始碼註釋 [Spring 原始碼分析 GitHub 地址](https://github.com/liu844869663/spring-framework) 進行閱讀 > > Spring 版

定義監控網頁報警,郵件報警

zabbix註:zabbix的安裝配置參考另一篇文章1.Zabbix客戶端配置[[email protected]/* */ ~]# cat /tmp/user.sh #!/bin/bashuptime |awk ‘{print $4}‘[[email protected]/* */

js將一串隨機數字每四位加一個定義符號格式:1234-5678-90

方法一:      let string = '1234567890',result = '', index = 0; for(let i=0; i<string.length; i++){      result +=

定義Banner寫在Fragment裡了

Fragment類裡新增 Banner package com.example.lenovo.myapplication.Fragment.Fragment; import android.content.Context; import android.os.Bundle; impor

iOS定義tabbar沒有tabbar上的黑線

自定義tabbar相信在很多專案中都要用到。有的時候 還需要那種 不規則的tabbar,例如中間高兩邊底,例如需要新增tabbar的背景圖片等等。這裡 我要介紹一種 自定義tabbar的方法 ,這種方法可以呼叫系統的 hidesBottomBarWhenPushed 方法,很方便的隱藏tab

WPF TabIndex預設樣式修改:去掉預設虛線框、定義樣式Button控制元件為例

去掉Tab選中預設虛線框 Tab鍵切換時,被選控制元件自動存在虛線框,有時候為了介面美觀,這個虛線框就顯得比較麻煩。廢話不多說,下面是方法。 <Window.Resources> <Style x:Key="MeyFocusVisual" TargetType="{

Idea:通過Live Template定義模板類註釋、方法註釋

1.選擇Live Template File-Settings--Editor--Live Template 2.建立自定義Template Group 點選右邊的+號,選擇Template Group,輸入名稱,比如user  

ListView與定義介面卡顯示java端的資料

一、自定義介面卡 1、BaseAdapter:是所有介面卡類的父類,可以對列表項進行最大限度的定製 2、自定義介面卡中的方法 @Override public int getCount() {//從java端獲取到多少條資料 return da

利用C語言簡單的實現庫存管理系統本地檔案生成形式儲存

問題描述: 庫存管理系統是廠家、商城、商場等的管理商品資訊不可缺少的部分,利用這個系統,可以更好地管理商品的資訊。 本系統的功能: 新增、修改、刪除庫存商品資訊,商品資訊包括:編號、名稱、類別、數量、產地等; 進貨或售出商品後,相應的庫存商品數量應該有所改

SparkSQL 使用者定義函式UDF、UDAF、開窗

UDF: 操作單個數據行,產生單個數據行; UDAF: 操作多個數據行,產生一個數據行。 UDTF: 操作一個數據行,產生多個數據行一個表作為輸出。 UDF函式 通過spark.udf.register(“funcName”, func) 來進行註冊 使用:se