Java日志系統(學習總結)


JDK Logging

從jdk1.4起,JDK開始自帶一套日志系統。JDK Logger最大的優點就是不需要任何類庫的支持,只要有Java的運行環境就可以使用。相對於其他的日志框架,JDK自帶的日志可謂是雞肋,無論易用性,功能還是擴展性都要稍遜一籌,所以在商業系統中很少直接使用。Java標准庫內置了日志包java.util.logging,我們可以直接用。

JDK的Logging定義了7個日志級別,從嚴重到普通:

  • SEVERE
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST

因為默認級別是INFO,因此,INFO級別以下的日志,不會被打印出來。使用日志級別的好處在於,調整級別,就可以屏蔽掉很多調試相關的日志輸出。

使用Java標准庫內置的Logging有以下局限:

Logging系統在JVM啟動時讀取配置文件並完成初始化,一旦開始運行main()方法,就無法修改配置;

配置不太方便,需要在JVM啟動時傳遞參數-Djava.util.logging.config.file=<config-file-name>

因此,Java標准庫內置的Logging使用並不是非常廣泛。

Commons Logging日志庫

Commons Logging是使用最廣泛的日志模塊;

和Java標准庫提供的日志不同,Commons Logging是一個第三方日志庫,它是由Apache創建的日志模塊。

Commons Logging的特色是,它可以掛接不同的日志系統,並通過配置文件指定掛接的日志系統。默認情況下,Commons Loggin自動搜索並使用Log4j(Log4j是另一個流行的日志系統),如果沒有找到Log4j,再使用JDK Logging。

使用Commons Logging只需要和兩個類打交道,並且只有兩步:

第一步,通過LogFactory獲取Log類的實例; 第二步,使用Log實例的方法打日志。

Commons Logging定義了6個日志級別:

  • FATAL
  • ERROR
  • WARNING
  • INFO
  • DEBUG
  • TRACE

默認級別是INFO

使用Commons Logging時,如果在靜態方法中引用Log,通常直接定義一個靜態類型變量:

// 在靜態方法中引用Log:
public class Main {
    static final Log log = LogFactory.getLog(Main.class);

    static void foo() {
        log.info("foo");
    }
}

在實例方法中引用Log,通常定義一個實例變量:

// 在實例方法中引用Log:
public class Person {
    protected final Log log = LogFactory.getLog(getClass());

    void foo() {
        log.info("foo");
    }
}

注意到實例變量log的獲取方式是LogFactory.getLog(getClass()),雖然也可以用LogFactory.getLog(Person.class),但是前一種方式有個非常大的好處,就是子類可以直接使用該log實例。例如:

// 在子類中使用父類實例化的log:
public class Student extends Person {
    void bar() {
        log.info("bar");
    }
}

此外,Commons Logging的日志方法,例如info(),除了標准的info(String)外,還提供了一個非常有用的重載方法:info(String, Throwable),這使得記錄異常更加簡單:

try {
    ...
} catch (Exception e) {
    log.error("got exception!", e);
}

Log4j日志框架

Commons Logging,可以作為“日志接口”來使用。而真正的“日志實現”可以使用Log4j。

Log4j是一種非常流行的日志框架,最新版本是2.x。

Log4j是一個組件化設計的日志系統,它的架構大致如下:

image-20200819091506541

當我們使用Log4j輸出一條日志時,Log4j自動通過不同的Appender把同一條日志輸出到不同的目的地。例如:

  • console:輸出到屏幕;
  • file:輸出到文件;
  • socket:通過網絡輸出到遠程計算機;
  • jdbc:輸出到數據庫

在輸出日志的過程中,通過Filter來過濾哪些log需要被輸出,哪些log不需要被輸出。例如,僅輸出ERROR級別的日志。

最后,通過Layout來格式化日志信息,例如,自動添加日期、時間、方法名稱等信息。

上述結構雖然復雜,但我們在實際使用的時候,並不需要關心Log4j的API,而是通過配置文件來配置它。

