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

系統(tǒng)講解Java如何優(yōu)雅地打印日志

 更新時間:2026年05月30日 10:13:50   作者:xiaoyu?  
本文將介紹如何優(yōu)雅地打印日志,告別 System.out.println,使用專業(yè)的日志框架(Logback/Log4j2)來管理日志,讓你的日志更專業(yè)、更規(guī)范、更易于排查問題

前言

“為什么生產(chǎn)環(huán)境的代碼還在用 System.out.println?”、“日志格式混亂,沒有時間戳,不知道是誰打印的”、“日志級別使用不當(dāng),錯誤信息混在普通信息中”。

這些都是開發(fā)中常見的日志問題。本文將介紹如何優(yōu)雅地打印日志,告別 System.out.println,使用專業(yè)的日志框架(Logback/Log4j2)來管理日志,讓你的日志更專業(yè)、更規(guī)范、更易于排查問題。

一、為什么要使用日志框架

1.1 System.out.println 的問題

System.out.println 的主要問題:

1.性能問題

  • 同步輸出,會阻塞線程
  • 無法關(guān)閉,即使生產(chǎn)環(huán)境也會輸出
  • 無法按級別控制輸出

2.功能缺失

  • 沒有日志級別(DEBUG、INFO、WARN、ERROR)
  • 沒有時間戳、線程信息、類名等關(guān)鍵信息
  • 沒有上下文信息

3.管理困難

  • 無法按級別過濾日志
  • 無法按日期分割日志文件
  • 無法分類存儲不同類型的日志
  • 無法遠程傳輸日志

1.2 日志框架的優(yōu)勢

日志框架的優(yōu)勢:

1.性能優(yōu)化

  • 支持異步輸出,不阻塞主線程
  • 支持日志級別控制,生產(chǎn)環(huán)境只輸出WARN和ERROR
  • 支持按包路徑控制日志級別

2.功能豐富

  • 支持多種日志級別(DEBUG、INFO、WARN、ERROR)
  • 支持自定義格式(時間、線程、類名、行號等)
  • 支持MDC(Mapped Diagnostic Context)上下文信息

3.管理便捷

  • 支持按日期、大小分割日志文件
  • 支持按類型分類存儲日志
  • 支持異步寫入數(shù)據(jù)庫或消息隊列
  • 支持結(jié)構(gòu)化日志(JSON格式)

二、日志級別

2.1 日志級別定義

/**
 * 日志級別從低到高
 * TRACE < DEBUG < INFO < WARN < ERROR
 */

// TRACE:追蹤信息,最詳細的日志信息
// 使用場景:調(diào)試時追蹤程序執(zhí)行流程
logger.trace("用戶登錄,用戶ID: {}", userId);

// DEBUG:調(diào)試信息,開發(fā)調(diào)試時使用
// 使用場景:開發(fā)階段,記錄變量值、方法調(diào)用等
logger.debug("查詢用戶,用戶ID: {}", userId);

// INFO:重要信息,記錄關(guān)鍵業(yè)務(wù)流程
// 使用場景:記錄重要操作、系統(tǒng)狀態(tài)等
logger.info("用戶創(chuàng)建成功,用戶ID: {}", userId);

// WARN:警告信息,潛在的問題
// 使用場景:參數(shù)校驗失敗、配置警告等
logger.warn("用戶余額不足,用戶ID: {}, 余額: {}", userId, balance);

// ERROR:錯誤信息,需要立即處理
// 使用場景:異常捕獲、系統(tǒng)錯誤等
logger.error("訂單創(chuàng)建失敗,用戶ID: {}", userId, exception);

2.2 日志級別使用原則

使用建議:

級別使用場景生產(chǎn)環(huán)境
TRACE追蹤詳細執(zhí)行流程關(guān)閉
DEBUG調(diào)試信息、變量值關(guān)閉
INFO重要操作、業(yè)務(wù)流程開啟
WARN警告、潛在問題開啟
ERROR錯誤、異常開啟

三、Logback 配置

3.1 Maven依賴

<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Starter Web (已包含logback) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Logback Classic -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.4.14</version>
    </dependency>
    <!-- Logback Access -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-access</artifactId>
        <version>1.4.14</version>
    </dependency>
</dependencies>

