Java日志框架案例

wen java案例 2

本文目录导读:

Java日志框架案例

  1. 常用日志框架对比
  2. 基础使用案例
  3. Logback配置案例
  4. Log4j2配置案例
  5. 高级应用案例
  6. 最佳实践总结
  7. 日志框架性能对比
  8. 常见问题解决

我将为您提供一个全面的Java日志框架案例,涵盖主流日志框架的使用和最佳实践。

常用日志框架对比

<!-- pom.xml 依赖配置 -->
<dependencies>
    <!-- SLF4J API - 日志门面 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.36</version>
    </dependency>
    <!-- Logback - 日志实现 -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.11</version>
    </dependency>
    <!-- Log4j2 - 替代Log4j 1.x -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.20.0</version>
    </dependency>
    <!-- 兼容旧版log4j -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>log4j-over-slf4j</artifactId>
        <version>1.7.36</version>
    </dependency>
    <!-- Lombok的@Slf4j注解(可选) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.30</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

基础使用案例

1 标准SLF4J使用

package com.example.logging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 日志使用基础案例
 */
public class BasicLoggingExample {
    // 推荐使用private static final
    private static final Logger logger = LoggerFactory.getLogger(BasicLoggingExample.class);
    public void demonstrateLogLevels() {
        // 不同级别的日志
        logger.trace("这是TRACE级别日志");
        logger.debug("这是DEBUG级别日志,用户ID: {}", 1001);
        logger.info("这是INFO级别日志");
        logger.warn("这是WARN级别日志");
        logger.error("这是ERROR级别日志", new RuntimeException("示例异常"));
    }
    public void demonstrateParameterizedLogging() {
        String username = "张三";
        int age = 25;
        // 使用占位符,避免字符串拼接
        logger.debug("用户{},年龄{},正在登录系统", username, age);
        // 只有debug级别启用时才执行方法
        if (logger.isDebugEnabled()) {
            logger.debug("复杂的调试信息: " + expensiveMethod());
        }
    }
    private String expensiveMethod() {
        // 模拟昂贵操作
        return "计算结果";
    }
    // 使用Lombok简化
    // @Slf4j
    public static void main(String[] args) {
        BasicLoggingExample example = new BasicLoggingExample();
        example.demonstrateLogLevels();
        example.demonstrateParameterizedLogging();
    }
}

2 MDC(Mapped Diagnostic Context)使用

package com.example.logging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.util.UUID;
/**
 * MDC用于跟踪请求上下文
 */
public class MDCExample {
    private static final Logger logger = LoggerFactory.getLogger(MDCExample.class);
    public void processRequest(String userId) {
        // 在请求开始时设置MDC
        MDC.put("requestId", UUID.randomUUID().toString());
        MDC.put("userId", userId);
        try {
            logger.info("开始处理用户请求");
            // 业务逻辑
            doBusinessLogic();
            logger.info("请求处理完成");
        } catch (Exception e) {
            logger.error("处理请求失败", e);
        } finally {
            // 确保清理MDC,避免内存泄漏
            MDC.clear();
        }
    }
    private void doBusinessLogic() {
        logger.debug("执行业务逻辑,当前用户: {}", MDC.get("userId"));
    }
}

Logback配置案例

1 logback.xml 完整配置

<?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>%d{HH:mm:ss.SSS} %highlight(%-5level) %cyan(%logger{36}) - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!-- 滚动文件 - 按大小 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/application.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/archive/application-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxHistory>30</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>50MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!-- 错误日志单独文件 -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/error.log</file>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/archive/error-%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>90</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!-- 异步输出 -->
    <appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
        <queueSize>512</queueSize>
        <discardingThreshold>0</discardingThreshold>
        <appender-ref ref="FILE"/>
    </appender>
    <!-- 不同包的日志级别设置 -->
    <logger name="org.springframework" level="WARN"/>
    <logger name="com.example" level="DEBUG"/>
    <!-- 根日志级别 -->
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="ASYNC_FILE"/>
        <appender-ref ref="ERROR_FILE"/>
    </root>
</configuration>

2 动态配置示例

package com.example.logging;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import org.slf4j.LoggerFactory;
public class DynamicLogLevelExample {
    public void changeLogLevel(String packageName, String level) {
        Object obj = LoggerFactory.getILoggerFactory();
        if (obj instanceof LoggerContext) {
            LoggerContext context = (LoggerContext) obj;
            ch.qos.logback.classic.Logger logger = context.getLogger(packageName);
            // 动态修改日志级别
            logger.setLevel(Level.toLevel(level));
        }
    }
}

Log4j2配置案例

1 log4j2.xml

<?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="%d{HH:mm:ss.SSS} %highlight{%-5level} %style{%logger{36}}{cyan} - %msg%n"/>
        </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/>
                <SizeBasedTriggeringPolicy size="50MB"/>
            </Policies>
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>
        <!-- 异步文件 -->
        <Async name="AsyncFile">
            <AppenderRef ref="RollingFile"/>
            <BufferSize>1024</BufferSize>
        </Async>
    </Appenders>
    <Loggers>
        <Logger name="org.springframework" level="WARN"/>
        <Logger name="com.example" level="DEBUG"/>
        <Root level="INFO">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="AsyncFile"/>
        </Root>
    </Loggers>
</Configuration>

高级应用案例

1 日志分类使用

package com.example.logging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 业务日志案例
 */