以XML配置為例,使用Log4j的時候,我們把一個log4j2.xml的文件放到classpath下就可以讓Log4j讀取配置文件並按照我們的配置來輸出日志。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
	<Properties>
        <!-- 定義日志格式 -->
		<Property name="log.pattern">%d{MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36}%n%msg%n%n</Property>
        <!-- 定義文件名變量 -->
		<Property name="file.err.filename">log/err.log</Property>
		<Property name="file.err.pattern">log/err.%i.log.gz</Property>
	</Properties>
    <!-- 定義Appender,即目的地 -->
	<Appenders>
        <!-- 定義輸出到屏幕 -->
		<Console name="console" target="SYSTEM_OUT">
            <!-- 日志格式引用上面定義的log.pattern -->
			<PatternLayout pattern="${log.pattern}" />
		</Console>
        <!-- 定義輸出到文件,文件名引用上面定義的file.err.filename -->
		<RollingFile name="err" bufferedIO="true" fileName="${file.err.filename}" filePattern="${file.err.pattern}">
			<PatternLayout pattern="${log.pattern}" />
			<Policies>
                <!-- 根據文件大小自動切割日志 -->
				<SizeBasedTriggeringPolicy size="1 MB" />
			</Policies>
            <!-- 保留最近10份 -->
			<DefaultRolloverStrategy max="10" />
		</RollingFile>
	</Appenders>
	<Loggers>
		<Root level="info">
            <!-- 對info級別的日志,輸出到console -->
			<AppenderRef ref="console" level="info" />
            <!-- 對error級別的日志,輸出到err,即上面定義的RollingFile -->
			<AppenderRef ref="err" level="error" />
		</Root>
	</Loggers>
</Configuration>

雖然配置Log4j比較繁瑣,但一旦配置完成,使用起來就非常方便。對上面的配置文件,凡是INFO級別的日志,會自動輸出到屏幕,而ERROR級別的日志,不但會輸出到屏幕,還會同時輸出到文件。並且,一旦日志文件達到指定大小(1MB),Log4j就會自動切割新的日志文件,並最多保留10份。

有了配置文件還不夠,因為Log4j也是一個第三方庫,我們需要從這里下載Log4j,解壓后,把以下3個jar包放到classpath中:

  • log4j-api-2.x.jar
  • log4j-core-2.x.jar
  • log4j-jcl-2.x.jar

因為Commons Logging會自動發現並使用Log4j,所以,把上一節下載的commons-logging-1.2.jar也放到classpath中。

要打印日志,只需要按Commons Logging的寫法寫,不需要改動任何代碼,就可以得到Log4j的日志輸出。

在開發階段,始終使用Commons Logging接口來寫入日志,並且開發階段無需引入Log4j。如果需要把日志寫入文件, 只需要把正確的配置文件和Log4j相關的jar包放入classpath,就可以自動把日志切換成使用Log4j寫入,無需修改任何代碼。

SLF4J和Logback

Commons Logging和Log4j這一對好基友,它們一個負責充當日志API,一個負責實現日志底層,搭配使用非常便於開發。

其實SLF4J類似於Commons Logging,也是一個日志接口,而Logback類似於Log4j,是一個日志的實現。

為什么有了Commons Logging和Log4j,又會蹦出來SLF4J和Logback?這是因為Java有着非常悠久的開源歷史,不但OpenJDK本身是開源的,而且我們用到的第三方庫,幾乎全部都是開源的。開源生態豐富的一個特定就是,同一個功能,可以找到若干種互相競爭的開源庫。

因為對Commons Logging的接口不滿意,有人就搞了SLF4J。因為對Log4j的性能不滿意,有人就搞了Logback。

對比一下Commons Logging和SLF4J的接口:

image-20200819093427782

使用SLF4J和Logback和前面講到的使用Commons Logging加Log4j是類似的,先分別下載SLF4JLogback,然后把以下jar包放到classpath下:

  • slf4j-api-1.7.x.jar
  • logback-classic-1.2.x.jar
  • logback-core-1.2.x.jar

然后使用SLF4J的Logger和LoggerFactory即可。和Log4j類似,我們仍然需要一個Logback的配置文件,把logback.xml放到classpath下,配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
		</encoder>
	</appender>

	<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<encoder>
			<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
			<charset>utf-8</charset>
		</encoder>
		<file>log/output.log</file>
		<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
			<fileNamePattern>log/output.log.%i</fileNamePattern>
		</rollingPolicy>
		<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
			<MaxFileSize>1MB</MaxFileSize>
		</triggeringPolicy>
	</appender>

	<root level="INFO">
		<appender-ref ref="CONSOLE" />
		<appender-ref ref="FILE" />
	</root>
</configuration>

從目前的趨勢來看,越來越多的開源項目從Commons Logging加Log4j轉向了SLF4J加Logback。

參考資料

https://www.liaoxuefeng.com/


免責聲明!

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



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