3.2 基礎(chǔ)配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 定義日志輸出格式 -->
    <property name="LOG_PATTERN" 
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
    <!-- 定義日志文件路徑 -->
    <property name="LOG_PATH" value="logs"/>
    <!-- 控制臺輸出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!-- 文件輸出 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/app.log</file>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <!-- 滾動策略:按日期滾動 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/app.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
    </appender>
    <!-- 錯誤日志單獨輸出 -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/error.log</file>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
    </appender>
    <!-- 異步日志 -->
    <appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="FILE"/>
        <queueSize>512</queueSize>
        <discardingThreshold>0</discardingThreshold>
    </appender>
    <!-- 根日志器 -->
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="ASYNC_FILE"/>
        <appender-ref ref="ERROR_FILE"/>
    </root>
</configuration>

3.3 完整配置(推薦)

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <!-- 定義常量 -->
    <property name="LOG_HOME" value="logs"/>
    <property name="APP_NAME" value="myapp"/>
    <!-- 日志格式 -->
    <property name="CONSOLE_LOG_PATTERN" 
              value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
    <property name="FILE_LOG_PATTERN" 
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
    <!-- 控制臺輸出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!-- 文件輸出 - 所有日志 -->
    <appender name="FILE_ALL" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/${APP_NAME}-all.log</file>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/${APP_NAME}-all.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>100MB</maxFileSize>
            <maxHistory>30</maxHistory>
            <totalSizeCap>3GB</totalSizeCap>
        </rollingPolicy>
    </appender>
    <!-- 文件輸出 - 錯誤日志 -->
    <appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/${APP_NAME}-error.log</file>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/${APP_NAME}-error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>50MB</maxFileSize>
            <maxHistory>60</maxHistory>
            <totalSizeCap>5GB</totalSizeCap>
        </rollingPolicy>
    </appender>
    <!-- 異步日志 - 提升性能 -->
    <appender name="ASYNC_FILE_ALL" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="FILE_ALL"/>
        <queueSize>1024</queueSize>
        <discardingThreshold>0</discardingThreshold>
    </appender>
    <appender name="ASYNC_FILE_ERROR" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="FILE_ERROR"/>
        <queueSize>512</queueSize>
        <discardingThreshold>0</discardingThreshold>
    </appender>
    <!-- 開發(fā)環(huán)境 -->
    <springProfile name="dev">
        <logger name="com.example" level="DEBUG"/>
        <root level="DEBUG">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="ASYNC_FILE_ALL"/>
            <appender-ref ref="ASYNC_FILE_ERROR"/>
        </root>
    </springProfile>
    <!-- 測試環(huán)境 -->
    <springProfile name="test">
        <logger name="com.example" level="INFO"/>
        <root level="INFO">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="ASYNC_FILE_ALL"/>
            <appender-ref ref="ASYNC_FILE_ERROR"/>
        </root>
    </springProfile>
    <!-- 生產(chǎn)環(huán)境 -->
    <springProfile name="prod">
        <logger name="com.example" level="INFO"/>
        <root level="INFO">
            <appender-ref ref="ASYNC_FILE_ALL"/>
            <appender-ref ref="ASYNC_FILE_ERROR"/>
        </root>
    </springProfile>
</configuration>

3.4 配置說明

# application.yml
logging:
  config: classpath:logback-spring.xml
  level:
    root: INFO
    com.example: DEBUG
    org.springframework: INFO
    org.hibernate.SQL: DEBUG
    org.hibernate.type.descriptor.sql.BasicBinder: TRACE

四、Log4j2 配置

4.1 Maven依賴

<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <!-- 排除默認的logback -->
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!-- Log4j2 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-log4j2</artifactId>
    </dependency>
    <!-- 異步日志支持 -->
    <dependency>
        <groupId>com.lmax</groupId>
        <artifactId>disruptor</artifactId>
        <version>3.4.4</version>
    </dependency>
</dependencies>

