1. 程式人生 > 其它 >Spring5.2.x-02-日誌體系

Spring5.2.x-02-日誌體系

現象

  1. Spring體系中, 如果log是由spring-jcl搞出來的, 那麼log4j的配置會覆蓋logback;即使有logback與slf4j的繫結器, 也會優先使用log4j; 而其他, 如mybatis裡的日誌, 還是用自己的獨立的log元件來列印
  2. Spring-boot 預設使用logback, 日誌技術是統一的, 所有的是用一致的log元件

問題

  1. Spring的日誌體系為什麼與mybatis不互通
  2. Spring-boot為什麼可以讓日誌技術統一

java日誌分類

實現類框架

  1. JUL: java自帶log框架
  2. Log4j/Log4j2/LogBack: 第三方日誌記錄框架

門面框架

  1. JCL: 面向Log介面程式設計, 缺點是不更新、無法解決歷史硬編碼的log、log元件的選擇範圍是硬編碼於程式碼中
  2. Slf4j: 通過 Adapter 和 Bridge, 解決了歷史硬編碼的log不統一; 通過繫結器, 解決log元件的範圍自行配置

Spring-JCL

spring4.x用的是apache的jcl, 5.x之後用的是自己module的spring-jcl, 因為原來的jcl沒有繼續維護, spring-jcl自行維護了

spring-jcl只要看到log4j2, 就會啟用, 詳見以下程式碼片段

	private static final String LOG4J_SPI = "org.apache.logging.log4j.spi.ExtendedLogger";

	private static final String LOG4J_SLF4J_PROVIDER = "org.apache.logging.slf4j.SLF4JProvider";

	private static final String SLF4J_SPI = "org.slf4j.spi.LocationAwareLogger";

	private static final String SLF4J_API = "org.slf4j.Logger";
		// 只要有 log4j
		if (isPresent(LOG4J_SPI)) {
			// 有橋接器 && 有slf4j, 才啟用 slf4j
			if (isPresent(LOG4J_SLF4J_PROVIDER) && isPresent(SLF4J_SPI)) {
				// log4j-to-slf4j bridge -> we'll rather go with the SLF4J SPI;
				// however, we still prefer Log4j over the plain SLF4J API since
				// the latter does not have location awareness support.
				logApi = LogApi.SLF4J_LAL;
			}
			else {
				// Use Log4j 2.x directly, including location awareness support
				logApi = LogApi.LOG4J;
			}
		}
		else if (isPresent(SLF4J_SPI)) {
			// Full SLF4J SPI including location awareness support
			logApi = LogApi.SLF4J_LAL;
		}
		else if (isPresent(SLF4J_API)) {
			// Minimal SLF4J API without location awareness support
			logApi = LogApi.SLF4J;
		}
		else {
			// java.util.logging as default
			logApi = LogApi.JUL;
		}

基本原理, 就是看能不能用反射找到對應的類, 找不到說明沒有執行環境沒有這個類, catch住為false即可

導致spring內部的log和外部其他的log不一定統一, 可能會出現Spring裡面是log4j2, Tomcat又是 jul, 不能統一配置

因此要加一個log4j-to-slf4j的橋接器(Spring-boot-web預設配置了橋接器, 日誌能統一為logback)

從日誌框架轉向SLF4J 橋接器

橋接器的包結構和需要橋接的日誌框架是一樣的, 類也是一樣的, 這樣編譯就不會報錯

應該用了代理, 不知道細節是怎麼實現的...

  1. jul-to-slf4j:jdk-logging到slf4j的橋樑
  2. log4j-over-slf4j:log4j1到slf4j的橋樑
  3. jcl-over-slf4j:commons-logging到slf4j的橋樑

從SLF4J轉向具體的日誌框架 繫結器

  1. slf4j-jdk14:slf4j到jdk-logging的橋樑
  2. slf4j-log4j12:slf4j到log4j1的橋樑
  3. log4j-slf4j-impl:slf4j到log4j2的橋樑
  4. logback-classic:slf4j到logback的橋樑
  5. slf4j-jcl:slf4j到commons-logging的橋樑