帶你理清 Java 混亂的日誌體系 - log4j、logback、log4j2、jcl、SLFJ 究竟是啥關係?

1.JAVA混亂的日誌體系

換亂的java日誌體系

case:

SLF4J-JCL

LOG4J-CORE

LOGBACK

SLF4J-SIMPLE

JCL-OVER-SLF4J

LOGBACK-CORE

LOG4J

LOG4J-API

LOG4J-JUL

LOG4J-JCL

LOGBACK-ACCESS

LOGBACK-CLASSIC

SLF4-API

SLF4J-LOGJ12

LOGBACK-CLASSIC

LOG4J-SLF4J-IMPL

1.1 JAVA日誌體系概述

問題:

常用的日誌框架有哪些?

大家目前正在使用的

名稱 jar 描述
log4j log4j-1.2.17 早期常用的日誌組件
logback logback-core,logback-classic,logback-access 性能由於log4j
log4j2 log4j,log4j-api,log4j-core log4j升級
Java.util.logging jdk jdk實現,tomcat默認實現

開發一個類似spring框架,或者開發一個組件,如何選擇

選擇任何一種實現,都不太好,不同的日誌輸出不一樣,日誌也會打印多份。如何解決,日誌做抽象層

1.1.1 Apache Commons Logging(JCL)

1-官網介紹
記錄組件
在編寫一個庫時,記錄信息是非常有用的。然而,外面有很多日誌實現,一個庫不能把某一個特定的日誌實現強加給作爲庫一部分的整體應用。

Logging包是不同日誌實現之間的一個超薄橋樑。一個使用commons-logging API的庫可以在運行時與任何日誌實現一起使用。Common-logging自帶對許多流行的日誌實現的支持,爲其他的日誌實現編寫適配器是一項相當簡單的任務。

應用程序(而不是庫)也可以選擇使用commonons-logging。雖然日誌記錄實現的獨立性對應用程序來說不像庫那樣重要,但使用commons-logging確實允許應用程序在不重新編譯代碼的情況下改變到不同的日誌實現。

請注意,commons-logging不會嘗試初始化或終止運行時使用的底層日誌實現,這是應用程序的責任。然而,許多常用的日誌實現都會自動初始化;在這種情況下,應用程序可以避免包含任何特定於所使用的日誌實現的代碼。
2-實現原理
org.apache.commons.logging.impl.LogFactoryImpl#discoverLogImplementation
3-缺點,只能實現一種,通過靜態綁定實現,不易擴展,適配器模式。

1.1.2 SLFJ

slf4j全 稱爲Simple Logging Facade for JAVA,java簡單日誌門面。類似於Apache Common-Logging,是對不同日誌框架提供的一個門面封裝(是接口而非實現),可以在部署的時候不修改任何配置即可接入一種日誌實現方案。但是,他在編譯時靜態綁定真正的Log庫。使用SLF4J時,如果需要使用某一種日誌實現,那麼你必須選擇正確的SLF4J的jar包的集合(各種橋接包)。
----基於OSGI模塊化框架詳解
特點:
動態加載、更新、和卸載模塊而不用停止服務
實現系統的模塊化、版本化,允許多版本bundule(模塊)同時服務--註釋,很多三方jar中的MANIFEST.MF進行子描述,就是一個例子
	Manifest-Version: 1.0
    Archiver-Version: Plexus Archiver
    Created-By: Apache Maven
    Built-By: ceki
    Build-Jdk: 1.8.0_121
    Bundle-Description: The slf4j API
    Bundle-Version: 1.7.30
    Implementation-Version: 1.7.30
    X-Compile-Source-JDK: 1.5
    X-Compile-Target-JDK: 1.5
    Implementation-Title: slf4j-api
    Bundle-ManifestVersion: 2
    Bundle-SymbolicName: slf4j.api
    Bundle-Name: slf4j-api
    Bundle-Vendor: SLF4J.ORG
    Bundle-RequiredExecutionEnvironment: J2SE-1.5
    Automatic-Module-Name: org.slf4j
    Export-Package: org.slf4j;version=1.7.30, org.slf4j.spi;version=1.7.30
     , org.slf4j.helpers;version=1.7.30, org.slf4j.event;version=1.7.30
    Import-Package: org.slf4j.impl;version=1.6.0
    Service model允許模塊、插件相互依賴但松耦合,分享服務更簡單