4.2 基礎(chǔ)配置

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <!-- 定義日志格式 -->
    <Properties>
        <Property name="LOG_PATTERN">
            %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
        </Property>
        <Property name="LOG_PATH">logs</Property>
    </Properties>
    <!-- 控制臺輸出 -->
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="${LOG_PATTERN}"/>
        </Console>
        <!-- 文件輸出 - 所有日志 -->
        <RollingFile name="RollingFile" fileName="${LOG_PATH}/app.log"
                     filePattern="${LOG_PATH}/app.%d{yyyy-MM-dd}-%i.log">
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>
        <!-- 文件輸出 - 錯誤日志 -->
        <RollingFile name="ErrorFile" fileName="${LOG_PATH}/error.log"
                     filePattern="${LOG_PATH}/error.%d{yyyy-MM-dd}-%i.log">
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <SizeBasedTriggeringPolicy size="50 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="60"/>
        </RollingFile>
        <!-- 異步日志 -->
        <AsyncLogger name="AsyncRollingFile" level="info" additivity="false">
            <AppenderRef ref="RollingFile"/>
        </AsyncLogger>
        <AsyncLogger name="AsyncErrorFile" level="error" additivity="false">
            <AppenderRef ref="ErrorFile"/>
        </AsyncLogger>
    </Appenders>
    <!-- 日志器配置 -->
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="AsyncRollingFile"/>
            <AppenderRef ref="AsyncErrorFile"/>
        </Root>
    </Loggers>
</Configuration>

4.3 完整配置(推薦)

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
    <!-- 定義變量 -->
    <Properties>
        <Property name="LOG_HOME">logs</Property>
        <Property name="APP_NAME">myapp</Property>
        <Property name="LOG_PATTERN">
            %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{50} - %msg%n%ex
        </Property>
    </Properties>
    <!-- Appenders -->
    <Appenders>
        <!-- 控制臺輸出 -->
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="${LOG_PATTERN}"/>
        </Console>
        <!-- 文件輸出 - 所有日志 -->
        <RollingRandomAccessFile name="RollingFile"
                              fileName="${LOG_HOME}/${APP_NAME}.log"
                              filePattern="${LOG_HOME}/${APP_NAME}-%d{yyyy-MM-dd}-%i.log"
                              immediateFlush="false"
                              append="true">
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <OnStartupTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
            </Policies>
            <DefaultRolloverStrategy max="30"/>
        </RollingRandomAccessFile>
        <!-- 文件輸出 - 錯誤日志 -->
        <RollingRandomAccessFile name="ErrorFile"
                              fileName="${LOG_HOME}/${APP_NAME}-error.log"
                              filePattern="${LOG_HOME}/${APP_NAME}-error-%d{yyyy-MM-dd}-%i.log"
                              immediateFlush="false"
                              append="true">
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <OnStartupTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="50 MB"/>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
            </Policies>
            <DefaultRolloverStrategy max="60"/>
        </RollingRandomAccessFile>
        <!-- 異步日志 -->
        <AsyncLogger name="AsyncRollingFile" level="info" additivity="false">
            <AppenderRef ref="RollingFile"/>
        </AsyncLogger>
        <AsyncLogger name="AsyncErrorFile" level="error" additivity="false">
            <AppenderRef ref="ErrorFile"/>
        </AsyncLogger>
    </Appenders>
    <!-- Loggers -->
    <Loggers>
        <!-- Spring框架日志級別 -->
        <Logger name="org.springframework" level="INFO"/>
        <Logger name="org.hibernate" level="INFO"/>
        <Logger name="org.apache" level="INFO"/>
        <!-- 應(yīng)用日志級別 -->
        <Logger name="com.example" level="DEBUG"/>
        <!-- Root Logger -->
        <Root level="INFO">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="AsyncRollingFile"/>
            <AppenderRef ref="AsyncErrorFile"/>
        </Root>
    </Loggers>
</Configuration>

五、代碼中使用日志

5.1 基本使用

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 日志使用示例
 */
public class UserService {

    // 獲取Logger實例
    private static final Logger logger = LoggerFactory.getLogger(UserService.class);

    /**
     * 創(chuàng)建用戶
     */
    public void createUser(User user) {
        // TRACE:追蹤信息
        logger.trace("開始創(chuàng)建用戶,用戶名: {}", user.getUsername());

        try {
            // DEBUG:調(diào)試信息
            logger.debug("用戶信息: {}", user);

            // 業(yè)務(wù)邏輯
            saveUser(user);

            // INFO:重要信息
            logger.info("用戶創(chuàng)建成功,用戶ID: {}", user.getId());

        } catch (Exception e) {
            // ERROR:錯誤信息(包含異常堆棧)
            logger.error("用戶創(chuàng)建失敗,用戶名: {}", user.getUsername(), e);
        }
    }

