最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java打印高質(zhì)量日志的10條方法詳解

 更新時(shí)間:2025年06月09日 09:34:02   作者:Java技術(shù)小館  
你以為打日志是小事,也許正是這種輕視,讓你在凌晨三點(diǎn)被生產(chǎn)事故電話吵醒,一個(gè)優(yōu)秀的工程師和普通碼農(nóng)的區(qū)別,往往體現(xiàn)在那些看似微不足道的細(xì)節(jié)上,下面我們就來(lái)看看如何打印高質(zhì)量日志吧

一、日志的根本目的與常見(jiàn)誤區(qū)

1. 日志是為了"讀"

很多開(kāi)發(fā)者的第一反應(yīng)就是"日志當(dāng)然是為了排查問(wèn)題",但問(wèn)題是:誰(shuí)來(lái)讀這些日志?

大多數(shù)日志的命運(yùn)是:要么無(wú)人問(wèn)津,要么在深夜被迫閱讀。在你敲下每一行日志代碼時(shí),請(qǐng)想象凌晨三點(diǎn)被吵醒的自己(或者可憐的值班同事)需要從海量日志中找出線索。

// 糟糕的日志示例
log.info("處理開(kāi)始");
// 大量業(yè)務(wù)邏輯...
log.info("處理結(jié)束");

// 改進(jìn)后的日志
log.info("開(kāi)始處理用戶支付請(qǐng)求 userId={}, orderId={}, amount={}", userId, orderId, amount);
// 業(yè)務(wù)邏輯...
log.info("用戶支付請(qǐng)求處理完成 userId={}, orderId={}, result={}", userId, orderId, result);

2. 多打日志是一個(gè)危險(xiǎn)的建議

"多打點(diǎn)日志吧"是技術(shù)leader最常給的建議之一。但這句話就像"多喝點(diǎn)水"一樣空泛。沒(méi)有目的的狂打日志,只會(huì)讓真正有價(jià)值的信息淹沒(méi)在日志的海洋中。

我見(jiàn)過(guò)一個(gè)Java項(xiàng)目,5分鐘產(chǎn)生了50MB的日志文件,打開(kāi)一看,全是:

log.debug("進(jìn)入方法A");
log.debug("退出方法A");
log.debug("進(jìn)入方法B");
log.debug("退出方法B");
// ...無(wú)窮無(wú)盡...

這不是日志,這是垃圾。記住,每一行日志都有成本:存儲(chǔ)成本、I/O成本、閱讀成本。

3. 用戶視角 vs 開(kāi)發(fā)視角

日志應(yīng)該從誰(shuí)的視角出發(fā)?大多數(shù)日志都是從開(kāi)發(fā)者視角編寫(xiě)的:

log.info("用戶數(shù)據(jù)解析完畢,開(kāi)始更新緩存");

但真正有價(jià)值的日志應(yīng)該結(jié)合業(yè)務(wù)視角:

log.info("用戶{}的訂單{}狀態(tài)從{}變更為{}", userId, orderId, oldStatus, newStatus);

這樣的日志不僅技術(shù)人員看得懂,產(chǎn)品和運(yùn)營(yíng)同事也能大致理解,這在跨團(tuán)隊(duì)溝通時(shí)非常關(guān)鍵。

二、日志的等級(jí)藝術(shù)

1. 何時(shí)使用哪種級(jí)別

  • ERROR:系統(tǒng)無(wú)法正常運(yùn)行,需要立即關(guān)注
  • WARN:潛在的問(wèn)題,但不影響當(dāng)前功能
  • INFO:重要的業(yè)務(wù)事件,用于追蹤系統(tǒng)的工作流程
  • DEBUG:用于開(kāi)發(fā)調(diào)試的詳細(xì)信息
  • TRACE:最詳盡的信息,通常只在跟蹤代碼執(zhí)行路徑時(shí)使用

2. 為什么90%的ERROR級(jí)別日志都用錯(cuò)了

大多數(shù)開(kāi)發(fā)者會(huì)在catch塊里機(jī)械地寫(xiě):

try {
    // 業(yè)務(wù)邏輯
} catch (Exception e) {
    log.error("處理失敗", e);
}

但這是錯(cuò)的!很多異常是預(yù)期內(nèi)的業(yè)務(wù)異常,不是系統(tǒng)錯(cuò)誤。例如用戶輸入了錯(cuò)誤的密碼,這是業(yè)務(wù)邏輯,不是ERROR級(jí)別的系統(tǒng)故障。

