如何使用调试工具 DebugUtils?
李小翔
2014-01-20 10:59

内容列表
性能调试 	 # 
动态设置log4j的日志级别 	 # 
打印堆栈信息 	 # 
存取私有变量、执行私有方法 	 # 

BroToolkit 中有一个用于调试的 DebugUtils 工具类，下面对里面的几个常用的API做些简单介绍，更多方法参见API文档。

性能调试
DebugUtils 里包含了两个很简单的性能调试方法 ps 和 pm，可以用来打印代码的执行时间，比如下面代码：
DebugUtils.ps(”start”);
sleep 1000;
DebugUtils.pm(”111”);
sleep 2000;
DebugUtils.pm(”222”);
将输出：
0: start
1002: 111
2000: 222
也就是说从 “start” 到 “111” 之间的代码段运行了1002ms，而从 “111” 到 “222”，运行了 2000ms。
当发现某段代码执行缓慢、存在性能问题的时候，就可以将这段代码分成多个段，然后通过这种简单的打日志的方式，定位到最耗时间的代码，然后各个击破。
不过，ps、pm 方面只支持单线程。

动态设置log4j的日志级别
setLogLevel 方法可以用来动态设置日志级别，而不仅仅局限于启动时在 Config 中的限制。比如下面的代码：
// 输出所有通过 hibernate 执行的 sql 语句，可以用来调试 hql 的错误，或者是不是有重复执行、需要优化的数据库代码
DebugUtils.setLogLevel( ”org.hibernate.SQL”, ”DEBUG”) 

打印堆栈信息
调试问题代码的时候，如果仅仅局限于问题发生点，而不进行追溯，很容易发生只见树木、不见森林的情况，无法从源头解决问题；或者调试一些未知代码时，由于对代码结构不了接，经常会无从下手。
此时，可以通过 printStacktrace 方法打印出代码的整个调用堆栈信息，以便更好的跟踪调试，日志信息如：
1: java_lang_Thread$getStackTrace.call(Unknown Source)
2: org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
3: org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
4: org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)
5: bropen.toolkit.utils.DebugUtils.printStacktrace(DebugUtils.groovy:56)
6: bropen.toolkit.utils.DebugUtils$printStacktrace.call(Unknown Source)
7: org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
8: org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
9: org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)
10: Script1.run(Script1.groovy:1)
11: groovy.lang.GroovyShell.evaluate(GroovyShell.java:518)
12: groovy.lang.GroovyShell.evaluate(GroovyShell.java:556)
13: groovy.lang.GroovyShell.evaluate(GroovyShell.java:527)
14: groovy.lang.GroovyShell$evaluate.call(Unknown Source)
15: org.grails.plugins.console.ConsoleService.eval(ConsoleService.groovy:58)
......

存取私有变量、执行私有方法
java中，将成员变量、方法设置为 private、protected 目的就是为了不让别人访问，但是，调试问题、或者想要对开源组件进行定制的时候，getInaccessibleField、setInaccessibleField、invokeInaccessibleMethod 这几个无底线的方法却能发挥意想不到的作用。示例如下：
// 调用没有参数的方法 foo
DebugUtils.invokeInaccessibleMethod( foobar, ”foo”, null, null );
// 使用java调用有一个int类型参数的静态方法 bar
DebugUtils.invokeInaccessibleMethod( Foobar.class, ”bar”, (List) Arrays.asList(Integer.class), (List) Arrays.asList(200));
// 获得私有变量的值
DebugUtils.getInaccessibleField( Foobar.class, ”bar”, int.class );
// 设置私有变量的值  
DebugUtils.setInaccessibleField( this, ”excludePackageNames”, List.class, newValue );