    /**
     * 參數(shù)校驗失敗
     */
    public void validateUser(User user) {
        if (user.getUsername() == null || user.getUsername().isEmpty()) {
            // WARN:警告信息
            logger.warn("用戶名為空,用戶ID: {}", user.getId());
        }
    }
}

5.2 使用占位符

/**
 * 日志占位符使用示例
 */
public class OrderService {

    private static final Logger logger = LoggerFactory.getLogger(OrderService.class);

    /**
     * 創(chuàng)建訂單
     */
    public void createOrder(Order order) {
        // 使用占位符{},避免字符串拼接
        // 錯誤做法:logger.info("訂單創(chuàng)建成功,訂單ID: " + order.getId());
        // 正確做法:
        logger.info("訂單創(chuàng)建成功,訂單ID: {}", order.getId());

        // 多個占位符
        logger.info("訂單信息: ID={}, 用戶ID={}, 金額={}", 
                   order.getId(), order.getUserId(), order.getAmount());

        // 占位符數(shù)量不匹配時,會使用toString()
        logger.info("訂單創(chuàng)建: {}", order);
    }
}

5.3 異常日志

/**
 * 異常日志示例
 */
public class PaymentService {

    private static final Logger logger = LoggerFactory.getLogger(PaymentService.class);

    /**
     * 支付處理
     */
    public void processPayment(Payment payment) {
        try {
            // 業(yè)務(wù)邏輯
            process(payment);

        } catch (InsufficientBalanceException e) {
            // 帶異常的日志
            logger.error("余額不足,用戶ID: {}, 訂單ID: {}", 
                        payment.getUserId(), payment.getOrderId(), e);

        } catch (PaymentException e) {
            // 錯誤日志
            logger.error("支付處理失敗,訂單ID: {}", payment.getOrderId(), e);

        } catch (Exception e) {
            // 未預(yù)期的異常
            logger.error("系統(tǒng)異常,訂單ID: {}", payment.getOrderId(), e);
        }
    }
}

5.4 MDC 上下文

import org.slf4j.MDC;

/**
 * MDC(Mapped Diagnostic Context)使用示例
 */
public class RequestContext {

    private static final Logger logger = LoggerFactory.getLogger(RequestContext.class);

    /**
     * 設(shè)置請求上下文
     */
    public static void setContext(String traceId, String userId) {
        // 設(shè)置MDC
        MDC.put("traceId", traceId);
        MDC.put("userId", userId);
        MDC.put("requestTime", String.valueOf(System.currentTimeMillis()));

        logger.info("請求上下文已設(shè)置");
    }

    /**
     * 清除請求上下文
     */
    public static void clearContext() {
        MDC.clear();
        logger.info("請求上下文已清除");
    }

    /**
     * 使用示例
     */
    public void handleRequest(String traceId, String userId) {
        try {
            setContext(traceId, userId);

            // 日志會自動包含MDC信息
            logger.info("處理請求");

            // 業(yè)務(wù)邏輯
            processRequest();

        } finally {
            clearContext();
        }
    }
}

logback-spring.xml 中配置 MDC:

<pattern>
    %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%X{userId}] %-5level %logger{36} - %msg%n
</pattern>

六、日志格式說明

6.1 常用格式化符號

/**
 * Logback 格式化符號
 */
%logger{36}      // Logger名稱,最多36個字符
%date           // 日期和時間
%d{yyyy-MM-dd}  // 日期
%d{HH:mm:ss.SSS} // 時間
%p / %-5level    // 日志級別(5個字符寬度,左對齊)
%thread         // 線程名稱
%m / %msg        // 日志消息
%n              // 換行符
%ex             // 異常堆棧
%caller         // 調(diào)用者信息
%line           // 行號
%file           // 文件名
%M              // 方法名

/**
 * Log4j2 格式化符號
 */
