亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

目錄
1 日誌常見錯因
1.1 日誌框架繁多
#1.2 配置複雜且容易出錯
1.3 日誌記錄本身就有些誤解
2 SLF4J
3 日誌重複記錄
logger配置繼承關(guān)係導(dǎo)致日誌重複記錄
錯誤配置LevelFilter造成日誌重複
ThresholdFilter原始碼解析
LevelFilter
修正
4 非同步日誌提高效能?
原始碼解析
AsyncAppender
AsyncAppender異步日志坑
案例
源碼解析
queueSize 過大
queueSize 較小
neverBlock 默認false
使用日誌佔位符就無需判斷日誌等級?
首頁 Java Java基礎(chǔ) 搞懂Java日誌級別,重複記錄、遺失日誌問題

搞懂Java日誌級別,重複記錄、遺失日誌問題

Dec 11, 2020 pm 05:26 PM
java 日誌等級 重複記錄

java基礎(chǔ)教學(xué)專欄介紹如何解決Java日誌等級等問題

搞懂Java日誌級別,重複記錄、遺失日誌問題

#相關(guān)免費學(xué)習(xí)推薦:java基礎(chǔ)教學(xué)

1 日誌常見錯因

1.1 日誌框架繁多

不同類別庫可能使用不同日誌框架,相容是個難題

#1.2 配置複雜且容易出錯

日誌配置文件通常很繁雜,很多同學(xué)習(xí)慣從其他項目或網(wǎng)上博客直接複製份配置文件,但卻不仔細研究如何修改。常見錯誤發(fā)生於重複記錄日誌、同步日誌的效能、非同步記錄的錯誤配置。

1.3 日誌記錄本身就有些誤解

例如沒考慮到日誌內(nèi)容取得的代價、胡亂使用日誌等級等。

2 SLF4J

Logback、Log4j、Log4j2、commons-logging、JDK自帶的java.util.logging等,都是Java系統(tǒng)的日誌框架,確實非常多。而不同的類別庫,也可能選擇使用不同的日誌框架。這樣一來,日誌的統(tǒng)一管理就變得非常困難。

  • SLF4J(Simple Logging Facade For Java)就為解決該問題

  • 提供統(tǒng)一的日誌門面API,即圖中紫色部分,實現(xiàn)中立的日誌記錄API
  • 橋接功能,藍色部分,把各種日誌框架API(綠色部分)橋接到SLF4J API。這樣即便你的程式中使用各種日誌API記錄日誌,最終都可以橋接到SLF4J門面API。
  • 適配功能,紅色部分,可實現(xiàn)SLF4J API和實際日誌框架(灰色部分)綁定。
    SLF4J只是日誌標準,還是需要實際日誌框架。日誌框架本身未實作SLF4J API,所以需前置轉(zhuǎn)換。 Logback就是以SLF4J API標準實現(xiàn),所以才無需綁定模組做轉(zhuǎn)換。

雖然可用log4j-over-slf4j實作Log4j橋接到SLF4J,也可使用slf4j-log4j12實作SLF4J適配到Log4j,也把它們畫到了一列,但是它不能同時使用它們,否則就會產(chǎn)生死循環(huán)。 jcl和jul同理。

雖然圖中有4個灰色的日誌實作框架,但日常業(yè)務(wù)使用最多的還是Logback和Log4j,都是同一人開發(fā)的。 Logback可認為是Log4j改進版,更建議使用,基本上已是主流。

Spring Boot的日誌框架也是Logback。那為什麼我們沒有手動引進Logback包,就可以直接使用Logback?

spring-boot-starter模組依賴spring-boot-starter-logging模組
spring-boot-starter-logging模組自動引入logback -classic(包含SLF4J和Logback日誌框架)和SLF4J的一些適配器。其中,log4j-to-slf4j用來實作Log4j2 API到SLF4J的橋接,jul-to-slf4j則是實作java.util.logging API到SLF4J的橋接。

3 日誌重複記錄

日誌重複記錄不僅給查看日誌和統(tǒng)計工作帶來不必要的麻煩,還會增加磁碟和日誌收集系統(tǒng)的負擔(dān)。