public class BusinessLogExample {
    private static final Logger BUSINESS_LOGGER = LoggerFactory.getLogger("business");
    private static final Logger SECURITY_LOGGER = LoggerFactory.getLogger("security");
    private static final Logger OPERATION_LOGGER = LoggerFactory.getLogger("operation");
    public void businessOperation(String orderId, String userId) {
        // 业务日志
        BUSINESS_LOGGER.info("用户{}创建订单:{}", userId, orderId);
        // 安全审计日志
        SECURITY_LOGGER.info("用户操作:{} 执行创建订单操作,订单号:{}", userId, orderId);
        // 操作日志
        OPERATION_LOGGER.info("订单创建成功 | 操作人:{} | 订单号:{} | 时间:{}", 
            userId, orderId, System.currentTimeMillis());
    }
    public void throwException() {
        try {
            int result = 10 / 0;
        } catch (Exception e) {
            // 记录异常时的标准做法
            logger.error("计算异常,参数: a={}, b={}", 10, 0, e);
        }
    }
    private static final Logger logger = LoggerFactory.getLogger(BusinessLogExample.class);
}

2 日志埋点与性能监控

package com.example.logging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PerformanceLogExample {
    private static final Logger logger = LoggerFactory.getLogger(PerformanceLogExample.class);
    public void logWithTime() {
        long start = System.currentTimeMillis();
        try {
            // 模拟业务逻辑
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            long cost = System.currentTimeMillis() - start;
            // 性能日志,超过阈值告警
            if (cost > 50) {
                logger.warn("方法执行耗时过长: {} ms", cost);
            } else {
                logger.debug("方法执行耗时: {} ms", cost);
            }
        }
    }
    // 使用AOP实现方法性能监控
    // @Around("execution(* com.example..*.*(..))")
    // public Object logPerformance(ProceedingJoinPoint pjp) throws Throwable {
    //     long start = System.currentTimeMillis();
    //     Object result = pjp.proceed();
    //     long cost = System.currentTimeMillis() - start;
    //     logger.info("方法 {} 执行耗时: {}ms", pjp.getSignature(), cost);
    //     return result;
    // }
}

3 Spring Boot配置

# application.yml
logging:
  level:
    root: INFO
    com.example: DEBUG
    org.springframework: WARN
  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"
  file:
    name: ./logs/spring-boot-application.log
    max-size: 10MB
    max-history: 30

4 审计日志

package com.example.logging;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class AuditLogService {
    private static final Logger auditLogger = LoggerFactory.getLogger("AUDIT");
    private final ObjectMapper objectMapper = new ObjectMapper();
    public void logAction(String user, String action, Object data) {
        AuditLog auditLog = new AuditLog();
        auditLog.setUserId(user);
        auditLog.setAction(action);
        auditLog.setTimestamp(System.currentTimeMillis());
        try {
            String json = objectMapper.writeValueAsString(data);
            auditLog.setData(json);
        } catch (Exception e) {
            auditLog.setData("序列化失败");
        }
        // 写入审计日志
        auditLogger.info("AUDIT - {}", toJsonString(auditLog));
    }
    private String toJsonString(Object obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (Exception e) {
            return obj.toString();
        }
    }
    static class AuditLog {
        private String userId;
        private String action;
        private long timestamp;
        private String data;
        // getters and setters...
        public String getUserId() { return userId; }
        public void setUserId(String userId) { this.userId = userId; }
        public String getAction() { return action; }
        public void setAction(String action) { this.action = action; }
        public long getTimestamp() { return timestamp; }
        public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
        public String getData() { return data; }
        public void setData(String data) { this.data = data; }
    }
}

最佳实践总结

package com.example.logging;
/**
 * 日志最佳实践总结
 */
public class LoggingBestPractices {
    /**
     * 1. 使用SLF4J门面而不是直接使用具体实现
     * 2. 使用参数化日志,避免字符串拼接
     * 3. 正确处理异常日志
     * 4. 合理设置日志级别
     * 5. 清理MDC上下文
     */
    private static final org.slf4j.Logger logger = 
        org.slf4j.LoggerFactory.getLogger(LoggingBestPractices.class);
    public void rules() {
        // 1. 使用占位符而不是字符串拼接
        String name = "test";
        int id = 100;
        logger.debug("用户: {}, ID: {}", name, id);  // 正确
        logger.debug("用户: " + name + ", ID: " + id);  // 错误
        // 2. 完整异常信息
        try {
            // 业务代码
        } catch (Exception e) {
            logger.error("业务操作失败", e);  // 正确 - 打印完整堆栈
            // logger.error("业务操作失败: {}", e.getMessage());  // 错误 - 丢失堆栈
        }
        // 3. 避免记录敏感信息
        // 不要: logger.info("用户密码: {}", password);
        // 4. 使用debug级别记录调试信息
        logger.debug("进入方法,参数: {}", requestParams);
        // 5. 日志级别选择
        // TRACE: 详细调试信息
        // DEBUG: 调试信息
        // INFO: 关键业务信息
        // WARN: 可恢复的问题
        // ERROR: 异常错误
    }
}

日志框架性能对比

框架 特性 同步性能 异步性能 适用场景
Logback 实现简单,功能完善 中小型项目
Log4j2 强大的过滤和路由 大型系统
Java Util Logging JDK自带 简单应用

常见问题解决

// 1. 避免日志框架冲突
// 排除冲突依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
// 2. 循环依赖问题
// 在logback.xml中配置
<configuration>
    <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
</configuration>

这个案例涵盖了Java日志框架的主要使用场景和实践技巧,根据项目需求选择合适的技术方案,并遵循最佳实践来保证日志系统的健壮性和可用性。

上一篇JMH案例

下一篇Java性能基准案例

抱歉,评论功能暂时关闭!