天天看點

log4j+slf4j日志管理系統背景log4j日志管理架構slf4j簡單日志門面spring項目引入Log4j後記

背景

當我們在項目中想使用日志系統幫我們進行日志記錄管理時可以使用log4j+slf4j,這兩個日志架構(slf4j其實并不是日志架構,而是一個日志facade)。

log4j日志管理架構

log4j是Apache下面的日志記錄管理架構,它可以将日志輸出到控制台、檔案、資料庫、郵件等等。這些隻需要簡單的配置一下配置檔案即可完成。log4j有兩種配置檔案格式:log4j.xml和log4j.properties,使用log4j.properties可以簡單的使用鍵值對形式進行配置,但是功能實作來說相對log4j.xml差一些。比如級别過濾,是以當兩種配置檔案同時存在的時候以log4.xml為準。log4j優于System.out.println()是在于他在禁止一些資訊輸出的同時不會妨礙其他的輸出能力,他根據開發者的配置進行輸出。

log4j重要元件

log4j從功能實作來說,主要由三大元件組成:Logger、Appender、Layout。 他們的關系如下:

log4j+slf4j日志管理系統背景log4j日志管理架構slf4j簡單日志門面spring項目引入Log4j後記

Logger是日志記錄器(也可以說是日志級别)、Appender是日志輸出目的地、Layout是日志輸出格式控制器,一個日志記錄器可以對應多個輸出目的地,每個輸出目的地有特定的輸出格式。

Logger使用

log4j級别

log4j提供了7種級别:ALL<DEBUG<INFO<WARN<ERROR<FATAL<OFF,建議使用DEBUG到ERROR,實際項目中也是基本使用這5個級别。log4j級别原則是隻要大于指定優先級就進行輸出。

代碼中使用Logger

private static final Logger logger = Logger.getLogger(ClassName.Class);
logger.debug("debug information");
logger.info("info information");
logger.warn("warn information");
logger.error("error information");
           

實際ClassName.class可以自定義任意字元串,但是建議使用類名。

配置檔案log4j.xml定義logger

<logger name="loggerName" additivity="true|false">
	<appender-ref ref="appenderName1"/>
	<appender-ref ref="appenderName1"/>
</logger>
           

Appender

log4j将日志輸出源稱為Appender,每個輸出源一個Appender,一個logger可以對應多個Appender。

常見Appender

org.apache.log4j.ConsoleAppender:控制台輸出 org.apache.log4j.FileAppender:輸出到檔案 org.apache.log4j.DailyRollingFileAppender:每天産生日志輸出到檔案 org.apache.log4j.RollingFileAppender:檔案達到指定大小産生新檔案 org.apache.log4j.WriterAppender:以流格式發送到指定位置 org.apache.log4j.jdbc.JDBCAppender:輸出到資料庫

配置檔案log4j.xml定義Appender

<appender name="myConsole" class="org.apache.log4j.ConsoleAppender">
	<layout class="org.apache.log4j.PatternLayout">
		<param name="ConversionPattern"  value="%d{yyyy-MM-dd HH:mm:dd,SSS} [%t] [%c:%L] [%p] - %m%n"/>
	</layout>
	<filter class="org.apache.log4j.varia.LevelRangeFilter">
		<param  name="LevelMin" value="INFO"/>
		<param  neme="LevelMax" value="ERROR"/>
	</filter>
</appender>
           

Layout

每個Appender可以指定特定的輸出格式,log4j将輸出格式稱為Layout。

常見Layout

org.apache.log4j.HTMLLayout:HTML格式Layout org.apache.log4j.PatternLayout:自定義格式Layout(常用) org.apache.log4j.SimpleLayout:包含日志資訊級别及資訊 org.apache.log4j.TTCCLayout:包含生産時間、類别、級别等

常用PatternLayout

通常PatternLayout最為常用,可以根據自己的需求自定義輸出格式,使用格式化符指代特定資訊: %m 輸出代碼中指定的消息 

%p 輸出優先級,即DEBUG,INFO,WARN,ERROR,FATAL 

%r 輸出自應用啟動到輸出該log資訊耗費的毫秒數 

%c 輸出所屬的類目,通常就是所在類的全名 

%t 輸出産生該日志事件的線程名 

%n 輸出一個回車換行符,Windows平台為“\r\n”,Unix平台為“\n” 

%-x 左對其資訊

log4j.properties配置

#定義根Logger,後面為根Logger定義的級别,再後面為Appender名稱,可以有多個,根logger對應的多個appender。每個Appender再在後面定義
log4j.rootLogger=INFO, AppenderName1,AppenderName2
log4j.additivity.org.apache=false:表示logger不會繼承父Logger的appender輸出,預設為true及輸出

#定義輸出控制台的Appender
log4j.appender.AppenderName1=org.apache.log4j.ConsoleAppender		#指定輸出源
log4j.appender.AppenderName1.layout=org.apache.log4j.PatternLayout	#指定輸出樣式
log4j.appender.Threshold=WARN		#設定該appender的最低級别
log4j.appender.AppenderName1.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n	#設定輸出樣式
log4j.appender.AppenderName2=org.apache.log4j.DailyRollingFileAppender
log4j.appender.AppenderName2.file=xxx.log
log4j.appender.AppenderName2.append=true	#false為覆寫制定檔案内容,true為每天的日志追加到指定檔案
log4j.appender.AppenderName2.DatePattern='.'yyyy-MM	#每月滾動一次日志檔案,及每月産生一個日志檔案,當月名稱為xxx.log,上個月的為xxx.log.yyy-MM,這個也可以設定為:'.'yyyy-MM 每月、'.'yyyy-ww 每周、'.'yyyy-MM-dd 每天、'.'yyyy-MM-dd-a每天兩次、'.'yyyy-MM-dd-HH 每小時、'.'yyyy-MM-dd-HH-mm 每分鐘

