SpringBoot | 第二十五章:日誌管理之自訂Appender

來源:互聯網
上載者:User

標籤:val   關聯   官網   access   learn   部落格   manual   關係   連網   

前言

前面兩章節我們介紹了一些日誌架構的常見配置及使用實踐。一般上,在開發過程中,像log4j2logback日誌架構都提供了很多Appender,基本上可以滿足大部分的業務需求了。但在一些特殊需求或者需要將日誌進行集中管理(叢集部署時,日誌是分拆到不同伺服器上的,不可能去每一台伺服器上去下載檔案的,也不便於日誌檢索)時,就需要自訂Appender,將日誌集中輸出或者其他一些特殊需求。所以本章節就來簡單介紹下關於log4j2logback的自訂Appender知識。

  • 一點知識
    • log4j2內建Appender
    • logback內建Appender
  • 自訂Appender
    • log4j2自訂Appender
    • logback自訂Appender
    • 關於ShutdownHook
      • 一點知識
  • 參考資料
  • 總結
  • 最後
  • 老生常談
一點知識

編寫自訂Appender時,我們先來看看log4j2logback內建了哪些Appender,瞭解下是否可以滿足我們的個人化需求,避免重複製造輪子。

log4j2內建Appender

先看一張官網提供的Appender說明:

名稱 描述
AsyncAppender 使用一個單獨線程記錄日誌,實現非同步處理日誌事件。
CassandraAppender 將日誌資訊輸出到一個Apache的Cassandra資料庫
ConsoleAppender 將日誌資訊輸出到控制台
FailoverAppender 包含其他appenders,按順序嘗試,直至成功或結尾
FileAppender 一個OutputStreamAppender,將日誌輸出到檔案
FlumeAppender 將日誌輸出到Apache Flume系統
JDBCAppender 將日誌通過JDBC輸出到關係型資料庫
JMS Appender 將日誌輸出到JMS(Java Message Service)
JPAAppender 將日誌輸出到JPA架構
HttpAppender 通過HTTP輸出日誌
KafkaAppender 將日誌輸出到Apache Kafka
MemoryMappedFileAppender 將日誌輸出到一塊檔案關聯的記憶體
OutputStreamAppender 將日誌輸出到一個OutputStream
RandomAccessFileAppender 效能比FileAppender高20%~200%的檔案輸出Appender
RewriteAppender 允許對日誌資訊進行加工
RollingFileAppender 按log檔案最大長度限度產生新檔案
RollingRandomAccessFA 添加了緩衝的RollingFileAppender
RoutingAppender 將日誌事件分類,按條件分配給子appender
SMTPAppender 將日誌輸出到郵件
SocketAppender 將日誌輸出到一個Socket
SyslogAppender 是一個SocketAppender,將日誌輸出到遠程系統日誌
ZeroMQ/JeroMQ Appender 使用JeroMQ庫將日誌輸出到ZeroMQ終端

基本上已經覆蓋了百分之九十的業務情境了。相關的詳細說明或者配置大家自行搜尋或者查看官網說明。
官網地址:http://logging.apache.org/log4j/2.x/manual/appenders.html

logback內建Appender

log4j2一樣,內建的都差不多了。

名稱 描述
ConsoleAppender 將日誌輸出到控制台
FileAppender 將日誌輸出到檔案
RollingFileAppender 滾動檔案產生,按條件產生不同檔案,配合TriggeringPolicy使用
SocketAppender 輸出日誌到遠程執行個體中,明文傳輸
SSLSocketAppender 輸出日誌到遠程執行個體中,密文傳輸
SMTPAppender 將日誌輸出到郵件
DBAppender 日誌事件插入資料庫中,需要提前建立表
SyslogAppender 是一個SocketAppender,將日誌輸出到遠程系統日誌
SiftingAppender 可基於任何給定的即時屬性分開(或者篩選)日誌,如基於使用者會話分開日誌事件
AmqpAppender 將日誌輸出到MQ服務中

具體可查看:73327752 很詳細!

或者查看官網:https://logback.qos.ch/manual/appenders.html

自訂Appender

自訂Appender時,可以按實現的功能,適當的繼承(log4j2appender類基本上被設定成了final無法繼承)或者參考一些已有的功能,當然了也可以直接繼承其基類介面的。以下就簡單的樣本下,沒有實現特定的功能,⊙﹏⊙‖∣

log4j2自訂Appender

按官網的擴充說明,我們來簡單實現一個appender。

官網地址:http://logging.apache.org/log4j/2.x/manual/extending.html#Appenders

0.編寫自訂appender類,繼承AbstractAppender抽象實作類別:

MyLog4j2Appender.java