try {
    authService.login(username, password);
} catch (InvalidCredentialsException e) {
    // 這是預(yù)期內(nèi)的業(yè)務(wù)場(chǎng)景,用INFO記錄
    log.info("用戶{}登錄失?。好艽a錯(cuò)誤", username);
    return "密碼錯(cuò)誤";
} catch (Exception e) {
    // 這是意外異常,用ERROR記錄
    log.error("用戶{}登錄過(guò)程發(fā)生系統(tǒng)異常", username, e);
    return "系統(tǒng)異常";
}

3. 如何正確劃分日志級(jí)別的邊界

簡(jiǎn)單的判斷標(biāo)準(zhǔn):

  • ERROR:需要人工介入處理的問(wèn)題
  • WARN:今天不處理,明天可能變成ERROR的問(wèn)題
  • INFO:標(biāo)記重要業(yè)務(wù)節(jié)點(diǎn),幫助理解系統(tǒng)運(yùn)行狀態(tài)
  • DEBUG/TRACE:臨時(shí)排查問(wèn)題用,生產(chǎn)環(huán)境通常關(guān)閉

4. 不同環(huán)境的日志級(jí)別策略

  • 開(kāi)發(fā)環(huán)境:DEBUG或更詳細(xì),幫助開(kāi)發(fā)者調(diào)試
  • 測(cè)試環(huán)境:INFO,關(guān)注業(yè)務(wù)流程是否正確
  • 生產(chǎn)環(huán)境:WARN或INFO,取決于系統(tǒng)規(guī)模和性能要求

大型系統(tǒng)建議:

# 生產(chǎn)環(huán)境配置示例
logging.level.root=WARN
logging.level.com.yourcompany.core=INFO
logging.level.com.yourcompany.thirdparty=ERROR

三、日志內(nèi)容的軍規(guī)

1. 一條好日志的必備要素

一條完美的日志應(yīng)包含:

  • 時(shí)間(日志框架通常自動(dòng)提供)
  • 環(huán)境/實(shí)例標(biāo)識(shí)(在分布式系統(tǒng)中尤其重要)
  • 請(qǐng)求標(biāo)識(shí)(如TraceId)
  • 操作主體(誰(shuí)做了什么)
  • 關(guān)鍵業(yè)務(wù)參數(shù)
  • 操作結(jié)果
// 差的日志
log.info("支付成功");

// 好的日志
log.info("[訂單中心] 用戶{}完成訂單{}支付 金額={} 渠道={} 耗時(shí)={}ms", 
    userId, orderId, amount, channel, costTime);

2. 避免日志污染的7個(gè)實(shí)用技巧

1.避免在循環(huán)中打印日志

// 差的日志
log.info("支付成功");

// 好的日志
log.info("[訂單中心] 用戶{}完成訂單{}支付 金額={} 渠道={} 耗時(shí)={}ms", 
    userId, orderId, amount, channel, costTime);

2.使用占位符,而不是字符串拼接

// 糟糕的實(shí)踐
for (Item item : items) {
    log.info("處理商品: {}", item);  // 可能輸出成千上萬(wàn)條
}

// 改進(jìn)
log.info("開(kāi)始處理{}個(gè)商品", items.size());
// 處理邏輯
log.info("完成處理{}個(gè)商品, 成功={}, 失敗={}", items.size(), successCount, failCount);

3.警惕大對(duì)象輸出

// 危險(xiǎn)操作
log.debug("用戶數(shù)據(jù): {}", user);  // 可能輸出大量無(wú)用信息

// 更好的做法
log.debug("用戶基本信息: id={}, name={}, type={}", user.getId(), user.getName(), user.getType());

4.使用日志脫敏工具

5.引入SLF4J MDC機(jī)制關(guān)聯(lián)同一請(qǐng)求的日志

6.定期清理不再使用的日志

7.使用斷言判斷是否需要打印復(fù)雜日志

if (log.isDebugEnabled()) {
    log.debug("復(fù)雜對(duì)象詳情: {}", generateExpensiveDebugInfo(obj));
}

3. 敏感信息處理

這是最容易被忽視的問(wèn)題,卻可能導(dǎo)致最嚴(yán)重的后果。請(qǐng)務(wù)必從日志中去除:

  • 密碼、密鑰、令牌
  • 身份證、手機(jī)號(hào)、銀行卡
  • 用戶地址等個(gè)人信息