logger配置繼承關(guān)係導(dǎo)致日誌重複記錄

  • 定義一個方法實作debug、info、warn和error四種日誌的記錄

  • Logback設(shè)定

  • 設(shè)定看沒啥問題,執(zhí)行方法後出現(xiàn)日誌重複記錄

  • 分析
    CONSOLE這個Appender同時掛載到了兩個Logger,定義的<logger><root>,由於定義的<logger>繼承自<root>,所以同一日誌既會透過logger記錄,也會傳送到root記錄,因此應(yīng)用package下日誌出現(xiàn)重複記錄。

如此配置的初衷是啥呢?
內(nèi)心是想實現(xiàn)自訂logger配置,讓應(yīng)用程式內(nèi)的日誌暫時開啟DEBUG等級日誌記錄。其實,這無需重複掛載Appender,去掉<logger>下掛載的Appender即可:

<logger name="org.javaedge.time.commonmistakes.logging" level="DEBUG"/>

若自訂<logger>請把日誌輸出到不同Appender:
例如

  • 應(yīng)用程式日誌輸出到檔案app.log
  • 其他框架日誌輸出到控制臺

可設(shè)定<logger>additivity屬性為false,這就不會繼承<root>的Appender

錯誤配置LevelFilter造成日誌重複

  • 在記錄日誌到控制臺的同時,把日誌記錄依照不同等級記錄到兩個檔案

  • 執(zhí)行結(jié)果

  • info.log 檔案包含INFO、WARN和ERROR三級日誌,不符合預(yù)期

  • error.log包含WARN和ERROR兩個等級日誌,導(dǎo)致日誌重複收集

  • #事故問責(zé)
    有些公司使用自動化ELK方案收集日誌,日誌會同時輸出到控制臺和文件,開發(fā)人員在本地測試不會關(guān)心文件中記錄的日誌,而在測試和生產(chǎn)環(huán)境又因為開發(fā)人員沒有伺服器存取權(quán)限,所以原始日誌文件中的重複問題難以發(fā)現(xiàn)。

日誌到底為何重複呢?

ThresholdFilter原始碼解析

  • 當(dāng)日誌等級≥ 設(shè)定等級 傳回NEUTRAL,繼續(xù)呼叫過濾器鏈上的下個篩選器
  • 否則回傳DENY,直接拒絕記錄日誌

#該案例我們將ThresholdFilter# WARN,因此可記錄WARNERROR等級日誌。

LevelFilter

用於比較日誌級別,然後進行相應(yīng)處理。

  • 若符合就呼叫onMatch定義的處理方式:預(yù)設(shè)交給下一個篩選器處理(AbstractMatcherFilter基底類別中定義的預(yù)設(shè)值)
  • #否則呼叫onMismatch定義的處理方式:預(yù)設(shè)也是交給下一個過濾器


ThresholdFilter 不同,LevelFilter僅配置level無法真正運作。

由於未配置onMatch和onMismatch屬性,所以該過濾器失效,導(dǎo)致INFO以上等級日誌都記錄了。

修正

配置LevelFilter的onMatch屬性為ACCEPT,表示接收INFO等級的日誌;配置onMismatch屬性為DENY,表示除了INFO等級都不記錄:

如此,_info.log檔案只會有INFO等級日誌,不會再出現(xiàn)日誌重複。

4 非同步日誌提高效能?

知道到底如何正確將日誌輸出到檔案後,就該考慮如何避免日誌記錄成為系統(tǒng)效能瓶頸。這可解決,磁碟(如機械磁碟)IO效能較差、日誌量又很大的情況下,如何記錄日誌問題。

定義如下的日誌配置,一共有兩個Appender:

FILE是一個FileAppender,用來記錄所有的日誌;
CONSOLE是一個ConsoleAppender,用來記錄帶有time標記的日誌。

把大量日誌輸出到檔案中,日誌檔案會非常大,如果效能測試結(jié)果也混在其中的話,就很難找到那個日誌。所以,這裡使用EvaluatorFilter對日誌按照標記進行過濾,並將過濾出的日誌單獨輸出到控制臺上。在該案例中給輸出測試結(jié)果的那段日誌上做了time標記。

