java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java打印日志

系统讲解Java如何优雅地打印日志

作者:xiaoyu❅

本文将介绍如何优雅地打印日志,告别 System.out.println,使用专业的日志框架(Logback/Log4j2)来管理日志,让你的日志更专业、更规范、更易于排查问题

前言

“为什么生产环境的代码还在用 System.out.println?”、“日志格式混乱,没有时间戳,不知道是谁打印的”、“日志级别使用不当,错误信息混在普通信息中”。

这些都是开发中常见的日志问题。本文将介绍如何优雅地打印日志,告别 System.out.println,使用专业的日志框架(Logback/Log4j2)来管理日志,让你的日志更专业、更规范、更易于排查问题。

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

1.1 System.out.println 的问题

System.out.println 的主要问题:

1.性能问题

2.功能缺失

3.管理困难

1.2 日志框架的优势

日志框架的优势:

1.性能优化

2.功能丰富

3.管理便捷

二、日志级别

2.1 日志级别定义

/**
 * 日志级别从低到高
 * TRACE < DEBUG < INFO < WARN < ERROR
 */

// TRACE:追踪信息,最详细的日志信息
// 使用场景:调试时追踪程序执行流程
logger.trace("用户登录,用户ID: {}", userId);

// DEBUG:调试信息,开发调试时使用
// 使用场景:开发阶段,记录变量值、方法调用等
logger.debug("查询用户,用户ID: {}", userId);

// INFO:重要信息,记录关键业务流程
// 使用场景:记录重要操作、系统状态等
logger.info("用户创建成功,用户ID: {}", userId);

// WARN:警告信息,潜在的问题
// 使用场景:参数校验失败、配置警告等
logger.warn("用户余额不足,用户ID: {}, 余额: {}", userId, balance);

// ERROR:错误信息,需要立即处理
// 使用场景:异常捕获、系统错误等
logger.error("订单创建失败,用户ID: {}", userId, exception);

2.2 日志级别使用原则

使用建议:

级别使用场景生产环境
TRACE追踪详细执行流程关闭
DEBUG调试信息、变量值关闭
INFO重要操作、业务流程开启
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 基础配置

<?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>
    <!-- 开发环境 -->
    <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>
    <!-- 测试环境 -->
    <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>
    <!-- 生产环境 -->
    <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 基础配置

<?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"/>
        <!-- 应用日志级别 -->
        <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);

    /**
     * 创建用户
     */
    public void createUser(User user) {
        // TRACE:追踪信息
        logger.trace("开始创建用户,用户名: {}", user.getUsername());

        try {
            // DEBUG:调试信息
            logger.debug("用户信息: {}", user);

            // 业务逻辑
            saveUser(user);

            // INFO:重要信息
            logger.info("用户创建成功,用户ID: {}", user.getId());

        } catch (Exception e) {
            // ERROR:错误信息(包含异常堆栈)
            logger.error("用户创建失败,用户名: {}", user.getUsername(), e);
        }
    }

    /**
     * 参数校验失败
     */
    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);

    /**
     * 创建订单
     */
    public void createOrder(Order order) {
        // 使用占位符{},避免字符串拼接
        // 错误做法:logger.info("订单创建成功,订单ID: " + order.getId());
        // 正确做法:
        logger.info("订单创建成功,订单ID: {}", order.getId());

        // 多个占位符
        logger.info("订单信息: ID={}, 用户ID={}, 金额={}", 
                   order.getId(), order.getUserId(), order.getAmount());

        // 占位符数量不匹配时,会使用toString()
        logger.info("订单创建: {}", order);
    }
}

