天天看點

Java常用的日志架構對比

作為Java開發人員,對于日志記錄架構一定非常熟悉。而且幾乎在所有應用裡面,一定會用到各種各樣的日志架構用來記錄程式的運作資訊。而對于一個成熟的Java應用,這個是必不可少的。在開發和調試階段,日志可以幫助我們更快的定位問題;而在應用的運維過程中,日志系統又可以幫助我們記錄大部分的異常資訊,通常很多企業會通過收集日志資訊來對系統的運作狀态進行實時監控預警。

總體概覽

image.png

目前的日志架構有JDK自帶的

logging

log4j1

log4j2

logback

,這些架構都自己定制了日志 API ,并且有相應的實作;目前用于實作日志統一的架構

Apache commons-logging

slf4j

,遵循面向接口程式設計的原則,這兩大架構可以讓使用者在程式運作期間去選擇具體的日志實作系統(

log4j1\log4j2\logback

等)來記錄日志,是統一抽象出來的一些接口。

日志級别

log4j

定義了8個級别的log(除去OFF和ALL,可以說分為6個級别),

優先級從高到低依次為:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。

ALL

最低等級的,用于打開所有日志記錄。

TRACE

很低的日志級别,一般不會使用。

DEBUG

指出細粒度資訊事件對調試應用程式是非常有幫助的,主要用于開發過程中列印一些運作資訊。

INFO

消息在粗粒度級别上突出強調應用程式的運作過程。這個可以用于生産環境中輸出程式運作的一些重要資訊。

WARN

表明會出現潛在錯誤的情形,有些資訊不是錯誤資訊,但是也要給開發者的一些提示。

ERROR

指出發生錯誤的資訊,可能會導緻系統出錯或是當機等,必須要避免

FATAL

指出每個嚴重的錯誤事件将會導緻應用程式的退出。這個級别比較高了。重大錯誤,這種級别你可以直接停止程式了。

OFF

最高等級,用于關閉所有日志記錄。

Log4j

官網位址:

https://logging.apache.org/log4j/1.2/

簡介:

Apache 的一個開放源代碼項目,通過使用Log4j,我們可以控制日志資訊輸送的目的地是控制台、檔案、GUI元件、甚至是套接口伺服器、NT的事件記錄器、UNIX Syslog守護程序等;使用者也可以控制每一條日志的輸出格式;通過定義每一條日志資訊的級别,使用者能夠更加細緻地控制日志的生成過程。這些可以通過一個 配置檔案來靈活地進行配置,而不需要修改程式代碼。

在JDK 1.3及以前,Java打日志依賴

System.out.println()

,

System.err.println()

或者

e.printStackTrace()

,Debug日志被寫到

STDOUT

流,錯誤日志被寫到

STDERR

流。這樣打日志有一個非常大的缺陷,即無法定制化,且日志粒度不夠細。log4j是在這樣的環境下誕生的,它是一個裡程碑式的架構,它定義的

Logger

Appender

Level

等概念如今已經被廣泛使用。

https://mvnrepository.com/

中可以查到,log4j1從2005年11月更新到2012年3月,後面就沒再更新了

最新的依賴(May 26, 2012)

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
           

2015年8月5日,項目管理委員會宣布

Log4j 1.x

已達到使用壽命。建議使用者使用

Log4j 1

更新到

Apache Log4j 2

Log4j2

官網位址:

https://logging.apache.org/log4j/2.x/

Log4j2

Log4j1

的更新版本。

Log4j2

基本上把

Log4j1

版本的核心全部重構掉了,而且基于

Log4j1

做了很多優化和改變。并提供了

Logback

中可用的許多改進,同時修複了

Logback

架構中的一些固有問題。

Log4j2最新的依賴(Mar 11, 2018)

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.11.0</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.11.0</version>
</dependency>
           

SpringBoot也有Log4j2相關的依賴

不過

SpringBoot

自帶的

jar

包已經夠用了