1.2 常用的日誌組成方案與應用場景

目前最優的方案:slf4j的異步模式+log4j2,性能好
例如,springboot+mybatis+slf4j+log4j2

1.2.1 jcl

1.2.2 slfj

在這裏插入圖片描述
jcl本來就是抽象的,slf4j能橋接jcl,是爲了能夠適配。

橋接包
slfj-log4j12.jar(橋接log4j)
slf4j-jdk14.jar(橋接jdk Logging)
slf4j-jcl.jar(橋接jcl)
log4j-slf4j-impl.jar(橋接log4j2)
logback-classic.jar(橋接logback)

參考項目:log-slfj

1.2.3 日誌切換,適配器

在這裏插入圖片描述

如果當前系統之中再用jcl打印日誌,比如spring4,但這是想加入slfj來打印日誌,就會出現兩類日誌輸出,如何既覺

只要classpath當中指定了slfj適配器,包,即可無縫江源日誌輸出轉移到slfj上來
jcl-over-slfj:轉移 jcl日誌至slf4j

舉例子:
組件1:mybatis---jcl+log4j
組件2:springboot----sjlf+logback
組件2:shiro----jcl+logback
默認:jcl+jul
期望結果:slfj+logback

1:slfj+橋接器+實現

log4j-over-slfj:轉移log4j日誌至slf4j

jul-over-slfj:轉移jul日誌至slf4j
名稱 描述 相關JAR包
門面 slf4jAPI接口 slf4j-api.jar
橋接 用於slf4j連接對應日誌實現 slfj-log4j12.jar,slfj-jdk14.jar,log4j-slf4j-impl,logback-classic,slf4j-jcl.jar
適配器 用於將原日誌輸出無縫轉移到slf4j cl-over-slf4j.jar,log4j-over-slfj,jul-over-slfj,
具體實現 日誌的具體實現 log4j.jar,logback,log4j2,java.util.logging

1.2.4 循環依賴

如果clappsth中既有橋接器也有適配器,日誌會被踢來踢去,陷入死循環

1.3.slfj+log4j2 統一系統應用日誌

由於系統組件中,可能採用了不同的日誌體系,spring5之前,spring採用的是apache-common-log,spring5之後,採用spring-jcl

1.4 日誌規範

1. 【強制】應用中不可直接使用日誌系統(Log4j、Logback)中的 API,而應依賴使用日誌框架
(SLF4J、JCL--Jakarta Commons Logging)中的 API,使用門面模式的日誌框架,有利於維護和
各個類的日誌處理方式統一。
說明:日誌框架(SLF4J、JCL--Jakarta Commons Logging)的使用方式(推薦使用 SLF4J)
 使用 SLF4J:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Test.class);
使用 JCL:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
private static final Log log = LogFactory.getLog(Test.class);
2. 【強制】所有日誌文件至少保存 15 天,因爲有些異常具備以“周”爲頻次發生的特點。對於
當天日誌,以“應用名.log”來保存,保存在/home/admin/應用名/logs/</font>目錄下,
過往日誌格式爲: {logname}.log.{保存日期},日期格式:yyyy-MM-dd
說明:以 mppserver 應用爲例,日誌保存在/home/admin/mppserver/logs/mppserver.log,歷史日誌
名稱爲 mppserver.log.2016-08-01
3. 【強制】應用中的擴展日誌(如打點、臨時監控、訪問日誌等)命名方式:
appName_logType_logName.log。logType:日誌類型,如 stats/monitor/access 等;logName:日誌描
述。這種命名的好處:通過文件名就可知道日誌文件屬於什麼應用,什麼類型,什麼目的,也有利於歸類查
找。
說明:推薦對日誌進行分類,如將錯誤日誌和業務日誌分開存放,便於開發人員查看,也便於通過日誌對系
統進行及時監控。
正例:mppserver 應用中單獨監控時區轉換異常,如:mppserver_monitor_timeZoneConvert.log
4. 【強制】在日誌輸出時,字符串變量之間的拼接使用佔位符的方式。
說明:因爲 String 字符串的拼接會使用 StringBuilder 的 append()方式,有一定的性能損耗。使用佔位符僅
是替換動作,可以有效提升性能。
正例:logger.debug("Processing trade with id: {} and symbol: {}", id, symbol);
5. 【強制】對於 trace/debug/info 級別的日誌輸出,必須進行日誌級別的開關判斷。
說明:雖然在 debug(參數)的方法體內第一行代碼 isDisabled(Level.DEBUG_INT)爲真時(Slf4j 的常見實現
Log4j 和 Logback),就直接 return,但是參數可能會進行字符串拼接運算。此外,如果 debug(getName())
這種參數內有 getName()方法調用,無謂浪費方法調用的開銷。
正例:
// 如果判斷爲真,那麼可以輸出 trace 和 debug 級別的日誌
if (logger.isDebugEnabled()) {
 logger.debug("Current ID is: {} and name is: {}", id, getName());
}
6. 【強制】避免重複打印日誌,浪費磁盤空間,務必在 log4j.xml 中設置 additivity=false。
正例:<logger name="com.taobao.dubbo.config" additivity="false">
	case:
	 <logger name="com.jd" level="DEBUG">
     	<AppenderRef ref="console"/>
     </logger>
	