搭配使用標記和EvaluatorFilter,實作日誌的按標籤過濾

  • 測試程式碼:實現(xiàn)記錄指定次數(shù)的大日誌,每個日誌包含1MB位元組的模擬數(shù)據(jù),最後記錄一條以time為標記的方法執(zhí)行耗時日誌:

執(zhí)行程式後可以看到,記錄1000次日誌和10000次日誌的呼叫耗時,分別是5.1秒和39秒

對只記錄檔案日誌的程式碼,這耗時過長。

原始碼解析

FileAppender繼承自O(shè)utputStreamAppender

在追加日誌時,是直接把日誌寫入OutputStream中,屬同步記錄日誌

所以日志大量寫入才會曠日持久。如何才能實現(xiàn)大量日志寫入時,不會過多影響業(yè)務(wù)邏輯執(zhí)行耗時而影響吞吐量呢?

AsyncAppender

使用Logback的AsyncAppender

即可實現(xiàn)異步日志記錄。AsyncAppender類似裝飾模式,在不改變類原有基本功能情況下為其增添新功能。這便可把AsyncAppender附加在其他Appender,將其變?yōu)楫惒健?/p>

定義一個異步Appender ASYNCFILE,包裝之前的同步文件日志記錄的FileAppender, 即可實現(xiàn)異步記錄日志到文件

  • 記錄1000次日志和10000次日志的調(diào)用耗時,分別是537毫秒和1019毫秒

異步日志真的如此高性能?并不,因為這并沒有記錄下所有日志。

AsyncAppender異步日志坑

  • 記錄異步日志撐爆內(nèi)存
  • 記錄異步日志出現(xiàn)日志丟失
  • 記錄異步日志出現(xiàn)阻塞。

案例

模擬慢日志記錄場景:
首先,自定義一個繼承自ConsoleAppenderMySlowAppender,作為記錄到控制臺的輸出器,寫入日志時休眠1秒。

  • 配置文件中使用AsyncAppender,將MySlowAppender包裝為異步日志記錄

  • 測試代碼

  • 耗時很短但出現(xiàn)日志丟失:要記錄1000條日志,最終控制臺只能搜索到215條日志,而且日志行號變問號。

  • 原因分析
    AsyncAppender提供了一些配置參數(shù),而當(dāng)前沒用對。

源碼解析

  • includeCallerData
    默認false:方法行號、方法名等信息不顯示
  • queueSize
    控制阻塞隊列大小,使用的ArrayBlockingQueue阻塞隊列,默認容量256:內(nèi)存中最多保存256條日志
  • discardingThreshold
    丟棄日志的閾值,為防止隊列滿后發(fā)生阻塞。默認隊列剩余容量 < 隊列長度的20%,就會丟棄TRACE、DEBUG和INFO級日志
  • neverBlock
    控制隊列滿時,加入的數(shù)據(jù)是否直接丟棄,不會阻塞等待,默認是false
    • 隊列滿時:offer不阻塞,而put會阻塞
    • neverBlock為true時,使用offer