引入依賴(Jun 14, 2018)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
    <version>2.0.3.RELEASE</version>
</dependency>
           

引入之後可以看到外部jar包多了三個關于log4j2的

關于上面的包

log4j-to-slf4j

log4j2

的接口适配到

slf4j

上,不能和

log4j-slf4j-impl

同時存在

log4j-api

包含

.class

但是隻是一堆接口而已,實際使用需要

log4j

log4j-core

.class

.java

也就是源碼

jul

指的是

java.util.logging

,是

java

内置的日志子產品

簡介:受

Log4j

啟發,Sun在

Java1.4

版本中引入了

java.util.logging

,但是

jul

功能遠不如

log4j

完善,開發者需要自己編寫

Appenders

(Sun稱之為

Handlers

),且隻有兩個

Handlers

可用(

Console

File

),

jul

Java1.5

以後性能和可用性才有所提升。

不是SpringBoot項目需要引用

jul

的話可以加

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-jul</artifactId>
    <version>2.11.0</version>
</dependency>
           

Log4j2的使用

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class SpringcloudEurekaApplication {
    static Logger logger = LogManager.getLogger(SpringcloudEurekaApplication.class);
    public static void main(String[] args) {
        logger.info("Current Time: {}", System.currentTimeMillis());
        logger.info("Current Time: " + System.currentTimeMillis());
        logger.info("Current Time: {}", System.currentTimeMillis());
        logger.trace("trace log");
        logger.warn("warn log");
        logger.debug("debug log");
        logger.info("info log");
        logger.error("error log");
    }
}
           

啟動項目後控制台輸出為

SLF4J

https://www.slf4j.org/

SLF4J(Simple Logging Facade for Java)用作各種日志架構(java.util.logging,logback,log4j)的簡單外觀或抽象,允許最終使用者在部署 時插入所需的日志架構。

最新依賴(Mar 21, 2018)

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.8.0-beta2</version>
</dependency>
           

Springboot項目不需要引入任何依賴都可以使用

用法

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SpringcloudEurekaApplication{
    private static final Logger logger = LoggerFactory.getLogger(SpringcloudEurekaApplication.class);
    public static void main(String[] args) {
        logger.info("Current Time: {}", System.currentTimeMillis());
        logger.info("Current Time: " + System.currentTimeMillis());
        logger.info("Current Time: {}", System.currentTimeMillis());
        logger.trace("trace log");
        logger.warn("warn log");
        logger.debug("debug log");
        logger.info("info log");
        logger.error("error log");
    }
}
           

通常輸出日志開銷非常大,

SLF4J

通過

{}

作為占位符的方式輸出字元串,相比字元串拼接的方式,效率有顯著的提升。

logback

https://logback.qos.ch/

logback

log4j

是同一個作者創作,它是

log4j

的更新版

Logback的體系結構足夠通用,以便在不同情況下應用。

logback分為三個子產品:

logback-core

logback-classic

logback-access

logback-core

子產品為其他兩個子產品奠定了基礎。

logback-classic

子產品可以被同化為

log4j

的顯着改進版本。

logback-classic

本身實作了

SLF4J API

,是以您可以在

logback

和其他日志架構(如

log4j

java.util.logging(JUL)

)之間來回切換。

logback-access

子產品​​與

Servlet

容器(如Tomcat和Jetty)內建,以提供

HTTP

通路日志功能。可以在

logback-core

之上輕松建構自己的子產品。

Logback的核心對象:Logger、Appender、Layout

Logback主要建立于

Logger

Appender

Layout

這三個類之上。

Logger

:日志的記錄器,把它關聯到應用的對應的

context

上後,主要用于存放日志對象,也可以定義日志類型、級别。

Logger

對象一般多定義為靜态常量.

Appender

:用于指定日志輸出的目的地,目的地可以是控制台、檔案、遠端套接字伺服器、

MySQL

PostreSQL

Oracle

和其他資料庫、

JMS

和遠端

UNIX Syslog

守護程序等。

Layout

