what? e.getMessage()居然为null?

what? e.getMessage()居然为null?

最近在项目开发中遇到一个问题: 业务异常的时候记录日志log.error("异常:{}", e.getMessage()), 结果日志打印出来显示居然为null? 这到底是怎么回事呢? 大体研究了一下, 整理一下过程如下:

Throwable

e.getMessage()方法来自于顶级父类Throwable, 让我们先来看一下

public String getMessage() {
    return detailMessage;
}

该方法直接返回的是成员变量detailMessage, 而detailMessage则是通过构造函数传入的, 如下面的这个构造函数

public Throwable(String message) {
    fillInStackTrace();
    detailMessage = message;
}

大名鼎鼎的NullPointException

NullPointException有两个构造器, 一个传了message, 一个没传

public NullPointerException() {
    super();
}

public NullPointerException(String s) {
    super(s);
}

我们平时遇到最多的空指针是啥情况呢?

写一个最简单的例子

public static void main(String[] args) {
    Order order = null;
    System.out.println(order.getId());
}

在NullPointException类构造器加断点, 好吧, 确实是进了无参的构造器

正确的打印日志的方法

当我们使用log.error("e:{}", e.getMessage())这样的语句来打印日志的情况下, 好多根本捕获不到信息, 怎么办呢?

看一下org.slf4j.Logger, 我们平时用到的Slf4j记录日志的这个类(用lombok的@Slf4j注解也是这个类)

/**
     * Log an exception (throwable) at the ERROR level with an
     * accompanying message.
     *
     * @param msg the message accompanying the exception
     * @param t   the exception (throwable) to log
     */
public void error(String msg, Throwable t);

你会发现, 有的能直接接收异常实例当参数唉? 注释上写着the exception to log(要记录的异常), 那他内部怎么实现的呢? 直接传整个实例能打印出日志吗?

找一个Logback的实现类ch.qos.logback.classic.Logger看一下, 一层层的点方法, 最终到了这

SyslogOutputStream sos;
.....
@Override
protected void append(E eventObject) {
    if (!isStarted()) {
        return;
    }

    try {
        // 这里doLayout方法用于展示不同的形式, 输出的是Event对象
        String msg = layout.doLayout(eventObject);
        if (msg == null) {
            return;
        }
        if (msg.length() > maxMessageSize) {
            msg = msg.substring(0, maxMessageSize);
        }
        sos.write(msg.getBytes(charset));
        sos.flush();
        postProcess(eventObject, sos);
    } catch (IOException ioe) {
        addError("Failed to send diagram to " + syslogHost, ioe);
    }
}

最终把Event输出了, 这个Event就是对日志记录参数的封装, 我们再看看Event是怎么构造的

public LoggingEvent(String fqcn, Logger logger, Level level, String message, Throwable throwable, Object[] argArray) {
    this.fqnOfLoggerClass = fqcn;
    this.loggerName = logger.getName();
    this.loggerContext = logger.getLoggerContext();
    this.loggerContextVO = loggerContext.getLoggerContextRemoteView();
    this.level = level;

    this.message = message;
    this.argumentArray = argArray;

    if (throwable == null) {
        throwable = extractThrowableAnRearrangeArguments(argArray);
    }

    // 把Throwble放入一个Throwable的代理对象中
    if (throwable != null) {
        this.throwableProxy = new ThrowableProxy(throwable);
        LoggerContext lc = logger.getLoggerContext();
        if (lc.isPackagingDataEnabled()) {
            this.throwableProxy.calculatePackagingData();
        }
    }

    timeStamp = System.currentTimeMillis();
}

试一试吧

我只能说好使了, 图就不贴了

拓展

上面举例的那个方法public void error(String msg, Throwable t);, 第一个参数是异常的信息, 第二个参数是异常实例; 但是我们用Slf4j最舒服的地方不就是那个{}占位符吗? 现在又要拼字符串了?

另一个方法

public void error(String format, Object... args) {
    this.logMessageWithFormat("ERROR", format, args);
}

Logback中的实现和上面的流程基本一致, 其他的实现暂且不论…

好了好了, 这下舒服了 !!!

总结

综上所述, 正确的记录错误异常日志的方法

log.error("e:{}", e.getMessage(), e)
你好,根据你提供的代码及错误信息,很可能是在执行查询数据库的方法 `getRs()` 时,`conn()` 方法返回的 `stat` 为 `null`,导致后续调用 `stat` 对象的方法时出现空指针异常。可以在 `conn()` 方法中加入判断语句,当获取连接失败时返回 `null`,并在调用 `getRs()` 方法时加入判断 `stat` 是否为 `null` 的语句。修改后的代码如下: ```java public class dbConn { public dbConn() { try { jbInit(); } catch (Exception e) { e.printStackTrace(); } } private Statement conn() { try { // 加载驱动 Class.forName("com.mysql.jdbc.Driver"); // 数据库名称,管理员账号、密码 String url = "jdbc:mysql://localhost:3306/test"; String user = "root"; String pwd = "123456789"; // 连接 Connection con = null; con = DriverManager.getConnection(url, user, pwd); Statement stat = con.createStatement(); return stat; } catch (ClassNotFoundException ex) { return null; } catch (SQLException ex1) { return null; } } // 查询数据库 public ResultSet getRs(String sql) { try { Statement stat = conn(); if (stat == null) { // 判断连接是否成功 return null; } ResultSet rs = stat.executeQuery(sql); System.out.println(rs); return rs; } catch (SQLException ex) { System.err.println("------------" + ex.getMessage()); return null; } } // 更新数据库 public int getUpdate(String sql) { try { Statement stat = conn(); if (stat == null) { // 判断连接是否成功 return -1; } int i = stat.executeUpdate(sql); return i; } catch (Exception ex) { System.out.println(">>>>>>>>" + ex.getMessage()); return -1; } } private void jbInit() throws Exception { conn(); } } ``` 希望能帮到你。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值