// 危險(xiǎn)的日志
log.info("用戶登錄: username={}, password={}", username, password);

// 安全的日志
log.info("用戶登錄: username={}, password=****", username);

// 更好的做法:使用專業(yè)的日志脫敏工具
log.info("用戶信息: {}", LogSensitiveUtils.mask(userInfo));

四、結(jié)構(gòu)化日志的威力

1. 為什么說(shuō)非結(jié)構(gòu)化日志正在被淘汰

傳統(tǒng)日志是為人類閱讀設(shè)計(jì)的,但現(xiàn)代系統(tǒng)規(guī)模已經(jīng)超出人類直接閱讀的能力范圍。結(jié)構(gòu)化日志(如JSON格式)具有以下優(yōu)勢(shì):

  • 機(jī)器可讀,便于自動(dòng)化分析
  • 字段明確,不會(huì)混淆
  • 易于索引和搜索
  • 支持復(fù)雜數(shù)據(jù)結(jié)構(gòu)

2. JSON日志格式的實(shí)現(xiàn)方法

在Spring Boot項(xiàng)目中配置JSON日志:

# application.properties
logging.pattern.console=
logging.config=classpath:logback-spring.xml
<!-- logback-spring.xml -->
<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
    </appender>
    
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
    </root>
</configuration>

輸出示例:

{
  "timestamp": "2023-01-01T12:00:00.000Z",
  "level": "INFO",
  "thread": "http-nio-8080-exec-1",
  "logger": "com.example.OrderService",
  "message": "用戶支付成功",
  "userId": "12345",
  "orderId": "ORD9876",
  "amount": 99.99,
  "channel": "wechat"
}

五、日志與性能的選擇

1. 日志為何會(huì)影響性能

日志操作是典型的I/O密集型操作,主要性能瓶頸在于:

  • 磁盤(pán)I/O(寫(xiě)入速度有限)
  • 文件系統(tǒng)緩存
  • 字符串處理(格式化、拼接)
  • 線程同步(在多線程環(huán)境下)

測(cè)試表明,大量日志可能導(dǎo)致應(yīng)用吞吐量下降30%-50%!

2. 異步日志的正確實(shí)現(xiàn)方式

通過(guò)異步方式將日志寫(xiě)入從主線程分離:

<!-- logback異步配置 -->
<configuration>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 配置省略 -->
    </appender>
    
    <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="FILE" />
        <queueSize>512</queueSize>
        <discardingThreshold>0</discardingThreshold>
        <includeCallerData>false</includeCallerData>
    </appender>
    
    <root level="INFO">
        <appender-ref ref="ASYNC" />
    </root>
</configuration>

3. 高并發(fā)場(chǎng)景下的日志實(shí)現(xiàn)

采樣日志:只記錄部分請(qǐng)求的詳細(xì)信息

// 簡(jiǎn)單的采樣實(shí)現(xiàn)
if (Math.random() < 0.01) {  // 1%采樣率
    log.info("詳細(xì)請(qǐng)求信息: request={}, headers={}", request, headers);
}

批量日志:將多條日志合并為一條寫(xiě)入

異步非阻塞日志:使用Disruptor等高性能隊(duì)列

日志緩沖區(qū)調(diào)優(yōu):增加緩沖區(qū)大小,減少刷盤(pán)頻率

六、日志的運(yùn)維與治理

1. 日志輪轉(zhuǎn)與歸檔策略

日志文件不能無(wú)限增長(zhǎng),需要設(shè)置輪轉(zhuǎn)策略:

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>app.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>app-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <maxFileSize>100MB</maxFileSize>
        <maxHistory>30</maxHistory>
        <totalSizeCap>3GB</totalSizeCap>
    </rollingPolicy>
    <encoder>
        <pattern>%d %p %c{1} [%t] %m%n</pattern>
    </encoder>
</appender>

2. 集中式日志平臺(tái)的選擇

現(xiàn)代系統(tǒng)普遍采用ELK(Elasticsearch + Logstash + Kibana)或EFK(Elasticsearch + Fluentd + Kibana)棧來(lái)集中管理日志。

簡(jiǎn)單架構(gòu):

應(yīng)用 -> Filebeat -> Logstash -> Elasticsearch -> Kibana

關(guān)鍵是在應(yīng)用層面保證日志格式統(tǒng)一、字段規(guī)范,才能充分利用日志平臺(tái)的能力。