:負責把事件轉換成字元串,格式化的日志資訊的輸出。

具體使用

引入依賴(Feb 11, 2018),分别對應

logback

上的三個子產品

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.3.0-alpha4</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>1.3.0-alpha4</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-access</artifactId>
    <version>1.3.0-alpha4</version>
</dependency>
           

注:如果在SpringBoot下是不需要引入的

Spring Boot内部日志系統使用的是

Commons Logging

,但開放底層的日志實作。預設為會

Java Util Logging, Log4J, Log4J2和Logback

提供配置。每種情況下都會預先配置使用控制台輸出,也可以使用可選的檔案輸出。

因為在SpringBoot中本身就内置了日志功能,在

spring-boot-starter

依賴中

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
</dependency>
           

logback的配置介紹

如果沒有配置檔案,那麼

logback

預設地會調用

BasicConfigurator

,建立一個最小化配置。最小化配置由一個關聯到根

logger

ConsoleAppender

組成。輸出用模式為

%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n

PatternLayoutEncoder

進行格式化。

root

logger

預設級别是

DEBUG

官方推薦

官方推薦使用的

xml

名字的格式為:

logback-spring.xml

而不是

logback.xml

,因為帶

spring

字尾的可以使用

<springProfile>

這個标簽。

Logback配置檔案的基本結構

<configuration>

開頭,後面有任意個

<appender>

元素,有任意個

<logger>

元素,有最多一個

<root>

元素。

Logback

配置檔案的文法非常靈活。正因為靈活,是以無法用

DTD XML schema

進行定義。

1、根節點

<configuration>

,包含下面三個屬性:

scan

: 當此屬性設定為

true

時,配置檔案如果發生改變,将會被重新加載,預設值為

true

scanPeriod

: 設定監測配置檔案是否有修改的時間間隔,如果沒有給出時間機關,預設機關是毫秒。當

scan

true

時,此屬性生效。預設的時間間隔為1分鐘。

debug

true

時,将列印出

logback

内部日志資訊,實時檢視

logback

運作狀态。預設值為

false

<configuration scan="true" scanPeriod="60 seconds" debug="false"> 
  <!--省略其他配置--> 
</configuration>
           

2.子節點,分别有<appender>,<root>,<logger>,<property>,<contextName>,<conversionRule>,<springProfile>

1.子節點

<appender>

:Logback将執行日志事件輸出的元件稱為Appender,實作的

Appender

必須繼承

ch.qos.logback.core.Appender

接口

它有兩個必要屬性

name

class

name

指定

appender

名稱,

class

appender

的全限定名

注:

class="ch.qos.logback.core.rolling.RollingFileAppender"

常見的日志輸出到檔案,随着應用的運作時間越來越長,日志也會增長的越來越多,将他們輸出到同一個檔案并非一個好辦法。

RollingFileAppender

用于切分檔案日志

<appender>

有四個子節點

<encoder>

:對日志進行格式化。必須指定,否則不會往檔案輸出内容

%d{HH: mm:ss.SSS}

——日志輸出時間

%thread

——輸出日志的程序名字,這在Web應用以及異步任務進行中很有用

%-5level

——日志級别,并且使用5個字元靠左對齊

%logger{36}

——日志輸出者的名字

%msg

——日志消息

%n

——平台的換行符

<rollingPolicy>

:循環政策:基于時間建立日志檔案。當發生日志切換時,

RollingFileAppender

的切換行為。例如日志檔案名的修改

常用子節點

<maxHistory>

表示隻保留最近天數的日志,最好設定下,以防止日志填滿整個磁盤空間。

<timeBasedFileNamingAndTriggeringPolicy>

<maxFileSize>

是對日志大小進行切割,設定每個日志檔案的最大值

<filter>

:過濾器,執行一個過濾器會有傳回個枚舉值。

傳回

DENY

,日志将立即被抛棄不再經過其他過濾器;

NEUTRAL

,有序清單裡的下個過濾器過接着處理日志;