7. 【強制】生產環境禁止直接使用 System.out 或 System.err 輸出日誌或使用
e.printStackTrace()打印異常堆棧。
說明:標準日誌輸出與標準錯誤輸出文件每次 Jboss 重啓時才滾動,如果大量輸出送往這兩個文件,容易
造成文件大小超過操作系統大小限制。
8. 【強制】異常信息應該包括兩類信息:案發現場信息和異常堆棧信息。如果不處理,那麼通過
關鍵字 throws 往上拋出。
正例:logger.error(各類參數或者對象 toString() + "_" + e.getMessage(), e);
case:不允許記錄日誌後又拋出異常,因爲這樣會多次記錄日誌,只允許記錄一次日誌
9. 【強制】日誌打印時禁止直接用 JSON 工具將對象轉換成 String。
說明:如果對象裏某些 get 方法被重寫,存在拋出異常的情況,則可能會因爲打印日誌而影響正常業務流
程的執行。
正例:打印日誌時僅打印出業務相關屬性值或者調用其對象的 toString()方法。
10.【推薦】謹慎地記錄日誌。生產環境禁止輸出 debug 日誌;有選擇地輸出 info 日誌;如果使用
warn 來記錄剛上線時的業務行爲信息,一定要注意日誌輸出量的問題,避免把服務器磁盤撐
爆,並記得及時刪除這些觀察日誌。
說明:大量地輸出無效日誌,不利於系統性能提升,也不利於快速定位錯誤點。記錄日誌時請思考:這些
日誌真的有人看嗎?看到這條日誌你能做什麼?能不能給問題排查帶來好處?
11.【推薦】可以使用 warn 日誌級別來記錄用戶輸入參數錯誤的情況,避免用戶投訴時,無所適
從。如非必要,請不要在此場景打出 error 級別,避免頻繁報警。
說明:注意日誌輸出的級別,error 級別只記錄系統邏輯出錯、異常或者重要的錯誤信息。
12.【推薦】儘量用英文來描述日誌錯誤信息,如果日誌中的錯誤信息用英文描述不清楚的話使用
中文描述即可,否則容易產生歧義。
說明:國際化團隊或海外部署的服務器由於字符集問題,使用全英文來註釋和描述日誌錯誤信息。
if (logger.isDebugEnabled()) {
	logger.debug();
}

1.5 性能測試

1.5.1 單線程

名稱 未開啓緩存,立即刷出 開啓緩存,不立即刷出 異步-appender-未開啓緩存,立即輸出 異步appender-開啓緩存,不立即輸出
log4j 87.342秒 10.757秒 91.752 10.058秒
logback 81.617 5.547 100.245 10.69
log4j2 5.272/5.614/5.196 5.502/5.53/5.453 5.423/5.378/4.953 5.063/4.74/5.246(AsyncRoot)==>5.348/5.712/4.818(Async)

1.5.2 多線程