public?class?AsyncAppender?extends?AsyncAppenderBase<ILoggingEvent>?{
	//?是否收集調(diào)用方數(shù)據(jù)
????boolean?includeCallerData?=?false;
????protected?boolean?isDiscardable(ILoggingEvent?event)?{
????????Level?level?=?event.getLevel();
????????//?丟棄?≤?INFO級日志
????????return?level.toInt()?<= Level.INFO_INT;
    }
    protected void preprocess(ILoggingEvent eventObject) {
        eventObject.prepareForDeferredProcessing();
        if (includeCallerData)
            eventObject.getCallerData();
    }}public class AsyncAppenderBase<E>?extends?UnsynchronizedAppenderBase<E>?implements?AppenderAttachable<E>?{

	//?阻塞隊列:實現(xiàn)異步日志的核心
????BlockingQueue<E>?blockingQueue;
????//?默認隊列大小
????public?static?final?int?DEFAULT_QUEUE_SIZE?=?256;
????int?queueSize?=?DEFAULT_QUEUE_SIZE;
????static?final?int?UNDEFINED?=?-1;
????int?discardingThreshold?=?UNDEFINED;
????//?當(dāng)隊列滿時:加入數(shù)據(jù)時是否直接丟棄,不會阻塞等待
????boolean?neverBlock?=?false;

????@Override
????public?void?start()?{
???????	...
????????blockingQueue?=?new?ArrayBlockingQueue<E>(queueSize);
????????if?(discardingThreshold?==?UNDEFINED)
????????//默認丟棄閾值是隊列剩余量低于隊列長度的20%,參見isQueueBelowDiscardingThreshold方法
????????????discardingThreshold?=?queueSize?/?5;
????????...
????}

????@Override
????protected?void?append(E?eventObject)?{
????????if?(isQueueBelowDiscardingThreshold()?&&?isDiscardable(eventObject))?{?//判斷是否可以丟數(shù)據(jù)
????????????return;
????????}
????????preprocess(eventObject);
????????put(eventObject);
????}

????private?boolean?isQueueBelowDiscardingThreshold()?{
????????return?(blockingQueue.remainingCapacity()?< discardingThreshold);
    }

    private void put(E eventObject) {
        if (neverBlock) { //根據(jù)neverBlock決定使用不阻塞的offer還是阻塞的put方法
            blockingQueue.offer(eventObject);
        } else {
            putUninterruptibly(eventObject);
        }
    }
    //以阻塞方式添加數(shù)據(jù)到隊列
    private void putUninterruptibly(E eventObject) {
        boolean interrupted = false;
        try {
            while (true) {
                try {
                    blockingQueue.put(eventObject);
                    break;
                } catch (InterruptedException e) {
                    interrupted = true;
                }
            }
        } finally {
            if (interrupted) {
                Thread.currentThread().interrupt();
            }
        }
    }}

默認隊列大小256,達到80%后開始丟棄<=INFO級日志后,即可理解日志中為什么只有兩百多條INFO日志了。

queueSize 過大

可能導(dǎo)致OOM

queueSize 較小

默認值256就已經(jīng)算很小了,且discardingThreshold設(shè)置為大于0(或為默認值),隊列剩余容量少于discardingThreshold的配置就會丟棄<=INFO日志。這里的坑點有兩個:

  1. 因為discardingThreshold,所以設(shè)置queueSize時容易踩坑。
    比如本案例最大日志并發(fā)1000,即便置queueSize為1000,同樣會導(dǎo)致日志丟失
  2. discardingThreshold參數(shù)容易有歧義,它不是百分比,而是日志條數(shù)。對于總?cè)萘?0000隊列,若希望隊列剩余容量少于1000時丟棄,需配置為1000

neverBlock 默認false

意味總可能會出現(xiàn)阻塞。

  • discardingThreshold = 0,那么隊列滿時再有日志寫入就會阻塞
  • discardingThreshold != 0,也只丟棄≤INFO級日志,出現(xiàn)大量錯誤日志時,還是會阻塞

queueSize、discardingThreshold和neverBlock三參密不可分,務(wù)必按業(yè)務(wù)需求設(shè)置:

  • 若優(yōu)先絕對性能,設(shè)置neverBlock = true,永不阻塞
  • 若優(yōu)先絕不丟數(shù)據(jù),設(shè)置discardingThreshold = 0,即使≤INFO級日志也不會丟。但最好把queueSize設(shè)置大一點,畢竟默認的queueSize顯然太小,太容易阻塞。
  • 若兼顧,可丟棄不重要日志,把queueSize設(shè)置大點,再設(shè)置合理的discardingThreshold

以上日志配置最常見兩個誤區(qū)

再看日誌記錄本身的誤解。

使用日誌佔位符就無需判斷日誌等級?

SLF4J的{}佔位符語法,到真正記錄日誌時才會取得實際參數(shù),因此解決了日誌數(shù)據(jù)獲取的效能問題。
這說法對嗎?

  • 驗證程式碼:傳回結(jié)果耗時1秒