%d              // 日期和時間
%p / %-5p       // 日志級別
%t              // 線程名稱
%c / %-36c      // Logger名稱
%m              // 日志消息
%n              // 換行符
%ex             // 異常堆棧
%F              // 文件名
%L              // 行號
%M              // 方法名
%r              // 毫秒數(shù)

6.2 推薦的日志格式

<!-- 控制臺格式(帶顏色) -->
<pattern>
    %clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} 
    %clr(%5p) 
    %clr([%15.15t]){faint} 
    %clr(%-40.40logger{39}){cyan} 
    %clr(:){faint} 
    %m%n
</pattern>
<!-- 文件格式 -->
<pattern>
    %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n%ex
</pattern>
<!-- 包含MDC的格式 -->
<pattern>
    %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%X{userId}] %-5level %logger{36} - %msg%n
</pattern>

七、日志最佳實踐

7.1 最佳實踐示例

/**
 * 日志最佳實踐
 */
public class OrderService {

    private static final Logger logger = LoggerFactory.getLogger(OrderService.class);

    /**
     * 最佳實踐1:合理使用日志級別
     */
    public void createOrder(Order order) {
        logger.trace("開始創(chuàng)建訂單");  // 追蹤信息
        logger.debug("訂單詳情: {}", order);  // 調(diào)試信息

        // 重要操作
        logger.info("訂單創(chuàng)建成功,訂單ID: {}, 用戶ID: {}", 
                   order.getId(), order.getUserId());
    }

    /**
     * 最佳實踐2:使用占位符而不是字符串拼接
     */
    public void updateOrder(Order order) {
        // 錯誤做法
        // logger.info("訂單更新: ID=" + order.getId() + ", 狀態(tài)=" + order.getStatus());

        // 正確做法
        logger.info("訂單更新: ID={}, 狀態(tài)={}", order.getId(), order.getStatus());
    }

    /**
     * 最佳實踐3:異常日志包含關(guān)鍵信息
     */
    public void processOrder(Long orderId) {
        try {
            process(orderId);
        } catch (OrderNotFoundException e) {
            // 包含關(guān)鍵業(yè)務(wù)信息
            logger.error("訂單不存在,訂單ID: {}", orderId, e);
        } catch (Exception e) {
            // 包含異常堆棧
            logger.error("訂單處理失敗,訂單ID: {}", orderId, e);
        }
    }

    /**
     * 最佳實踐4:避免在循環(huán)中打印日志
     */
    public void processBatch(List<Long> orderIds) {
        // 錯誤做法:在循環(huán)中打印日志
        // for (Long orderId : orderIds) {
        //     logger.info("處理訂單: {}", orderId);
        // }

        // 正確做法:批量打印
        logger.info("批量處理訂單,數(shù)量: {}, 訂單ID列表: {}", 
                   orderIds.size(), orderIds);

        // 或者只打印摘要
        logger.info("批量處理訂單,數(shù)量: {}", orderIds.size());
    }

    /**
     * 最佳實踐5:使用MDC記錄請求上下文
     */
    public void handleRequest(String traceId, Long userId) {
        MDC.put("traceId", traceId);
        MDC.put("userId", String.valueOf(userId));

        try {
            logger.info("開始處理請求");
            // 業(yè)務(wù)邏輯
        } finally {
            MDC.clear();
        }
    }
}

7.2 性能優(yōu)化建議

/**
 * 日志性能優(yōu)化
 */
public class PerformanceOptimization {

    /**
     * 優(yōu)化1:判斷日志級別
     */
    public void expensiveOperation() {
        // 錯誤做法:即使日志級別不滿足,也會執(zhí)行字符串拼接
        // logger.debug("用戶列表: " + getUsers());

        // 正確做法:先判斷日志級別
        if (logger.isDebugEnabled()) {
            logger.debug("用戶列表: {}", getUsers());
        }
    }

    /**
     * 優(yōu)化2:使用異步日志
     */
    // 在配置文件中配置異步Appender

    /**
     * 優(yōu)化3:避免記錄敏感信息
     */
    public void logPayment(Payment payment) {
        // 錯誤做法:記錄敏感信息
        // logger.info("支付信息: {}", payment);

        // 正確做法:脫敏處理
        logger.info("支付信息: ID={}, 金額={}, 卡號={}", 
                   payment.getId(), 
                   payment.getAmount(),
                   maskCardNumber(payment.getCardNumber()));
    }