/** * 自訂log4j2輸出源,簡單的輸出到控制台 * @author oKong * *///這裡的 MyLog4j2 對應就是 xml中,/** *  *  <appenders> *     <MyLog4j2 name="customAppender" printString="一枚趔趄的猿"> *     </MyLog4j2> *  </appenders> * */@Plugin(name = "MyLog4j2", category = "Core", elementType = "appender", printObject = true)public class MyLog4j2Appender extends AbstractAppender {    String printString;   /**       *建構函式 可自訂參數 這裡直接傳入一個常量並輸出     *     */     protected MyLog4j2Appender(String name, Filter filter, Layout<? extends Serializable> layout,String printString) {        super(name, filter, layout);        this.printString = printString;    }    @Override    public void append(LogEvent event) {         if (event != null && event.getMessage() != null) {             // 此處自訂實現輸出                          // 擷取輸出值:event.getMessage().toString()             // System.out.print(event.getMessage().toString());             // 格式化輸出             System.out.print(printString + ":" + getLayout().toSerializable(event));          }            }        /**  接收設定檔中的參數      *      * @PluginAttribute 字面意思都知道,是xml節點的attribute值,如<oKong name="oKong"></oKong> 這裡的name 就是 attribute     * @PluginElement:表示xml子節點的元素,     * 如     *     <oKong name="oKong">     *         <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>     *     </oKong>     *   其中,PatternLayout就是 的 Layout,其實就是{@link Layout}的實作類別。     */     @PluginFactory    public static MyLog4j2Appender createAppender(            @PluginAttribute("name") String name,            @PluginElement("Filter") final Filter filter,             @PluginElement("Layout") Layout<? extends Serializable> layout,            @PluginAttribute("printString") String printString) {                if (name == null) {            LOGGER.error("no name defined in conf.");             return null;         }         //預設使用 PatternLayout        if (layout == null) {             layout = PatternLayout.createDefaultLayout();         }                 return new MyLog4j2Appender(name, filter, layout, printString);    }        @Override    public void start() {        System.out.println("log4j2-start方法被調用");        super.start();    }        @Override    public void stop() {        System.out.println("log4j2-stop方法被調用");        super.stop();    }}

簡單說明下,相關注意點:

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

1.使用自訂的appender。

log4j2-spring.xml

<?xml version="1.0" encoding="UTF-8"?> <configuration status="WARN" monitorInterval="30" packages="cn.lqdev.learning">     <!--定義appenders-->     <appenders>         <MyLog4j2 name="oKong" printString="一枚趔趄的猿(log4j2)">            <!--輸出日誌的格式-->             <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>         </MyLog4j2>     </appenders>     <!--然後定義logger,只有定義了logger並引入的appender,appender才會生效-->     <loggers>         <!--過濾掉spring和mybatis的一些無用的DEBUG資訊-->         <logger name="org.springframework" level="INFO"></logger>         <logger name="org.mybatis" level="INFO"></logger>         <!-- 自訂包下設定為INFO,則可以看見輸出的日誌不包含debug輸出了 -->         <logger name="cn.lqdev.learning" level="INFO"/>         <root level="all">             <appender-ref ref="oKong"/>         </root>     </loggers> </configuration>

這裡需要注意,需要在configuration中,加入屬性packages為自定類所在包名cn.lqdev.learning才會被掃描生效,不知道是否還有其他方法。

2.啟動後,就可以看見相關輸出了。

...部分省略...一枚趔趄的猿(log4j2):[14:47:43:751] [INFO] - org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:180) - Using a shared selector for servlet write/read一枚趔趄的猿(log4j2):[14:47:43:761] [INFO] - org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.start(TomcatEmbeddedServletContainer.java:216) - Tomcat started on port(s): 8080 (http)一枚趔趄的猿(log4j2):[14:47:43:764] [INFO] - org.springframework.boot.StartupInfoLogger.logStarted(StartupInfoLogger.java:57) - Started Chapter25Application in 2.03 seconds (JVM running for 3.164)一枚趔趄的猿(log4j2):[14:47:43:764] [INFO] - cn.lqdev.learning.springboot.chapter25.Chapter25Application.main(Chapter25Application.java:14) - Chapter25啟動!

不知道如何整合log4j2的,可以查看:《第二十三章:日誌管理之整合篇》

logback自訂Appender

logback的自訂,也是類似的,都是基於一個基類appender來實現。本身logback提供了AppenderBaseUnsynchronizedAppenderBase兩個抽象類別(同步和非同步),所以我們自訂時,只需要看實際業務繼承其中的一個即可。先看下其類繼承結構:

0.編寫自訂appender類。

MyLogbackAppender.java