名稱 未開啓緩存,立即刷出 開啓緩存,不立即刷出 異步appender-未開啓緩存,立即輸出 異步appender-開啓緩存,不立即輸出
log4j 102.823 13.324 87.966 10.651
logback 100.853 8.238 111.272 55.813
log4j2 8.164/6.94/7.073 8.02/6.597/8.009 8.11/7.485 8.178/8.226

架構圖對比:
在這裏插入圖片描述
log4j2-AsyncLogger

Log4j2中的AsyncLogger的內部使用了Disruptor框架。
參考:https://www.cnblogs.com/yeyang/p/7944906.html

Disruptor簡介
Disruptor是英國外匯交易公司LMAX開發的一個高性能隊列,基於Disruptor開發的系統單線程能支撐每秒600萬訂單。

目前,包括Apache Strom、Log4j2在內的很多知名項目都應用了Disruptor來獲取高性能。
Disruptor框架內部核心數據結構爲RingBuffer,其爲無鎖環形隊列。

單線程每秒能夠處理600萬訂單,Disruptor爲什麼這麼快?

a.lock-free-使用了CAS來實現線程安全
ArrayBlockingQueue使用鎖實現併發控制,當get或put時,當前訪問線程將上鎖,當多生產者、多消費者的大量併發情形下,由於鎖競爭、線程切換等,會有性能損失。
Disruptor通過CAS實現多生產者、多消費者對RingBuffer的併發訪問。CAS相當於樂觀鎖,其性能優於Lock的性能。

b.使用緩存行填充解決僞共享問題
計算機體系結構中,內存的訪問速度遠遠低於CPU的運行速度,在內存和CPU之間,加入Cache,CPU首先訪問Cache中的數據,CaChe未命中,才訪問內存中的數據。
僞共享:Cache是以緩存行(cache line)爲單位存儲的,當多個線程修改互相獨立的變量時,如果這些變量共享同一個緩存行,就會無意中影響彼此的性能。
關於僞共享的深度分析,可參考《僞共享,併發編程的性能殺手》這篇文章。

日誌輸出方式
sync	        同步打印日誌,日誌輸出與業務邏輯在同一線程內,當日志輸出完畢,才能進行後續業務邏輯操作
Async Appender	異步打印日誌,內部採用ArrayBlockingQueue,對每個AsyncAppender創建一個線程用於處理日誌輸出。
Async Logger	異步打印日誌,採用了高性能併發框架Disruptor,創建一個線程用於處理日誌輸出。

1.6 日誌的全鏈路追蹤 簡介

1.6.1 全鏈路追蹤的解決方案

全鏈路追蹤的背景
在這裏插入圖片描述

上圖是一個典型的微服務調用鏈路,面對的場景問題如下:

1-如果D服務是一個關鍵服務,返回結果錯誤,無論是日誌,還是監控平臺,並不能很快捷的定位問題出現在了那裏,因爲不能串聯整個調用鏈路的流程

2-當對某一個服務架構升級或者改造的時候,不好評估影響範圍,不明確服務之間的依賴關係,給技術決策帶來了困難

3-性能瓶頸,整個調用鏈路那個環節耗時比較久

4-當一次請求結束後,不好確定執行順序,都給業務邏輯上的理解帶來了困難

需要解決問題:

1-串用調用鏈,快速定位問題

2-釐清服務依賴關係

3-進行各個服務接口的性能分析

4-跟蹤業務流的處理順序

已有方案

1-Google Dapper

2-Twitter Zipkin

3-Spring Cloud Sleuth

​ 3-1 與springboot及spring組件無縫集成

​ 3-2支持Zipkin輸出(mysql,es)

​ 3-3 支持MQ和HTTP方式傳輸

1.6.2 Spring Cloud Sleuth 介紹

基本概念:

​ Trace(鏈路)

​ Span(跨度)

​ Annotation(標註):CS(發送請求),SR(接受請求),SS(相應發送),CR(相應被客戶端接收)

架構圖

在這裏插入圖片描述

流程圖

在這裏插入圖片描述

基本概念

在這裏插入圖片描述

1.6.3 分佈式日誌檢索解決方案-ELK

E:Elasticsearch —存儲

L:LogStash —收集

K:Kibana —展示

流程
在這裏插入圖片描述


補充

JCL
在這裏插入圖片描述
slf4j
+jcl 或其它
在這裏插入圖片描述

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章