3. 日志監(jiān)控告警的設(shè)計(jì)原則

基于日志的監(jiān)控策略:

  • 錯(cuò)誤率監(jiān)控:ERROR日志量突增
  • 關(guān)鍵字監(jiān)控:特定異常類型或錯(cuò)誤碼
  • 模式識(shí)別:異常的日志模式變化
  • 缺失檢測(cè):預(yù)期日志未出現(xiàn)(如定時(shí)任務(wù)未執(zhí)行)

設(shè)置合理的告警閾值和降噪規(guī)則,避免"狼來(lái)了"效應(yīng)。

七、實(shí)戰(zhàn)案例解析

1. 日志缺失導(dǎo)致的災(zāi)難性后果

某電商平臺(tái)在618期間遭遇支付系統(tǒng)故障,用戶支付后無(wú)法收到訂單確認(rèn),導(dǎo)致大量重復(fù)下單。系統(tǒng)日志竟然只有:

2023-06-18 10:30:15 ERROR Process failed
2023-06-18 10:30:17 ERROR Process failed
2023-06-18 10:30:22 ERROR Process failed

沒(méi)有任何上下文信息,技術(shù)團(tuán)隊(duì)不得不手動(dòng)比對(duì)數(shù)據(jù)庫(kù)數(shù)據(jù)排查問(wèn)題。最終發(fā)現(xiàn)是支付回調(diào)接口超時(shí),但由于缺乏有效日志,排查過(guò)程耗時(shí)4小時(shí),造成直接經(jīng)濟(jì)損失超過(guò)100萬(wàn)。

改進(jìn)后的日志:

2023-09-01 14:22:15 ERROR [PaymentService] 支付回調(diào)處理失敗 orderId=ORD123456 userId=10001 paymentId=PAY789012 channel=alipay amount=1299.00 errorCode=TIMEOUT errorMsg=第三方接口連接超時(shí) traceId=c7d2eb9f14ce

有了詳細(xì)日志后,類似問(wèn)題10分鐘內(nèi)可確定根因并采取補(bǔ)救措施。

2. 從日志優(yōu)化看系統(tǒng)設(shè)計(jì)的演進(jìn)

某社交應(yīng)用的消息推送服務(wù)日志優(yōu)化經(jīng)歷:

第一階段:簡(jiǎn)單文本日志

2022-01-01 12:00:00 INFO 推送消息成功

第二階段:添加基本參數(shù)

2022-03-01 12:00:00 INFO 推送消息成功 userId=123 msgId=456

第三階段:結(jié)構(gòu)化JSON日志 + 分布式追蹤

{
  "timestamp": "2022-06-01T12:00:00.000Z",
  "level": "INFO",
  "service": "push-service",
  "instance": "push-pod-3",
  "traceId": "abc123def456",
  "message": "消息推送成功",
  "data": {
    "userId": "123",
    "msgId": "456",
    "channel": "app",
    "deviceType": "iOS",
    "latency": 24
  }
}

第四階段:引入業(yè)務(wù)指標(biāo)和日志聚合

{
  "timestamp": "2022-10-01T12:00:00.000Z",
  "level": "INFO",
  "service": "push-service",
  "instance": "push-pod-3",
  "traceId": "abc123def456",
  "message": "批量消息推送完成",
  "data": {
    "batchId": "batch789",
    "totalMessages": 1000,
    "successCount": 950,
    "failCount": 50,
    "avgLatency": 28,
    "p95Latency": 42,
    "errorBreakdown": {
      "TIMEOUT": 30,
      "DEVICE_OFFLINE": 15,
      "PERMISSION_DENIED": 5
    }
  }
}

這種演進(jìn)不僅改進(jìn)了日志本身,還反過(guò)來(lái)優(yōu)化了系統(tǒng)的可觀測(cè)性設(shè)計(jì),最終使整個(gè)系統(tǒng)更加健壯和可維護(hù)。

