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中配置)。

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端

Client端

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