log4j2自定義Appender(輸出到文件/RPC服務中)


1、背景

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

本文,將通過兩個例子說明自定義APPender,一個是將日志寫入文件中,另一個是將日志發送到遠程Thrift服務中。

本文代碼詳見:https://github.com/hawkingfoo/log-demo

2、自定義文件Appender

2.1 定義文件Appender

先上代碼:

  1.  
    @Plugin(name = "FileAppender", category = "Core", elementType = "appender", printObject = true)
  2.  
    public class FileAppender extends AbstractAppender {
  3.  
    private String fileName;
  4.  
     
  5.  
    /* 構造函數 */
  6.  
    public FileAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, String fileName) {
  7.  
    super(name, filter, layout, ignoreExceptions);
  8.  
    this.fileName = fileName;
  9.  
    }
  10.  
     
  11.  
    @Override
  12.  
    public void append(LogEvent event) {
  13.  
    final byte[] bytes = getLayout().toByteArray(event);
  14.  
    writerFile(bytes);
  15.  
     
  16.  
    }
  17.  
     
  18.  
    /* 接收配置文件中的參數 */
  19.  
    @PluginFactory
  20.  
    public static FileAppender createAppender(@PluginAttribute("name") String name,
  21.  
    @PluginAttribute("fileName") String fileName,
  22.  
    @PluginElement("Filter") final Filter filter,
  23.  
    @PluginElement("Layout") Layout<? extends Serializable> layout,
  24.  
    @PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {
  25.  
    if (name == null) {
  26.  
    LOGGER.error( "no name defined in conf.");
  27.  
    return null;
  28.  
    }
  29.  
    if (layout == null) {
  30.  
    layout = PatternLayout.createDefaultLayout();
  31.  
    }
  32.  
    // 創建文件
  33.  
    if (!createFile(fileName)) {
  34.  
    return null;
  35.  
    }
  36.  
    return new FileAppender(name, filter, layout, ignoreExceptions, fileName);
  37.  
    }
  38.  
     
  39.  
    private static boolean createFile(String fileName) {
  40.  
    Path filePath = Paths. get(fileName);
  41.  
    try {
  42.  
    // 每次都重新寫文件,不追加
  43.  
    if (Files.exists(filePath)) {
  44.  
    Files.delete(filePath);
  45.  
    }
  46.  
    Files.createFile(filePath);
  47.  
    } catch (IOException e) {
  48.  
    LOGGER.error( "create file exception", e);
  49.  
    return false;
  50.  
    }
  51.  
    return true;
  52.  
    }
  53.  
     
  54.  
    private void writerFile(byte[] log) {
  55.  
    try {
  56.  
    Files.write(Paths. get(fileName), log, StandardOpenOption.APPEND);
  57.  
    } catch (IOException e) {
  58.  
    LOGGER.error( "write file exception", e);
  59.  
    }
  60.  
    }
  61.  
    }

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

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

2.2 添加log4j2.xml配置

  1.  
    <?xml version="1.0" encoding="UTF-8"?>
  2.  
     
  3.  
    <configuration status="INFO" monitorInterval="30">
  4.  
    <appenders>
  5.  
    <!--這個輸出控制台的配置-->
  6.  
    <console name="Console" target="SYSTEM_OUT">
  7.  
    <!--輸出日志的格式-->
  8.  
    <PatternLayout pattern="%highlight{[ %p ] [%-d{yyyy-MM-dd HH:mm:ss}] [%l] %m%n}"/>
  9.  
    </console>
  10.  
     
  11.  
    <!-- 這個就是自定義的Appender -->
  12.  
    <FileAppender name="File" fileName="log.log">
  13.  
    <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5p] {%F:%L} - %m%n" />
  14.  
    </FileAppender>
  15.  
     
  16.  
    </appenders>
  17.  
     
  18.  
    <loggers>
  19.  
    <!--過濾掉spring和mybatis的一些無用的DEBUG信息-->
  20.  
    <logger name="org.springframework" level="INFO"></logger>
  21.  
    <logger name="org.mybatis" level="INFO"></logger>
  22.  
    <root level="all">
  23.  
    <appender-ref ref="Console"/>
  24.  
    <appender-ref ref="File"/>
  25.  
    </root>
  26.  
    </loggers>
  27.  
    </configuration>

備注:

  • 上面的log配置,一共配了2個輸出。一個是終端輸出,一個是采用自定義的FileAppender輸出到文件中。
  • <FileAppender>標簽要與自定義Appender中的類注解保持一致。

2.3 測試

  1.  
    public class TestLogFile {
  2.  
    private static final Logger logger = LogManager.getLogger(TestLogFile.class);
  3.  
     
  4.  
    public static void main(String[] args) {
  5.  
    logger.info( "1");
  6.  
    logger.info( "2");
  7.  
    logger.info( "3");
  8.  
    }
  9.  
    }