    /**
     * 卡號脫敏
     */
    private String maskCardNumber(String cardNumber) {
        if (cardNumber == null || cardNumber.length() < 16) {
            return "****";
        }
        return cardNumber.substring(0, 4) + "****" + cardNumber.substring(12);
    }
}

八、日志分析工具

8.1 常用日志分析工具

/**
 * 日志分析工具推薦
 */
public class LogAnalysisTools {

    /**
     * 1. ELK Stack
     * - Elasticsearch: 日志存儲和搜索
     * - Logstash: 日志收集和處理
     * - Kibana: 日志可視化
     */

    /**
     * 2. Splunk
     * - 商業(yè)日志分析平臺
     * - 強大的搜索和分析功能
     */

    /**
     * 3. Graylog
     * - 開源日志管理平臺
     * - 基于Elasticsearch和MongoDB
     */

    /**
     * 4. Loki
     * - Grafana出品
     * - 輕量級日志聚合系統(tǒng)
     */

    /**
     * 5. Sentry
     * - 錯誤監(jiān)控和日志收集
     * - 支持實時告警
     */
}

8.2 日志查詢示例

# 使用grep查詢?nèi)罩?
grep "ERROR" app.log
grep "用戶創(chuàng)建成功" app.log

# 查找特定時間段的日志
grep "2024-01-01" app.log

# 查找包含特定關(guān)鍵詞的日志
grep -E "(ERROR|WARN)" app.log

# 統(tǒng)計錯誤數(shù)量
grep "ERROR" app.log | wc -l

# 查找特定用戶的日志
grep "userId=123456" app.log

九、Spring Boot 集成

9.1 配置文件配置

# application.yml
logging:
  config: classpath:logback-spring.xml
  level:
    root: INFO
    com.example: DEBUG
    org.springframework: INFO
    org.hibernate.SQL: DEBUG
    org.hibernate.type.descriptor.sql.BasicBinder: TRACE
  file:
    name: logs/app.log
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
    file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"

9.2 不同環(huán)境配置

# application-dev.yml(開發(fā)環(huán)境)
logging:
  level:
    root: DEBUG
    com.example: DEBUG
# application-test.yml(測試環(huán)境)
logging:
  level:
    root: INFO
    com.example: INFO
# application-prod.yml(生產(chǎn)環(huán)境)
logging:
  level:
    root: WARN
    com.example: INFO
  file:
    name: /var/log/myapp/app.log

十、總結(jié)

10.1 最佳實踐清單

配置管理:

  • 使用日志框架(Logback/Log4j2)
  • 配置合理的日志格式
  • 配置日志文件滾動策略
  • 配置不同環(huán)境的日志級別

代碼使用:

  • 使用占位符而不是字符串拼接
  • 合理使用日志級別
  • 異常日志包含關(guān)鍵信息
  • 使用MDC記錄請求上下文

性能優(yōu)化:

  • 使用異步日志
  • 判斷日志級別后再記錄
  • 避免在循環(huán)中打印日志
  • 避免記錄敏感信息

日志管理:

  • 使用日志分析工具
  • 定期清理舊日志
  • 配置日志告警
  • 建立日志規(guī)范

10.2 避坑指南

常見錯誤:

  • ? 使用 System.out.println
  • ? 字符串拼接日志消息
  • ? 所有日志都用INFO級別
  • ? 異常日志不包含堆棧
  • ? 在循環(huán)中打印大量日志
  • ? 記錄敏感信息

正確做法:

  • ? 使用日志框架
  • ? 使用占位符{}
  • ? 合理使用日志級別
  • ? 異常日志包含堆棧和關(guān)鍵信息
  • ? 批量記錄或記錄摘要
  • ? 脫敏處理敏感信息

記住:日志是系統(tǒng)的重要組成部分,良好的日志記錄能夠幫助你快速定位問題、分析系統(tǒng)狀態(tài)。選擇合適的日志框架,合理配置日志級別和格式,遵循最佳實踐,讓你的日志更有價值!