以上就是Java打印高質(zhì)量日志的10條方法詳解的詳細(xì)內(nèi)容,更多關(guān)于Java打印日志的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java字符串中有多個(gè)分隔符的處理方式

    Java字符串中有多個(gè)分隔符的處理方式

    這篇文章主要介紹了Java字符串中有多個(gè)分隔符的處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-05-05
  • 使用Java實(shí)現(xiàn)價(jià)格加密與優(yōu)化功能

    使用Java實(shí)現(xiàn)價(jià)格加密與優(yōu)化功能

    在現(xiàn)代軟件開(kāi)發(fā)中,數(shù)據(jù)加密是一個(gè)非常重要的環(huán)節(jié),尤其是在處理敏感信息(如價(jià)格、用戶數(shù)據(jù)等)時(shí),本文將詳細(xì)介紹如何使用?Java?實(shí)現(xiàn)價(jià)格加密,并對(duì)代碼進(jìn)行優(yōu)化,需要的朋友可以參考下
    2025-01-01
  • 關(guān)于JAVA11中圖片與BASE64相互轉(zhuǎn)換的實(shí)現(xiàn)

    關(guān)于JAVA11中圖片與BASE64相互轉(zhuǎn)換的實(shí)現(xiàn)

    這篇文章主要介紹了關(guān)于JAVA11中圖片與BASE64相互轉(zhuǎn)換的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • spring boot 實(shí)現(xiàn)阿里云視頻點(diǎn)播功能(刪除視頻)

    spring boot 實(shí)現(xiàn)阿里云視頻點(diǎn)播功能(刪除視頻)

    這篇文章主要介紹了spring boot 實(shí)現(xiàn)阿里云視頻點(diǎn)播(刪除視頻功能),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • Maven本地jar引用的實(shí)現(xiàn)方法

    Maven本地jar引用的實(shí)現(xiàn)方法

    這篇文章主要介紹了Maven本地jar引用的實(shí)現(xiàn)方法的相關(guān)資料,希望通過(guò)本文能幫助到大家,實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下
    2017-10-10
  • ssm框架上傳圖片保存到本地和數(shù)據(jù)庫(kù)示例

    ssm框架上傳圖片保存到本地和數(shù)據(jù)庫(kù)示例

    本篇文章主要介紹了ssm框架上傳圖片保存到本地和數(shù)據(jù)庫(kù)示例,主要使用了Spring+SpringMVC+MyBatis框架集合,有興趣的可以了解一下。
    2017-03-03
  • Java最簡(jiǎn)單的DES加密算法實(shí)現(xiàn)案例

    Java最簡(jiǎn)單的DES加密算法實(shí)現(xiàn)案例

    下面小編就為大家?guī)?lái)一篇Java最簡(jiǎn)單的DES加密算法實(shí)現(xiàn)案例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-06-06
  • jackson 如何將實(shí)體轉(zhuǎn)json json字符串轉(zhuǎn)實(shí)體

    jackson 如何將實(shí)體轉(zhuǎn)json json字符串轉(zhuǎn)實(shí)體

    這篇文章主要介紹了jackson 實(shí)現(xiàn)將實(shí)體轉(zhuǎn)json json字符串轉(zhuǎn)實(shí)體,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • SpringBoot項(xiàng)目整合OpenFeign啟動(dòng)失敗及運(yùn)行時(shí)常見(jiàn)錯(cuò)誤解決方案

    SpringBoot項(xiàng)目整合OpenFeign啟動(dòng)失敗及運(yùn)行時(shí)常見(jiàn)錯(cuò)誤解決方案

    文章總結(jié)了在使用Feign進(jìn)行服務(wù)調(diào)用時(shí)可能遇到的常見(jiàn)問(wèn)題及其解決方案,涵蓋了依賴配置、接口定義、運(yùn)行時(shí)錯(cuò)誤和其它常見(jiàn)問(wèn)題的解決方法,建議使用兼容的版本、統(tǒng)一的包名結(jié)構(gòu),并為接口方法添加HTTP注解,感興趣的朋友跟隨小編一起看看吧
    2026-01-01
  • Maven配置倉(cāng)庫(kù)的方法步驟

    Maven配置倉(cāng)庫(kù)的方法步驟

    本文主要介紹了Maven配置倉(cāng)庫(kù)的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06

最新評(píng)論

洛浦县| 本溪| 林口县| 闸北区| 吉隆县| 通辽市| 太仆寺旗| 石棉县| 广饶县| 阳江市| 石泉县| 剑河县| 剑河县| 武川县| 松潘县| 靖边县| 高州市| 醴陵市| 仁怀市| 南昌市| 新泰市| 静乐县| 宁德市| 衡东县| 瑞安市| 招远市| 苏州市| 巩义市| 崇州市| 西乡县| 江西省| 武陟县| 米林县| 汉阴县| 常山县| 唐河县| 内丘县| 梅河口市| 阳曲县| 永德县| 洞口县|