Java日志打印的6个高质量方法:反例 + 正例,告别无效日志

AI 概述
Java程序日志是排查问题关键,建议用 SLF4J + Logback/Log4j2 框架。高质量日志打印要点有:日志要包含上下文、参数、异常信息;按场景合理使用日志级别;异常必须打印堆栈且避免重复打印;用户手机号、身份证等敏感信息需脱敏后再打印;用占位符“{}”代替“+”拼接字符串,避免性能损耗;使用 MDC 实现链路追踪,通过配置 logback.xml 输出 traceId,实现多个服务调用全链路追踪。
目录
文章目录隐藏
  1. 前提
  2. 1.参数完整
  3. 2.日志级别使用不当
  4. 3.异常必须打印堆栈
  5. 4.敏感信息泄露
  6. 5.占位符的正确打开方式
  7. 6.MDC 链路追踪

Java 日志打印的 6 个高质量方法:反例 + 正例,告别无效日志

在 Java 程序开发中,日志堪称排查问题的“生命线”,是定位异常、分析性能瓶颈的关键依据。然而,不少开发者在日志记录上存在误区,日志内容冗余、缺乏关键信息,导致问题排查时犹如大海捞针,难以精准定位。今天,我们就从专业视角深入探讨几个 Java 高质量日志打印方法,涵盖日志级别合理运用、关键业务数据记录、异常堆栈完整捕获等核心要点,助你提升日志质量,让日志真正成为解决问题的得力助手,快速高效地排查 Java 程序中的各类问题。

前提

建议使用统一的日志框架 SLF4J + Logback/Log4j2

// 正确引入(类顶部)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class OrderService {
    // 静态常量,避免每次创建 Logger 实例
    private static final Logger log = LoggerFactory.getLogger(OrderService.class);
}

1.参数完整

  • 反例:只说 “失败了”,要说清 “为什么失败”
    log.info("用户登录失败");
    

哪个用户失败了?在何时?失败原因是什么?

  • 核心原则:日志必须包含「上下文 + 参数 + 异常信息」
  • 正例:
    log.warn("用户登录失败 name={}, IP={}, time={}, failReason={}", 
        username, clientIP, new Date(),"密码错误");
    

2.日志级别使用不当

  • 反例:级别滥用
    // 反例 1:调试信息用 INFO,生产环境日志刷屏
    log.info("订单查询参数:orderId={}", 123456);
    
    // 反例 2:致命异常用 WARN,易被忽略
    log.warn("数据库连接失败,无法创建订单");
    
    // 反例 3:ERROR 打印非异常信息(如正常流程)
    log.error("订单创建成功,orderId={}", 123456);
    
  • 核心原则:日志级别按 “从细到粗” 分为 TRACE < DEBUG < INFO < WARN < ERROR。
  • 级别定义表:
    级别 适用场景
    TRACE 极细粒度的调试信息(如框架内部流程)
    DEBUG 开发/测试环境的调试信息(如参数、执行步骤)
    INFO 生产环境核心流程(如服务启动、订单创建成功)
    WARN 非致命异常(如参数不合法、资源不足)
    ERROR 致命异常(如调用第三方接口失败、数据库连接异常)
  • 正例:
    // DEBUG:开发环境调试,生产可关闭
    log.debug("订单查询参数:orderId={}", 123456);
    
    // INFO:核心业务流程,生产必打
    log.info("订单创建成功,orderId={},用户 ID={}", 123456, 789);
    
    // WARN:非致命问题,需关注但不阻断流程
    log.warn("订单参数不合法:userId={},原因:{}", 789, "用户 ID 为空");
    
    // ERROR:致命异常,必须包含异常栈+上下文
    try {
        createOrder();
    } catch (SQLException e) {
        log.error("创建订单失败,orderId={}", 123456, e);
    }
    

3.异常必须打印堆栈

  • 反例:
    // 反例 1:仅打印异常信息,丢失栈
    try {
        orderDao.insert(order);
    } catch (SQLException e) {
        log.error("插入订单失败:{}", e.getMessage());
    }
    
    // 反例 2:重复打印异常栈
    try {
        orderDao.insert(order);
    } catch (SQLException e) {
        log.error("插入订单失败", e);
        throw new BusinessException("插入失败", e); // 上层又打印一次,日志冗余
    }
    
  • 核心原则:异常日志必须传递 Throwable 对象
  • 正例:
    // 正例 1:打印完整异常栈
    try {
        orderDao.insert(order);
    } catch (SQLException e) {
        log.error("插入订单失败,订单 ID={}", order.getId(), e);
        // 若需要抛上层,无需重复打印,上层统一处理
        thrownew BusinessException("插入订单失败", e);
    }
    
    // 正例 2:吞异常时必须打日志
    try {
        thirdService.sendMsg(userId, msg);
    } catch (Exception e) {
        // 允许消息发送失败,但必须记录日志
        log.warn("消息发送失败,用户 ID={},消息内容={}", userId, msg, e);
    }
    

4.敏感信息泄露

打印日志泄露用户手机号有可能被投诉

  • 反例:
    // 反例 1:打印完整手机号/身份证
    log.info("用户登录,手机号={},身份证={}", "13812345678", "110101199001011234");
    
    // 反例 2:打印密码/令牌
    log.debug("调用第三方接口,token={},密码={}", "admin_edfojofwog", "123456");
    
  • 核心原则:用户手机号、身份证、密码、银行卡号等敏感信息,脱敏后再打印
  • 正例
    // 工具方法:手机号脱敏(138****5678)
    private static String maskPhone(String phone) {
        if (phone == null || phone.length() != 11) {
            return phone;
        }
        return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
    }
    
    // 工具方法:身份证脱敏(110101********1234)
    private static String maskIdCard(String idCard) {
        if (idCard == null || idCard.length() != 18) {
            return idCard;
        }
        return idCard.replaceAll("(\\d{6})\\d{8}(\\d{4})", "$1********$2");
    }
    
    // 正例:打印脱敏后的信息
    log.info("用户登录,手机号={},身份证={}", maskPhone("13812345678"), maskIdCard("110101199001011234"));
    log.debug("调用第三方接口,token={}", "******"); 
    

5.占位符的正确打开方式

  • 反例:
    log.info("User:" + user.getId() + " username " + user.getName()); 
    
  • 核心原则:别用 “+”,避免性能损耗
  • 正例:
    log.info("User:{} username {}", user.getId(), user.getName());
    

6.MDC 链路追踪

MDC.put("traceId", UUID.randomUUID().toString().substring(0,8));

//logback.xml 配置
<pattern>%d{yyyy-MM-dd} [%X{traceId}] %-5level %logger{36} - %msg%n</pattern>
  • 核心原则:多个服务调用可以通过 traceId 全联路追踪
  • 输出结果
    2026-01-13 [e44d4gae-6kk4-3er4-rr4t-t0ykyreg] INFO com.example.OrderService - 下单成功

以上关于Java日志打印的6个高质量方法:反例 + 正例,告别无效日志的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。

「点点赞赏,手留余香」

0

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » Java日志打印的6个高质量方法:反例 + 正例,告别无效日志

发表回复