ACCEPT

,日志會被立即處理,不再經過剩餘過濾器。

<Appender>

添加一個或多個過濾器後,可以用任意條件對日志進行過濾。

<Appender>

有多個過濾器時,按照配置順序執行。

最常用的過濾器

LevelFilter

: 級别過濾器,根據日志級别進行過濾。如果日志級别等于配置級别,過濾器會根據

onMath

onMismatch

接收或拒絕日志。

有以下子節點:

<level>

:設定過濾級别

<onMatch>

:用于配置符合過濾條件的操作

<onMismatch>

:用于配置不符合過濾條件的操作

<file>

:指定正在記錄的日志檔案的路徑及檔案名。

注意在

windows

當中,反斜杠

\

需要轉義,或直接使用

/

也可以。例如

c:/temp/test.log

c:\\temp\\test.log

都可以。可以是相對目錄,也可以是絕對目錄,沒有預設值,如果上層目錄不存在,

FileAppender

會自動建立。

我測試時放在日志檔案是放在E盤下log檔案夾(目錄設定下文有提到)

<appender>

節點定義示例

<!-- 時間滾動輸出 level為 INFO 日志 -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在記錄的日志檔案的路徑及檔案名 -->
        <file>${log.path}/log_info.log</file>
        <!--日志檔案輸出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <!-- 日志記錄器的滾動政策,按日期,按大小記錄 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天日志歸檔路徑以及格式 -->
            <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志檔案保留天數-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- 此日志檔案隻記錄info級别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>info</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
           

2.子節點

<property>

:用來定義變量值,它有兩個屬性

name

value

,通過

<property>

定義的值會被插入到

logger

上下文中,可以使“${}”來使用變量。

<property name="log.path" value="E:/log" />
           

在上面定義檔案路徑中有使用到

${log.path}

3.子節點

<contextName>

:用來設定上下文名稱,每個

logger

都關聯到

logger

上下文,預設上下文名稱為

default

。但可以使用<contextName>設定成其他名字,用于區分不同應用程式的記錄。一旦設定,不能修改。

<contextName>myAppLogBack</contextName> 
           

4.子節點

<logger>

:用來設定某一個包或具體的某一個類的日志列印級别、以及指定

<appender>

<logger>

僅有一個

name

屬性,一個可選的

level

和一個可選的

addtivity

屬性。

name

:用來指定受此

logger

限制的某一個包或者具體的某一個類。

level

:用來設定列印級别,大小寫無關:

TRACE

DEBUG

INFO

WARN

ERROR

ALL

OFF

addtivity

: 是否向上級

logger

傳遞列印資訊。預設是

true

<logger name="org.springframework.web" level="info" addtivity="true"/>
           

5.子節點

<root>

:它也是

<logger>

元素,但是它是根

logger

,是所有

<logger>

的上級。

root

節點是必選節點,用來指定最基礎的日志輸出級别,隻有一個

level

屬性,因為

name

已經被命名為

root

,且已經是最上級了。

level

:用來設定列印級别,不能設定為

INHERITED

或者同義詞

NULL

。預設是

DEBUG

可以包含零個或多個元素,辨別這個

appender

将會添加到這個

logger

<root level="all">
    <appender-ref ref="CONSOLE" />
    <appender-ref ref="DEBUG_FILE" />
    <appender-ref ref="INFO_FILE" />
    <appender-ref ref="WARN_FILE" />
    <appender-ref ref="ERROR_FILE" />
</root>
           

6.子節點

<springProfile>

:

Spring profile

Spring 3

引入的概念,主要用在項目多環境運作的情況下,通過激活方式實作多環境切換,省去多環境切換時配置參數和檔案的修改,相比較

Maven profile

簡單實用,易于上手。并且

Spring profile

提供了多種激活方法,例如配置檔案,注解,

jvm

參數設定等等

據不同環境(

prod

:生産環境,

test

:測試環境,

dev

:開發環境)來定義不同的日志輸出,在