@Getter@Setterpublic class MyLogbackAppender extends UnsynchronizedAppenderBase<ILoggingEvent>{    Layout<ILoggingEvent> layout;        //自訂配置     String printString;            @Override    public void start(){        //這裡可以做些初始化判斷 比如layout不能為null ,        if(layout == null) {            addWarn("Layout was not defined");        }        //或者寫入資料庫 或者redis時 初始化串連等等         super.start();    }        @Override    public void stop()    {       //釋放相關資源,如資料庫連接,redis線程池等等        System.out.println("logback-stop方法被調用");        if(!isStarted()) {            return;        }        super.stop();    }        @Override    public void append(ILoggingEvent event) {        if (event == null || !isStarted()){            return;        }             // 此處自訂實現輸出                          // 擷取輸出值:event.getFormattedMessage()             // System.out.print(event.getFormattedMessage());             // 格式化輸出                System.out.print(printString + ":" + layout.doLayout(event));              }}

也簡單說明下,相關注意點:

  • start方法:初始時調用。故在編寫如資料庫入庫,串連緩衝或者mq時,可以在這個方法裡面進行初始化操作。
  • stop:當停止時,調用。可做些資源釋放操作。

1.使用自訂appender:

logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?><configuration debug="false">    <!--定義記錄檔的儲存地址 勿在 LogBack 的配置中使用相對路徑 -->    <property name="LOG_HOME" value="/home" />    <!-- 控制台輸出 -->    <appender name="MyLogback"        class="cn.lqdev.learning.springboot.chapter25.config.MyLogbackAppender">        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">            <!-- 日誌收集最低記錄層級 -->            <level>INFO</level>        </filter>        <layout            class="ch.qos.logback.classic.PatternLayout">            <!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:層級從左顯示5個字元寬度%msg:日誌訊息,%n是分行符號 -->            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>        </layout>        <!-- 自訂參數 -->        <printString>一枚趔趄的猿(logback)</printString>    </appender>    <!-- 自訂包下設定為INFO,則可以看見輸出的日誌不包含debug輸出了 -->    <logger name="cn.lqdev.learning" level="INFO" />    <!-- 日誌輸出層級 -->    <root level="INFO">        <appender-ref ref="MyLogback" />    </root>    </configuration>

2.應用啟動,查看控制台輸出,效果是一樣的:

...部分省略...一枚趔趄的猿(logback):2018-08-25 15:01:57.486 [main] INFO  org.apache.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]一枚趔趄的猿(logback):2018-08-25 15:01:57.497 [main] INFO  org.apache.tomcat.util.net.NioSelectorPool - Using a shared selector for servlet write/read一枚趔趄的猿(logback):2018-08-25 15:01:57.520 [main] INFO  o.s.b.c.e.tomcat.TomcatEmbeddedServletContainer - Tomcat started on port(s): 8080 (http)一枚趔趄的猿(logback):2018-08-25 15:01:57.523 [main] INFO  c.l.l.springboot.chapter25.Chapter25Application - Started Chapter25Application in 54.349 seconds (JVM running for 55.377)一枚趔趄的猿(logback):2018-08-25 15:01:57.524 [main] INFO  c.l.l.springboot.chapter25.Chapter25Application - Chapter25啟動!
關於ShutdownHook

當你運行了以上的自訂appender後,停止應用時,你會發現定義的stop方法並沒有被執行。還需要配置一個ShutdownHook系統鉤子,使得在jvm在退出時之前會調用。

一點知識

我們知道,在java中,註冊一個關閉鉤子是很簡單的,使用Runtime類即可,具體用法如下:

        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {            @Override            public void run() {                // 執行資源釋放操作            }        }));

而在SpringBoot中,只需要配置logging.register-shutdown-hooktrue即可。

logging.register-shutdown-hook=true

對於logback而言,也可以在logback-spring.xml中配置:

<shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>

也是可以的。再或者在啟動類手動註冊這個DelayingShutdownHook也是可以的

這裡有個坑,log4j2而言,配置失效了。Google了一圈也沒有發現解決方案,網上的方案試了一遍都是不行。。很尷尬。要是使用log4j2的話,可以取巧下,在start()方法裡面,註冊鉤子之後調用stop方法。希望有知道的大神分享下如何解決!

參考資料
  1. 78494458
  2. http://logging.apache.org/log4j/2.x/manual/appenders.html
  3. http://logging.apache.org/log4j/2.x/manual/extending.html#Appenders
  4. https://logback.qos.ch/manual/appenders.html
  5. 75353854
總結

本文主要是簡單介紹了log4j2logback自訂appender相關知識。實現起來是相對簡單的,需要注意當涉及需要關閉釋放相關資源時,需要確認下關閉前是否有被調用,不然可能造成串連未關閉等行為,避免不必要的問題。關於最後使用log4j2關閉鉤子未生效問題,由於現在都預設使用logback了,這個問題就不深究了,還望有知道的同學分享下解決方案!謝謝!同時由於沒有對兩個架構有過多的深入瞭解,只能點到為止了,若文中有誤,還望指出!

最後

目前互連網上很多大佬都有SpringBoot系列教程,如有雷同,請多多包涵了。原創不易,碼字不易,還希望大家多多支援。若文中有所錯誤之處,還望提出,謝謝。

老生常談
  • 個人QQ:499452441
  • 公眾號:lqdevOps

個人部落格:http://blog.lqdev.cn

完整樣本:https://github.com/xie19900123/spring-boot-learning/tree/master/chapter-25

原文地址:http://blog.lqdev.cn/2018/08/25/springboot/chapter-twenty-five/

SpringBoot | 第二十五章:日誌管理之自訂Appender

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.