背景
随着应用越来越复杂,依赖越来越多,日志系统越来越混乱,有时会出现一些奇怪的日志,比如:
1 | [] [] [] No credential found |
那么怎样排查这些奇怪的日志从哪里打印出来的呢?因为搞不清楚是什么logger打印出来的,所以想定位就比较头疼。
下面介绍用Alibaba开源的应用诊断利器Arthas的redefine命令快速定位奇怪日志来源。
- Arthas: https://github.com/alibaba/arthas
- redefine命令:https://alibaba.github.io/arthas/redefine.html
修改StringBuilder
首先在java代码里,字符串拼接基本都是通过StringBuilder
来实现的。比如下面的代码:
1 | public static String hello(String world) { |
实际上生成的字节码也是用StringBuilder
来拼接的:
1 | public static java.lang.String hello(java.lang.String); |
在java的logger系统里,输出日志时通常也是StringBuilder
来实现的,最终会调用StringBuilder.toString()
,那么我们可以修改StringBuilder
的代码来检测到日志来源。
StringBuilder.toString()
的原生实现是:
1 |
|
修改为:
1 |
|
增加的逻辑是:当String里包含No credential found
时打印出当前栈,这样子就可以定位日志输出来源了。
编绎修改过的StringBuilder
其实很简单,在IDE里把StringBuilder
的代码复制一份,然后贴到任意一个工程里,然后编绎即可。
也可以直接用javac来编绎:
1 | javac StringBuilder.java |
启动应用,使用Arthas redefine修改过的StringBuilder
启动应用后,在奇怪日志输出之前,先使用arthas attach应用,再redefine StringBuilder:
1 | $ redefine -p /tmp/StringBuilder.class |
当执行到输出[] [] [] No credential found
的logger代码时,会打印当前栈。实际运行结果是:
1 | [] [] [] No credential found |
可以看到是spas.sdk
打印出了[] [] [] No credential found
的日志。
总结
- logger最终会用StringBuilder来输出
- 修改StringBuilder来定位输出特定日志的地方
- 使用Arthas redefine命令来加载修改过的StringBuilder
- redefine命令实际上实现了任意代码线上debug的功能,可以随意本地修改代码重新编绎,然后线上redefine加载
- redefine的功能过于强大,所以请小心使用:)