logback-spring.xml

中使用

springProfile

節點來定義,方法如下:

<!-- 測試環境+開發環境. 多個使用逗号隔開. -->
<springProfile name="test,dev">
    <logger name="com.example.springcloudeureka" level="info" />
</springProfile>
<!-- 生産環境. -->
<springProfile name="prod">
    <logger name="com.example.springcloudeureka" level="ERROR" />
</springProfile>
           

可以啟動服務的時候指定

profile

,springboot的話可以在配置檔案

yaml

中設定

spring.profiles.active

7.子節點

<conversionRule>

:logback 自定義Pattern模闆

例如需要在每條日志都輸出

logback output

字元串,可以這樣做

寫一個轉換器類,繼承

ClassicConvert

public class LogbackConvert extends ClassicConverter {
    @Override
    public String convert(ILoggingEvent iLoggingEvent) {
        return "logback output";
    }
}
           

實作裡面的方法,傳回的都是

String

類型

logback

配置檔案中中注冊該轉換器,并自定義轉換符

<conversionRule conversionWord="logback" 
converterClass="com.example.springcloudeureka.LogbackConvert"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender" > 
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %logback %n</pattern>
</appender>
           

<appender>

中自定義

Pattern

模闆,用

%轉換器名

即可

效果

都列印出

logback output

字元串

Apache Commons Logging

官方位址:

https://commons.apache.org/proper/commons-logging/

Jakarta Commons-logging(JCL)是

apache

最早提供的日志的門面接口。提供簡單的日志實作以及日志解耦功能。

commons-logging

Apache commons

類庫中的一員。

Apache commons

類庫是一個通用的類庫,提供了基礎的功能,比如說

commons-fileupload

commons-httpclient

commons-io

commons-codes

等。

commons-logging

能夠選擇使用

Log4j

還是

JDK Logging

,但是不依賴

Log4j

JDK Logging

API

。如果項目的

classpath

中包含了

log4j

的類庫,就會使用

log4j

,否則就使用

JDK Logging

。使用

commons-logging

能夠靈活的選擇使用那些日志方式,而且不需要修改源代碼。

不過現在

Apache Commons Logging

也不更新了,最新的依賴(Jul 05, 2014)

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>
           

具體實作

JCL有兩個基本的抽象類:Log( 基本記錄器 ) 和 LogFactory( 負責建立 Log 執行個體 )

public class SpringbootApplication {
    public static Log LOG= LogFactory.getLog(SpringbootApplication.class);
    public static void main(String[] args) {
        LOG.debug("debug()...");
        LOG.info("info()...");
        LOG.error("error()...");
    }
}
           

控制台顯示

SpringBoot已經預設內建了

log4j

關于commons-logging日志解耦可以參考文章:

https://blog.csdn.net/sakurainluojia/article/details/53534949

總結:

commons-logging

slf4j

都是日志的接口,供使用者使用,而沒有提供實作。

log4j

logback

等才是日志的真正實作,日志是接口+具體實作的方式來使用。

目前應用比較廣泛的是

Log4j2

logback

,而

logback

作為後起之秀,以替代

log4j

為目的,整體性能比

log4j

較佳,

log4j

log4j2

也是有諸多亮點

選擇使用

logback

Spring Boot

預設的日志系統,假如對日志沒有特殊要求,可以完全零配置(當然也可以自定義

logback-spring.xml

)使用

SLF4J(Simple Logging Facade For Java)

logback

來輸出日志。

個人推薦使用

log4j2

Log4j2

Log4j

的更新版,與之前的版本

Log4j 1.x

相比、有重大的改進,在修正了

Logback

固有的架構問題的同時,改進了許多

Logback

所具有的功能。關于

Log4j2

的新特性可以在其官網首頁檢視

關于

log4j2

的性能使用可以參考

https://www.jianshu.com/p/570b406bddcd https://blog.csdn.net/u011054333/article/details/54412360 https://blog.csdn.net/yjh1271845364/article/details/70888262