5.3 异常日志

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

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

    /**
     * 支付处理
     */
    public void processPayment(Payment payment) {
        try {
            // 业务逻辑
            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) {
            // 未预期的异常
            logger.error("系统异常,订单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);

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

        logger.info("请求上下文已设置");
    }

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

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

            // 日志会自动包含MDC信息
            logger.info("处理请求");

            // 业务逻辑
            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         // 调用者信息
%line           // 行号
%file           // 文件名
%M              // 方法名

/**
 * Log4j2 格式化符号
 */
%d              // 日期和时间
%p / %-5p       // 日志级别
%t              // 线程名称
%c / %-36c      // Logger名称
%m              // 日志消息
%n              // 换行符
%ex             // 异常堆栈
%F              // 文件名
%L              // 行号
%M              // 方法名
%r              // 毫秒数

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("开始创建订单");  // 追踪信息
        logger.debug("订单详情: {}", order);  // 调试信息

        // 重要操作
        logger.info("订单创建成功,订单ID: {}, 用户ID: {}", 
                   order.getId(), order.getUserId());
    }

    /**
     * 最佳实践2:使用占位符而不是字符串拼接
     */
    public void updateOrder(Order order) {
        // 错误做法
        // logger.info("订单更新: ID=" + order.getId() + ", 状态=" + order.getStatus());

        // 正确做法
        logger.info("订单更新: ID={}, 状态={}", order.getId(), order.getStatus());
    }

    /**
     * 最佳实践3:异常日志包含关键信息
     */
    public void processOrder(Long orderId) {
        try {
            process(orderId);
        } catch (OrderNotFoundException e) {
            // 包含关键业务信息
            logger.error("订单不存在,订单ID: {}", orderId, e);
        } catch (Exception e) {
            // 包含异常堆栈
            logger.error("订单处理失败,订单ID: {}", orderId, e);
        }
    }

    /**
     * 最佳实践4:避免在循环中打印日志
     */
    public void processBatch(List<Long> orderIds) {
        // 错误做法:在循环中打印日志
        // for (Long orderId : orderIds) {
        //     logger.info("处理订单: {}", orderId);
        // }

        // 正确做法:批量打印
        logger.info("批量处理订单,数量: {}, 订单ID列表: {}", 
                   orderIds.size(), orderIds);

        // 或者只打印摘要
        logger.info("批量处理订单,数量: {}", orderIds.size());
    }

    /**
     * 最佳实践5:使用MDC记录请求上下文
     */
    public void handleRequest(String traceId, Long userId) {
        MDC.put("traceId", traceId);
        MDC.put("userId", String.valueOf(userId));

        try {
            logger.info("开始处理请求");
            // 业务逻辑
        } finally {
            MDC.clear();
        }
    }
}

7.2 性能优化建议

/**
 * 日志性能优化
 */
public class PerformanceOptimization {

    /**
     * 优化1:判断日志级别
     */
    public void expensiveOperation() {
        // 错误做法:即使日志级别不满足,也会执行字符串拼接
        // logger.debug("用户列表: " + getUsers());

        // 正确做法:先判断日志级别
        if (logger.isDebugEnabled()) {
            logger.debug("用户列表: {}", getUsers());
        }
    }

    /**
     * 优化2:使用异步日志
     */
    // 在配置文件中配置异步Appender

    /**
     * 优化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
     * - 商业日志分析平台
     * - 强大的搜索和分析功能
     */

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

    /**
     * 4. Loki
     * - Grafana出品
     * - 轻量级日志聚合系统
     */

    /**
     * 5. Sentry
     * - 错误监控和日志收集
     * - 支持实时告警
     */
}

8.2 日志查询示例

# 使用grep查询日志
grep "ERROR" app.log
grep "用户创建成功" app.log

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

# 查找包含特定关键词的日志
grep -E "(ERROR|WARN)" app.log

# 统计错误数量
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 不同环境配置

# application-dev.yml(开发环境)
logging:
  level:
    root: DEBUG
    com.example: DEBUG
# application-test.yml(测试环境)
logging:
  level:
    root: INFO
    com.example: INFO
# application-prod.yml(生产环境)
logging:
  level:
    root: WARN
    com.example: INFO
  file:
    name: /var/log/myapp/app.log

十、总结

10.1 最佳实践清单

配置管理:

代码使用:

性能优化:

日志管理:

10.2 避坑指南

常见错误:

正确做法:

记住:日志是系统的重要组成部分,良好的日志记录能够帮助你快速定位问题、分析系统状态。选择合适的日志框架,合理配置日志级别和格式,遵循最佳实践,让你的日志更有价值!

以上就是系统讲解Java如何优雅地打印日志的详细内容,更多关于Java打印日志的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文