log4j.appender.AppenderName2.layout=org.apache.log4j.PatternLayout
log4j.appender.AppenderName2.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n

log4j.logger.xxx=INFO,AppenderName2     #指定logger的appender
           

log4j.xml配置

了解了log4j.properties中的含義,在看 log4j.xml配置就能基本對應上了。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c:%L] [%p] - %m%n"/>
        </layout>
        <!-- 過濾設定輸出級别 -->
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="LevelMin" value="INFO" />
            <param name="LevelMax" value="ERROR" />
        </filter>
    </appender>

    <!-- 錯誤日志Appender-->
    <appender name="error" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="File" value="/opt/yjz/server/jetty-6.1.22/logs/index_error.log"/>
        <param name="Append" value="true"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c:%L] [%p] - %m%n"/>
        </layout>
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="LevelMin" value="ERROR"/>
            <param name="LevelMax" value="ERROR"/>
        </filter>
    </appender>

    <!-- INFO到ERROR級别Appender -->
    <appender name="index" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="File" value="/opt/yjz/server/jetty-6.1.22/logs/index_all.log"/>
        <param name="Append" value="true"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c:%L] [%p] - %m%n"/>
        </layout>
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="LevelMin" value="INFO"/>
            <param name="LevelMax" value="ERROR"/>
        </filter>
    </appender>

    <!-- INFO到WARN級别Appender-->
    <appender name="info" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="File" value="/opt/yjz/server/jetty-6.1.22/logs/info.log"/>
        <param name="Append" value="true"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c:%L] [%p] - %m%n"/>
        </layout>
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="LevelMin" value="INFO"/>
            <param name="LevelMax" value="WARN"/>
        </filter>
    </appender>

    <appender name="debug" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="File" value="/opt/yjz/server/jetty-6.1.22/logs/debug.log"/>
        <param name="Append" value="true"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c:%L] [%p] - %m%n"/>
        </layout>
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="LevelMin" value="DEBUG"/>
            <param name="LevelMax" value="DEBUG"/>
        </filter>
    </appender>

    <!-- additivity是否繼承父Logger的appender,設定為false,Logger隻會在自己的appender輸出,如果是true,則會在父Appender輸出 -->
    <logger name="com.yjz" additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>

    <logger name="com.yjz.child" additivity="false">
        <appender-ref ref="info"/>
        <appender-ref ref="error"/>
    </logger>

    <root>
        <level value="debug"/>
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="index" />
    </root>

</log4j:configuration>
           

級别優化判斷

if(logger.isDebugEnable()){
	logger.debug("debug inforamtion"+name);
}
           

我們在一些成熟架構中經常看到這種判斷,不是說我們在配置檔案中已經過濾了輸出檔案嗎,為什麼這裡還要判斷?原因在于優化,字元串的拼接操作也會影響運作的效率,如果先判斷logger的級别,如果該級别已經過濾,就省去了字元串拼接操作了。

slf4j簡單日志門面

簡單日志門面(simple logging Facade for java),slf4j并不是一個完整的日志架構,他隻是為各種日志架構提供統一的API接口,及根據slf4j提供的接口規則使用Logger,而在部署的時候根據自己的需求配置自己希望使用的日志系統。可以說他是将各個日志系統接口進行了抽象,抽象為統一的slf4j接口,這樣隻要項目中使用slf4j API,我們可以随時更換日志系統,而不用更改代碼。

slf4j的jar包

當我們使用slf4j的時候,需要加入他的jar包slf4j-api.xxx.jar,如果日志系統使用的是log4j,則在加入slf4j-log4j.xxx.jar,如果使用JDK自帶的logging日志系統隻需将slf4j-log4j.xxx.jar替換為slf4j-jdkxx.xxx.jar

slf4j門面原理

log4j+slf4j日志管理系統背景log4j日志管理架構slf4j簡單日志門面spring項目引入Log4j後記

(圖檔來源于網上)

Logger API

private static final Logger logger = LoggerFactory.getLogger(ClassName.class);
logger.info("info information");
           

Java中Logger如何比對Appender

當我們在代碼中定義logger的時候,getLogger通過ClassName定義的logger(其實可以自定義),但是我們在log4j.xml并沒有看到以這個名字定義的logger,那麼這個日志資訊會輸出在哪裡?原來這裡是有一個繼承的問題,我們通過ClassName.class擷取的是類名稱,比如為com.yjz.xxx.yyyy,首先要知道,所有的logger都繼承自root,是以他們的資訊會在rootLogger定義的Appender中輸出,還有就是我們在log4j.xml中定義了com.yjz這個logger,這樣屬于com.yjz包下的類都會繼承這個logger,是以會在這個logger定義的appender輸出。為了不重複輸出我們使用additivity="false",這樣隻會輸出一遍。

spring項目引入Log4j

在web.xml中引入

<context-param>
	<param-name>log4jConfigLocation</param-name>
	<param-value>/WEB-INF/conf/log4j.xml</param-value>
</context-param>
<listener>
	<listerner-class>org.springframework.web.util.Log4jConfigListener</listerner-class>
</listener>
           

後記

這裡隻是這兩天簡單的應用了log4j+slf4j日志管理系統,對于其更深入的學習待有時間詳細了解。