#若記錄DEBUG日誌,並設(shè)定只記錄>=INFO等級日誌,程式是否也會耗時1秒?
三種方法測試:

  • 拼接字串方式記錄slowString
  • 使用佔位符號方式記錄slowString
  • 先判斷日誌等級是否啟用DEBUG。


前兩個方式都會呼叫slowString,所以都耗時1s。且方式二就是使用佔位符記錄slowString,這種方式雖允許傳Object,不顯式拼接String,但也只是延遲(若日誌不記錄那就是省去)日誌參數(shù)物件.toString()字串拼接的耗時。

本案例除非事先判斷日誌級別,否則必定會呼叫slowString。
所以使用{}佔位符不能透過延遲參數(shù)值來獲取,來解決日誌資料所取得的效能問題。

除事先判斷日誌級別,還可透過lambda表達式延遲參數(shù)內(nèi)容取得。但SLF4J的API還不支援lambda,因此需使用Log4j2日誌API,把Lombok的@Slf4j註解替換為**@Log4j2**註解,即可提供lambda表達式參數(shù)的方法:

這樣呼叫debug,簽章Supplier,參數(shù)就會延遲到真正需要記錄日誌時再取得:



##所以debug4並不會呼叫slowString方法


只是換成

Log4j2 API,真正的日誌記錄還是走的Logback,這就是SLF4J適配的好處。

總結(jié)

    SLF4J統(tǒng)一了Java日誌框架。在使用SLF4J時,要理清楚其橋接API和綁定。若程式啟動時出現(xiàn)SLF4J錯誤提示,那可能是設(shè)定問題,可使用Maven的dependency:tree指令來梳理依賴關(guān)係。
  • 非同步日誌解決效能問題,是用空間換時間。但空間畢竟有限,當(dāng)空間滿,要考慮阻塞等待or丟棄日誌。如果更希望不丟棄重要日誌,那麼選擇阻塞等待;如果更希望程式不要因為日誌記錄而阻塞,那麼就需要丟棄日誌。
  • 日誌框架提供的參數(shù)化日誌記錄方式不能完全取代日誌等級判斷。若你的日誌量很大,取得日誌參數(shù)代價也很大,就要判斷日誌級別,避免不記錄日誌也要耗時取得日誌參數(shù)。

以上是搞懂Java日誌級別,重複記錄、遺失日誌問題的詳細內(nèi)容。更多資訊請關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

本網(wǎng)站聲明
本文內(nèi)容由網(wǎng)友自願投稿,版權(quán)歸原作者所有。本站不承擔(dān)相應(yīng)的法律責(zé)任。如發(fā)現(xiàn)涉嫌抄襲或侵權(quán)的內(nèi)容,請聯(lián)絡(luò)admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅(qū)動的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Laravel 教程
1597
29
PHP教程
1488
72
VSCODE設(shè)置。 JSON位置 VSCODE設(shè)置。 JSON位置 Aug 01, 2025 am 06:12 AM

settings.json文件位於用戶級或工作區(qū)級路徑,用於自定義VSCode設(shè)置。 1.用戶級路徑:Windows為C:\Users\\AppData\Roaming\Code\User\settings.json,macOS為/Users//Library/ApplicationSupport/Code/User/settings.json,Linux為/home//.config/Code/User/settings.json;2.工作區(qū)級路徑:項目根目錄下的.vscode/settings

如何使用JDBC處理Java的交易? 如何使用JDBC處理Java的交易? Aug 02, 2025 pm 12:29 PM

要正確處理JDBC事務(wù),必須先關(guān)閉自動提交模式,再執(zhí)行多個操作,最後根據(jù)結(jié)果提交或回滾;1.調(diào)用conn.setAutoCommit(false)以開始事務(wù);2.執(zhí)行多個SQL操作,如INSERT和UPDATE;3.若所有操作成功則調(diào)用conn.commit(),若發(fā)生異常則調(diào)用conn.rollback()確保數(shù)據(jù)一致性;同時應(yīng)使用try-with-resources管理資源,妥善處理異常並關(guān)閉連接,避免連接洩漏;此外建議使用連接池、設(shè)置保存點實現(xiàn)部分回滾,並保持事務(wù)盡可能短以提升性能。

