java中记录日志方式比较及相关建议

本文对比了Java中记录日志的多种方法,强烈推荐使用log.error结合异常栈信息的方式,避免使用e.printStackTrace(),因为它不会记录到日志文件,可能导致内存占用过高和日志混乱。同时,提供了升级logback到1.2.10和log4j2到2.17.0的简单步骤,以增强日志管理和安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、记录日志的几种方式比较

为了测试,我在测试类中写了七种打印方式,分别如下:

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class ErrorLogTest {
    @Test
    public void ss(){
        try{
            float xx= 1/0;
            log.info("xx:{}",xx);
        }catch (Exception e){
            log.info("======================第一种打印LogExceptionStackUtil.logExceptionStack(e)======================");
            log.error("异常:{}",LogExceptionStackUtil.logExceptionStack(e));
            log.info("======================第二种打印e======================");
            log.error("异常:{}",e);
            log.info("======================第三种打印e.printStackTrace()======================");
            e.printStackTrace();
            log.info("======================第四种打印e.getMessage()======================");
            log.error("异常:{}",e.getMessage());
            log.info("======================第五种打印e.getStackTrace()======================");
            log.error("异常:{}",e.getStackTrace());
            log.info("======================第六种打印e.toString()======================");
            log.error("异常:{}",e.toString());
            log.info("======================第七种打印System.out.println======================");
            System.out.println("异常:{}"+e);
        }
    }
 }

我在工程中使用的是logback框架进行日志记录。
注意:一定要让spring容器启动起来,要不然你会苦恼得发现日志文件没生成(@RunWith和@SpringBootTest注解不要删)。

运行起来后,先给大家总结下效果吧:

方式效果
log.error(“异常:{}”,LogExceptionStackUtil.logExceptionStack(e));会打印完整日志,且打印的报错行和具体日志的开始在同一行
log.error(“异常:{}”,e);会打印完整日志,但打印的报错行和具体日志的开始不在同一行,如果用某些特殊搜索,会遗漏完整日志
e.printStackTrace();只在控制台打印了,日志文件中没有记录
log.error(“异常:{}”,e.getMessage());没有异常类型,只有异常的简单信息
log.error(“异常:{}”,e.getStackTrace());没有异常类型,只会打印从哪一行抛出的
log.error(“异常:{}”,e.toString());包括异常类型和异常的简单信息
System.out.println(“异常:{}”+e);只在控制台打印了(包括异常类型和异常的简单信息),日志文件中没有记录

日志文件节选(此处吐槽优快云不能让用户选择折叠和展开):

2022-02-21 17:13:07.392 [main] INFO  com.my.demojava.controller.ErrorLogTest.ss line:31  - ======================第一种打印LogExceptionStackUtil.logExceptionStack(e)======================
2022-02-21 17:13:07.393 [main] ERROR com.my.demojava.controller.ErrorLogTest.ss line:32  - 异常:java.lang.ArithmeticException: / by zero
	at com.my.demojava.controller.ErrorLogTest.ss(ErrorLogTest.java:28)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    此处略去30行
2022-02-21 17:13:07.394 [main] INFO  com.my.demojava.controller.ErrorLogTest.ss line:33  - ======================第二种打印e======================
2022-02-21 17:13:07.397 [main] ERROR com.my.demojava.controller.ErrorLogTest.ss line:34  - 异常:{}
java.lang.ArithmeticException: / by zero
	at com.my.demojava.controller.ErrorLogTest.ss(ErrorLogTest.java:28)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	此处略去30行
2022-02-21 17:13:07.397 [main] INFO  com.my.demojava.controller.ErrorLogTest.ss line:35  - ======================第三种打印e.printStackTrace()======================
2022-02-21 17:13:07.399 [main] INFO  com.my.demojava.controller.ErrorLogTest.ss line:37  - ======================第四种打印e.getMessage()======================
2022-02-21 17:13:07.399 [main] ERROR com.my.demojava.controller.ErrorLogTest.ss line:38  - 异常:/ by zero
2022-02-21 17:13:07.399 [main] INFO  com.my.demojava.controller.ErrorLogTest.ss line:39  - ======================第五种打印e.getStackTrace()======================
2022-02-21 17:13:07.399 [main] ERROR com.my.demojava.controller.ErrorLogTest.ss line:40  - 异常:com.my.demojava.controller.ErrorLogTest.ss(ErrorLogTest.java:28)
2022-02-21 17:13:07.400 [main] INFO  com.my.demojava.controller.ErrorLogTest.ss line:41  - ======================第六种打印e.toString()======================
2022-02-21 17:13:07.400 [main] ERROR com.my.demojava.controller.ErrorLogTest.ss line:42  - 异常:java.lang.ArithmeticException: / by zero
2022-02-21 17:13:07.400 [main] INFO  com.my.demojava.controller.ErrorLogTest.ss line:43  - ======================第七种打印System.out.println======================

总结:最推荐的做法是第一种打印方式:log.error(“异常:{}”,LogExceptionStackUtil.logExceptionStack(e));

public class LogExceptionStackUtil {
    public static String logExceptionStack(Throwable e) {
        StringWriter errorsWriter = new StringWriter();
        e.printStackTrace(new PrintWriter(errorsWriter));
        return errorsWriter.toString();
    }
}

或者使用最常用的第二种打印方式:log.error(“异常:{}”,e);
如果你不想略去详细信息,请不要用第四、第五、第六种;
最最最不应该使用的是第三种e.printStackTrace(),因为它压根不会在日志文件里打印,且容易堆栈溢出,造成第二节的严重后果,见下。

二、e.printStackTrace()为什么不要用

1)不会打印到日志文件中,生产环境排查问题的噩梦

只会打印在控制台,如果生产环境有问题,莫非要连上人家的环境启动项目?或者临时改代码?(说多了都是泪)

2)占用内存,严重时造成系统卡顿或挂掉

具体原因如下流程:

短时间内大量请求访问此接口 -> 
代码本身有问题,很多情况下抛异常  -> 
e.printStackTrace() 来打印异常到控制台 -> 
产生错误堆栈字符串到字符串池内存空间 -> 
此内存空间一下子被占满了 -> 
开始在此内存空间产出字符串的线程还没完全生产完整,就没空间了 ->  
大量线程产出字符串产出到一半,等在这儿-> 
相互等待 ->
等内存->
锁死了->
整个应用挂掉、访问没响应

参考文章:java中e.printStackTrace()不要使用,请使用logger记录

3)日志交错混合,不易读

printStackTrace()默认使用了System.err输出流进行输出,与System.out是两个不同的输出流,那么在打印时自然就形成了交叉。再就是输出流是有缓冲区的,所以对于什么时候具体输出也形成了随机。

三、升级logback或log4j2版本的简易方法

1)升级logback版本到1.2.10

在pom.xml中,< properties >标签中加入配置:
<logback.version>1.2.10</logback.version>

2)升级log4j2版本到2.17.0

在pom.xml中,< properties >标签中加入配置:
<log4j2.version>2.17.0</log4j2.version>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值