1 日誌門面和日誌實現
當我們的系統變的更加複雜的時候,我們的日誌就容易發生混亂;隨着系統開發的進行,可能會更新不同的日誌框架,造成當前系統中存在不同的日誌依賴,讓我們難以統一的管理和控制;就算我們強制要求所有的模塊使用相同的日誌框架,系統中也難以避免使用其他類似spring,mybatis等其他的第三方框架,它們依賴於我們規定不同的日誌框架,而且他們自身的日誌系統就有着不一致性,依然會出來日誌體系的混亂
所以我們需要借鑑JDBC的思想,爲日誌系統也提供一套門面,那麼我們就可以面向這些接口規範來開發,避免了直接依賴具體的日誌框架;這樣我們的系統在日誌中,就存在了日誌的門面和日誌的實現
日誌門面 (日誌的抽象層) | 日誌實現 |
---|---|
slf4j(Simple Logging Facade for Java) JCL(Jakarta Commons Logging) jboss-logging |
logback log4j2 JUL(java util logging) log4j |
日誌框架出現的歷史順序:log4j --> JUL --> JCL --> slf4j --> logback --> log4j2
slf4j、log4j、logback出至同一人之手(Ceki Gülcü);Ceki Gülcü將log4j捐獻給了Apache軟件基金會,使之成爲了Apache日誌服務的一個子項目,並且作者也在Apache工作了一段時間;由於log4j存在性能問題作者放棄維護log4j,開發了新的日誌框架 slf4j + logback;由於log4j相對於slf4j+logback沒有任何優勢Apache慌了(此時Ceki Gülcü自己創建了公司),趕緊對log4j進行升級也就是後面的log4j2
JCL全稱爲Jakarta Commons Logging,是Apache提供的一個通用日誌API;它是爲 "所有的Java日誌實現"提供一個統一的接口,它自身也提供一個日誌的實現,但是功能非常常弱 (SimpleLog);所以一般不會單獨使用它;他允許開發人員使用不同的具體日誌實現工具Log4j、Jdk自帶的日誌(JUL)
SLF4J簡單日誌門面(Simple Logging Facade For Java) SLF4J主要是爲了給Java日誌訪問提供一套標準、規範 的API框架,其主要意義在於提供接口,具體的實現可以交由其他日誌框架,例如log4j和logback等; 當然slf4j自己也提供了功能較爲簡單的實現,但是一般很少用到;對於一般的Java項目而言,日誌框架 會選擇slf4j-api作爲門面,配上具體的實現框架(log4j、logback等),中間使用橋接器完成橋接
JUL全稱Java util Logging是java原生的日誌框架,使用時不需要另外引用第三方類庫,相對其他日誌框 架使用方便,學習簡單,能夠在小型應用中靈活使用
log4j是Apache下的一款開源的日誌框架,通過在項目中使用 Log4J,我們可以控制日誌信息輸出到控制檯、文件、甚至是數據庫中;我們可以控制每一條日誌的輸出格式,通過定義日誌的輸出級別,可以 更靈活的控制日誌的輸出過程;方便項目的調試
log4j2是對log4j的升級版,參考了logback的一些優秀的設計,並且修復了一些問題,因此帶來了一些重大的提升
- 異常處理 : 在logback中,Appender中的異常不會被應用感知到,但是在log4j2中,提供了一些異常處理機制
- 性能提升 : log4j2相較於log4j和logback都具有很明顯的性能提升
- 自動重載配置 : 參考了logback的設計,當然會提供自動刷新參數配置,最實用的就是我們在生產上可以動態的修改日誌的級別而不需要重啓應用
- 無垃圾機制 : log4j2在大部分情況下,都可以使用其設計的一套無垃圾機制,避免頻繁的日誌收集導致的jvm gc
2 SLF4j和其他日誌框架的整合
2.1 整合原理
每一個日誌的實現框架都有自己的配置文件;使用slf4j以後配置文件還是做成日誌實現框架自己本身的配置文件
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
2.2 遺留問題
當我們項目有多個框架的時候,每個框架使用的日誌不一樣;比如 a(slf4j+logback)、 Spring(commons-logging)、Hibernate(jboss-logging)、MyBatis、xxxx;採用橋接舊的日誌框架即可實習統一使用slf4j進行日誌輸出
如何讓系統中所有的日誌都統一到slf4j
- 將系統中其他日誌框架先排除出去
- 用中間包來替換原有的日誌框架(中間包和原有的日誌框架一模一樣,只是具體調用的時候調用的是slf4j)
- 爲項目添加slf4j的具體實現
3 SpringBoot與日誌
3.1 SpringBoot默認日誌原理
- SpringBoot底層使用slf4j+logback方式進行日誌記錄
- SpringBoot也把其他的日誌都替換成了slf4j
//比如中間替換包 :jcl-over-slf4j
package org.apache.commons.logging;
public abstract class LogFactory {
//偷樑換柱
static LogFactory logFactory = new SLF4JLogFactory();
<!--點擊spring-boot-starter-logging即可看到如下內容-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
SpringBoot能自動適配所有的日誌,而且底層使用slf4j+logback的方式記錄日誌,引入其他框架的時候,只需要把這個框架依賴的日誌框架排除掉即可
3.2 SpringBoot日誌框架的使用
@SpringBootTest
@RunWith(SpringRunner.class)
public class LogApplicationTests {
//記錄器 org.slf4j.Logger org.slf4j.LoggerFactory
Logger logger = LoggerFactory.getLogger(getClass());
/**
* 日誌的級別 : trace < debug < info < warn < error
* 調整輸出的日誌級別 : 日誌就只會在這個級別以後的高級別生效
*/
@Test //org.junit.Test;
public void contextLoads() {
logger.trace("這是trace日誌..."); //跟蹤軌跡
logger.debug("這是debug日誌..."); //調試
//SpringBoot默認給我們使用的是info級別的,沒有指定級別的就用SpringBoot默認規定的級別(root級別)
logger.info("這是info日誌...");
logger.warn("這是warn日誌...");
logger.error("這是error日誌...");
}
springboot 設置指定包的日誌級別
logging.level.com.nobug=trace
logging:
level:
com.nobug: trace
springboot 設置輸出路徑
# 指定日誌文件存放的目錄(Windows爲當前項目所在目錄的根路徑),默認的文件名 spring.log
logging.file.path=/logs/springboot/
springboot 設置日誌輸出格式
# 在控制檯輸出的日誌的格式
logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
# 指定文件中日誌輸出的格式
logging.pattern.file=%d{yyyy-MM-dd} - [%thread] - %-5level - %logger{50} - %msg%n
3.3 使用自定義日誌配置文件
//org.springframework.boot.logging.LoggingSystemProperties
class LoggingSystemProperties {
//LoggingApplicationListener就是配置文件配置的(點擊配置文件即可進入)
static final String EXCEPTION_CONVERSION_WORD = LoggingApplicationListener.EXCEPTION_CONVERSION_WORD;
public void apply(LogFile logFile) {
RelaxedPropertyResolver propertyResolver = RelaxedPropertyResolver.ignoringUnresolvableNestedPlaceholders(this.environment, "logging.");
setSystemProperty(propertyResolver, EXCEPTION_CONVERSION_WORD,"exception-conversion-word");
setSystemProperty(PID_KEY, new ApplicationPid().toString());
setSystemProperty(propertyResolver, CONSOLE_LOG_PATTERN, "pattern.console");
setSystemProperty(propertyResolver, FILE_LOG_PATTERN, "pattern.file");
setSystemProperty(propertyResolver, LOG_LEVEL_PATTERN, "pattern.level");
if (logFile != null) {
logFile.applyToSystemProperties();
}
}
給類路徑下放上每個日誌框架自己的配置文件即可;SpringBoot就不使用它默認配置的了
Logging System | Customization |
---|---|
Logback |
|
Log4j2 |
|
JDK (Java Util Logging) |
|
使用logback.xml直接就被日誌框架識別了;使用logback-spring.xml日誌框架就不直接加載日誌的配置項,由SpringBoot解析日誌配置,可以使用SpringBoot的高級Profile功能
spring:
profiles:
active: dev
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<springProfile name="dev">
<pattern>開發環境輸出格式</pattern>
</springProfile>
<springProfile name="!dev">
<pattern>非開發環境輸出格式</pattern>
</springProfile>
</layout>
</appender>
如果使用logback.xml作爲日誌配置文件,還要使用profile功能會有以下錯誤
no applicable action for [springProfile]
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<target>System.err</target>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
</appender>
<logger name="com.nobug" level="trace" additivity="false">
<appender-ref ref="console"/>
</logger>
</configuration>
3.4 切換日誌框架
3.4.1 slf4j + log4j
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>logback-classic</artifactId>
<groupId>ch.qos.logback</groupId>
</exclusion>
<exclusion>
<artifactId>log4j-over-slf4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
</dependencies>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>logback-classic</artifactId>
<groupId>ch.qos.logback</groupId>
</exclusion>
<exclusion>
<artifactId>log4j-to-slf4j</artifactId>
<groupId>org.apache.logging.log4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
</dependencies>
log4j.properties
log4j.rootLogger = debug,stdout
### 輸出信息到控制擡 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
3.4.2 切換爲log4j2
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 排除logback日誌實現 -->
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 使用log4j2的日誌啓動器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
</dependencies>
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug" monitorInterval="5">
<Appenders>
<Console name="console" target="SYSTEM_ERR">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L %m%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="console"/>
</Root>
</Loggers>
</Configuration>