在Java的掌握依賴注入春季和Guice 在Java的掌握依賴注入春季和Guice Aug 01, 2025 am 05:53 AM

依賴性(di)IsadesignpatternwhereObjectsReceivedenciesenciesExtern上,推廣looseSecouplingAndEaseerTestingThroughConstructor,setter,orfieldInjection.2.springfraMefringframeWorkSannotationsLikeLikeLike@component@component,@component,@service,@autowiredwithjava-service和@autowiredwithjava-ligatiredwithjava-lase-lightike

Python Itertools組合示例 Python Itertools組合示例 Jul 31, 2025 am 09:53 AM

itertools.combinations用於生成從可迭代對像中選取指定數(shù)量元素的所有不重複組合(順序無關(guān)),其用法包括:1.從列表中選2個元素組合,如('A','B')、('A','C')等,避免重複順序;2.對字符串取3個字符組合,如"abc"、"abd",適用於子序列生成;3.求兩數(shù)之和等於目標值的組合,如1 5=6,簡化雙重循環(huán)邏輯;組合與排列的區(qū)別在於順序是否重要,combinations視AB與BA為相同,而permutations視為不同;

故障排除常見的java`ofmemoryError`場景'' 故障排除常見的java`ofmemoryError`場景'' Jul 31, 2025 am 09:07 AM

java.lang.OutOfMemoryError:Javaheapspace表示堆內(nèi)存不足,需檢查大對象處理、內(nèi)存洩漏及堆設(shè)置,通過堆轉(zhuǎn)儲分析工具定位並優(yōu)化代碼;2.Metaspace錯誤因類元數(shù)據(jù)過多,常見於動態(tài)類生成或熱部署,應(yīng)限制MaxMetaspaceSize並優(yōu)化類加載;3.Unabletocreatenewnativethread因係統(tǒng)線程資源耗盡,需檢查線程數(shù)限制、使用線程池、調(diào)整棧大小;4.GCoverheadlimitexceeded指GC頻繁但回收少,應(yīng)分析GC日誌,優(yōu)化

Python Pytest夾具示例 Python Pytest夾具示例 Jul 31, 2025 am 09:35 AM

fixture是用於為測試提供預(yù)設(shè)環(huán)境或數(shù)據(jù)的函數(shù),1.使用@pytest.fixture裝飾器定義fixture;2.在測試函數(shù)中以參數(shù)形式註入fixture;3.yield之前執(zhí)行setup,之後執(zhí)行teardown;4.通過scope參數(shù)控製作用域,如function、module等;5.將共用fixture放在conftest.py中實現(xiàn)跨文件共享,從而提升測試的可維護性和復(fù)用性。

了解Java虛擬機(JVM)內(nèi)部 了解Java虛擬機(JVM)內(nèi)部 Aug 01, 2025 am 06:31 AM

TheJVMenablesJava’s"writeonce,runanywhere"capabilitybyexecutingbytecodethroughfourmaincomponents:1.TheClassLoaderSubsystemloads,links,andinitializes.classfilesusingbootstrap,extension,andapplicationclassloaders,ensuringsecureandlazyclassloa

如何使用Java的日曆? 如何使用Java的日曆? Aug 02, 2025 am 02:38 AM

使用java.time包中的類替代舊的Date和Calendar類;2.通過LocalDate、LocalDateTime和LocalTime獲取當(dāng)前日期時間;3.使用of()方法創(chuàng)建特定日期時間;4.利用plus/minus方法不可變地增減時間;5.使用ZonedDateTime和ZoneId處理時區(qū);6.通過DateTimeFormatter格式化和解析日期字符串;7.必要時通過Instant與舊日期類型兼容;現(xiàn)代Java中日期處理應(yīng)優(yōu)先使用java.timeAPI,它提供了清晰、不可變且線

See all articles