以上就是系統(tǒng)講解Java如何優(yōu)雅地打印日志的詳細內(nèi)容,更多關(guān)于Java打印日志的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java里得到00:00:00格式的時分秒的Timestamp

    Java里得到00:00:00格式的時分秒的Timestamp

    Java里如何得到00:00:00格式的時分秒的Timestamp ,下面是具體的實現(xiàn)代碼,需要的朋友可以參考下。
    2009-09-09
  • SpringBoot用配置影響B(tài)ean加載@ConditionalOnProperty

    SpringBoot用配置影響B(tài)ean加載@ConditionalOnProperty

    這篇文章主要為大家介紹了SpringBoot用配置影響B(tài)ean加載@ConditionalOnProperty示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04
  • java使用Jsoup連接網(wǎng)站超時的解決方法

    java使用Jsoup連接網(wǎng)站超時的解決方法

    jsoup是一個非常好的解析網(wǎng)頁的包,用java開發(fā)的,提供了類似DOM,CSS選擇器的方式來查找和提取文檔中的內(nèi)容,提取文檔內(nèi)容時會出現(xiàn)超時的情況,解決方法可看下文
    2013-11-11
  • 寶塔升級JDK版本超詳細圖文教程

    寶塔升級JDK版本超詳細圖文教程

    寶塔自動安裝的JDK是一種用于開發(fā)和運行Java程序的軟件開發(fā)工具包,下面這篇文章主要給大家介紹了關(guān)于寶塔升級JDK版本的相關(guān)資料,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2023-12-12
  • idea中如何集成http請求

    idea中如何集成http請求

    這篇文章主要介紹了idea中如何集成http請求問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • easyexcel封裝工具類以及簡單使用方式

    easyexcel封裝工具類以及簡單使用方式

    文章介紹了如何使用封裝工具類、控制器和實體類進行開發(fā),并通過Postman進行測試,作者分享了個人經(jīng)驗,希望能對大家有所幫助
    2026-02-02
  • CentOS安裝jdk的三種方法

    CentOS安裝jdk的三種方法

    這篇文章主要為大家詳細介紹了CentOS安裝jdk的三種方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • SpringBoot實現(xiàn)數(shù)據(jù)轉(zhuǎn)換的四種對象映射方案

    SpringBoot實現(xiàn)數(shù)據(jù)轉(zhuǎn)換的四種對象映射方案

    在項目開發(fā)中,對象之間的相互轉(zhuǎn)換是一個高頻操作,尤其在分層架構(gòu)的系統(tǒng)中,數(shù)據(jù)在實體對象(Entity)、數(shù)據(jù)傳輸對象(DTO)、值對象(VO)之間的轉(zhuǎn)換尤為常見,選擇一個高效、可靠的對象映射方案對提高系統(tǒng)性能和開發(fā)效率至關(guān)重要,本文將介紹4種對象映射方案
    2025-06-06
  • Java中final關(guān)鍵字和final的四種用法實例

    Java中final關(guān)鍵字和final的四種用法實例

    final關(guān)鍵字代表最終的、不可改變的,下面這篇文章主要給大家介紹了關(guān)于Java中final關(guān)鍵字和final的四種用法實例,文中通過圖文以及實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-02-02
  • MyBatis復(fù)雜Sql查詢實現(xiàn)示例介紹

    MyBatis復(fù)雜Sql查詢實現(xiàn)示例介紹

    在利用mybatis做查詢的時候,一般返回結(jié)果用resulttype,這種情況必須是查詢的結(jié)果在對應(yīng) 的pojo類中有對應(yīng)的,一般都是單表查詢,但是對于一些復(fù)雜的情況,比如需要用到多表查詢的時候,resultType不再適用,此時一般用resultMap來表示返回的結(jié)果
    2022-12-12

最新評論

廉江市| 新田县| 本溪市| 巴东县| 资源县| 阳高县| 莒南县| 贵德县| 湘潭县| 同仁县| 西丰县| 裕民县| 江达县| 尼勒克县| 河北省| 都兰县| 潼南县| 东阿县| 壶关县| 平南县| 通化县| 册亨县| 远安县| 柏乡县| 北碚区| 咸宁市| 伽师县| 碌曲县| 揭东县| 南康市| 紫云| 哈尔滨市| 澳门| 平罗县| 德庆县| 临夏县| 眉山市| 晋宁县| 天全县| 阆中市| 浏阳市|