记一次排查问题时发现日志不打印详细堆栈的情况
背景
调用打印日志的代码:
LOGGER.error("actionDetailBiz max monitorTime upload data error, ", ex);
注意如果如下调用方式是不会打印堆栈的:
LOGGER.error("actionDetailBiz max monitorTime upload data error, msg:{} ", ex.getMessage());
- 使用Logger.error(e)、Logger.error(e.getMessage())、Logger.error(“some msg” + e)、Logger.error(“some msg” + e.getMessage()) 都是调用的error(Object message),这个方法都会将入参当作Object输出,不会打印堆栈信息。
- 在使用Logger.error("first param ",e)时会调用error(String message, Throwable t),此方法会完整的打印出错误堆栈信息。
- 细节参考:Logger.error方法之打印错误异常的详细堆栈信息
现象
以下是从服务器上捞下来的部分日志:
2019-06-12 00:00:06 068 [action-etl-main-thread-75:108382040] - [ERROR] com.x.action.handler.SDKStatHandler - actionDetailBiz config data error,
java.lang.NullPointerException
2019-06-12 00:00:06 362 [action-etl-main-thread-69:108382334] - [ERROR] com.x.action.handler.SDKStatHandler - actionDetailBiz config data error,
java.lang.NullPointerException
2019-06-12 00:00:06 732 [action-etl-main-thread-73:108382704] - [ERROR] com.x.action.handler.SDKStatHandler - actionDetailBiz config data error,
java.lang.NullPointerException
一开始还以为是grep 没有把堆栈获取到,所以加了个 -A 10 发现结果还是一样,就是没有堆栈。
查多方资料后发现,这是jvm自身的优化,JVM 为了性能,如频繁的抛出某个异常,会重新编译,不再打印异常堆栈。此时要找堆栈就得到最开头去找了。。。可是最开头可能在几天前,可能日志早就被清理了
解决
启动参数加上 -XX:-OmitStackTraceInFastThrow
禁用该优化,强制打印异常堆栈
当然这会损耗性能且日志过大咯
本人加了这个参数之后就可以看到详细堆栈了
2019-06-13 11:14:33 097 [action-etl-main-thread-2:32713] - [ERROR] com.x.action.handler.SDKStatHandler - actionDetailBiz max monitorTime upload data error,
java.lang.NullPointerException
at com.x.action.handler.SDKStatHandler.lambda$builderResults$1(SDKStatHandler.java:221)
at java.util.HashMap.forEach(HashMap.java:1288)
at com.x.action.handler.SDKStatHandler.builderResults(SDKStatHandler.java:219)
at com.x.action.handler.SDKStatHandler.run(SDKStatHandler.java:141)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)