日志輸出

日志輸出

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

3、自定義Thrift Appender

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

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

3.1 Thrift RPC服務

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

  1.  
    namespace java thrift
  2.  
     
  3.  
    service LogServer {
  4.  
    string getLogRes(1:string log);
  5.  
    }

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

3.2 定義ThriftAppender

  1.  
    @Plugin(name = "ThriftAppender", category = "Core", elementType = "appender", printObject = true)
  2.  
    public class ThriftAppender extends AbstractAppender {
  3.  
     
  4.  
    private LogServer.Client client;
  5.  
    private TTransport transport;
  6.  
     
  7.  
    /* 構造函數 */
  8.  
    public ThriftAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, String host) {
  9.  
    super(name, filter, layout, ignoreExceptions);
  10.  
    // 創建客戶端
  11.  
    createThriftClient(host);
  12.  
    }
  13.  
     
  14.  
    @Override
  15.  
    public void append(LogEvent event) {
  16.  
    final byte[] bytes = getLayout().toByteArray(event);
  17.  
    try {
  18.  
    String response = client.getLogRes( new String(bytes));
  19.  
    System.out.println(response);
  20.  
    } catch (TException e) {
  21.  
    e.printStackTrace();
  22.  
    }
  23.  
    }
  24.  
     
  25.  
    /* 接收配置文件中的參數 */
  26.  
    @PluginFactory
  27.  
    public static ThriftAppender createAppender(@PluginAttribute("name") String name,
  28.  
    @PluginAttribute("host") String host,
  29.  
    @PluginElement("Filter") final Filter filter,
  30.  
    @PluginElement("Layout") Layout<? extends Serializable> layout,
  31.  
    @PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {
  32.  
    if (name == null) {
  33.  
    LOGGER.error( "no name defined in conf.");
  34.  
    return null;
  35.  
    }
  36.  
    if (layout == null) {
  37.  
    layout = PatternLayout.createDefaultLayout();
  38.  
    }
  39.  
    return new ThriftAppender(name, filter, layout, ignoreExceptions, host);
  40.  
    }
  41.  
     
  42.  
    @Override
  43.  
    public void stop() {
  44.  
    if (transport != null) {
  45.  
    transport.close();
  46.  
    }
  47.  
    }
  48.  
     
  49.  
    private void createThriftClient(String host) {
  50.  
    try {
  51.  
    transport = new TFramedTransport(new TSocket(host, 9000));
  52.  
    transport.open();
  53.  
    TProtocol protocol = new TBinaryProtocol(transport);
  54.  
    client = new LogServer.Client(protocol);
  55.  
    LOGGER.info( "create client success");
  56.  
    } catch (Exception e) {
  57.  
    LOGGER.error( "create file exception", e);
  58.  
    }
  59.  
    }
  60.  
    }

備注:
除了和文件Appender相同的外,這里需要注意兩個地方。一個是Thrift Client的創建,另一個Thrift發送log。
具體的發送邏輯,在append()方法中實現。

3.3 添加log4j2.xml配置

  1.  
    <?xml version="1.0" encoding="UTF-8"?>
  2.  
     
  3.  
    <configuration status="INFO" monitorInterval="30">
  4.  
    <appenders>
  5.  
    <!--這個輸出控制台的配置-->
  6.  
    <console name="Console" target="SYSTEM_OUT">
  7.  
    <!--輸出日志的格式-->
  8.  
    <PatternLayout pattern="%highlight{[ %p ] [%-d{yyyy-MM-dd HH:mm:ss}] [%l] %m%n}"/>
  9.  
    </console>
  10.  
     
  11.  
    <!-- 這個就是自定義的Appender -->
  12.  
    <ThriftAppender name="Thrift" host="127.0.0.1">
  13.  
    <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5p] {%F:%L} - %m%n" />
  14.  
    </ThriftAppender>
  15.  
    </appenders>
  16.  
     
  17.  
    <loggers>
  18.  
    <!--過濾掉spring和mybatis的一些無用的DEBUG信息-->
  19.  
    <logger name="org.springframework" level="INFO"></logger>
  20.  
    <logger name="org.mybatis" level="INFO"></logger>
  21.  
    <root level="all">
  22.  
    <appender-ref ref="Console"/>
  23.  
    <appender-ref ref="Thrift"/>
  24.  
     
  25.  
    </root>
  26.  
    </loggers>
  27.  
    </configuration>

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

3.4 測試

  1.  
    public class TestThriftFile {
  2.  
    private static final Logger logger = LogManager.getLogger(TestThriftFile.class);
  3.  
     
  4.  
    public static void main(String[] args) {
  5.  
    logger.info( "a");
  6.  
    logger.info( "b");
  7.  
    logger.info( "c");
  8.  
    }
  9.  
    }

Server端

Client端

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


免責聲明!

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



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