LogBack入數據庫重寫


項目需要:將info以及error的日志信息寫入到數據庫中;同時所有的日志都要寫入到日志文件中。

可以封裝一下,在基類的logError/logInfo中調用了log.error()以及log.info之后在調用一次LoggerDBService進行寫入;但是這樣就意味着"不美",日志還需要調用兩次;而且因為早期設計問題,並不是所有的日志都采用基類的logError/logInfo。

看了一下logback源碼,分析了一下其機制,於是決定采用重寫DBAppender並結合AsyncAppender進行異步調用的方式進行實現。對於日志類操作,如果寫入數據庫這種比較消耗資源和時間的事情進行同步,很不值,於是才決定通過異步方式進行處理。

AsyncAppender(AA)是一個獨立的Appender,放置到它下面的appender(通過appender-ref屬性進行設定)也就不需要在放置到root節點下面,因為AsyncAppender設計的邏輯就是:在root下面引用該Appender,然后通過AA進行調度此appender進行日志輸出。

    <appender name="asyncLog" class="ch.qos.logback.classic.AsyncAppender">

        <discardingThreshold>0</discardingThreshold>

        <queueSize>10000</queueSize>

        <appender-ref ref="dbLog"/>

    </appender>

    <root level="debug">

        <appender-ref ref="stdout" />

        <appender-ref ref="txtLog" />

        <appender-ref ref="asyncLog"/>

    </root>

至於調度的邏輯,首先要明白一個概念: LoggingEvent(日志事件),任何一次logback的輸出動作,都是一個LogEvent,logEvent里面包括了很多信息,包括要寫入的內容,級別等等一系列信息。AA的調度邏輯就是將每次的輸出動作放到內置的BlockingQueue中;然后再從BlockingQueue中取出來交給關聯的Appender進行處理。LogEvent和Appender是無關的,前者是內容;后者是處理內容。

ILoggingEvent是每次傳入append方法的入參。

重寫的DB繼承自UnsynchronizedAppenderBase<ILoggingEvent>,重寫了append方法,里面使用自己的DBManager來進行數據庫處理;我沒有繼承DBAppenderBase,是因為里面固化了一些內容,很多都是不需要的;而且采用它,就必須要指定數據庫的連接字符串,用戶名密碼,這意味着同樣的數據庫要在兩個地方進行配置:應用級別的配置文件以及logback的配置文件。於是我索性就直接繼承了UnsynchronizedAppenderBase<ILoggingEvent>,沒有重用DBAppender相關內容。

public class TransportDBLoggerAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {

 

    @Override

    public void append(ILoggingEvent eventObject) {

        try {

            String content = eventObject.getFormattedMessage();

            System.out.println("content內容是: " + content);

            Map<String, String> map = new HashMap<String, String>();

            map.put("LOG_LEVEL", eventObject.getLevel().levelStr);

            map.put("CONTENT", content.replace("'", "''"));

            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

            map.put("CREATE_DATE", sdf.format(new Date()));

            // 拼接SQL語句,然后執行

            … …

        } catch (Throwable sqle) {

            String errorMsg = CommonUtil.getTrace(sqle);

            System.out.println(errorMsg);

        }

    }

}

在配置文件中,還需要指定過濾級別,因為只需要info和error需要寫入到數據庫中;在過來級別logback提供了兩種方式來進行處理,分別是LevelFilter以及ThresholderFilter,前者只能指定過來特定的級別的操作(ACCEPT,NEUTRAL,DENY),后者則是過濾指定級別,之下的將會被拒絕(DENY)。

    <appender name="dbLog" class="test.MyDBLoggerAppender">

        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">

            <level>INFO</level>

        </filter>

    </appender>

調用測試代碼,發現並沒有走入庫邏輯,后來才發現原來是因為測試代碼走完后,整個應用退出,於是logback也退出了;換言之,放置到Queue里面的內容根本就沒有被處理,logback的線程也就消亡了。於是嘗試讓測試線程阻塞10秒鍾,至此,才看到入庫的動作以及數據。

        Logger logger = LoggerFactory.getLogger(this.getClass());

        logger.info("test transDbLogger INFO");

        logger.error("test transDbLogger ERROR");

        logger.debug("test transDbLogger DEBUG");

        System.out.println("OK, complete!");

        try {

            Thread.sleep(10000);

        } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

 

 


免責聲明!

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



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