在开始之前,必须已完成练习 1.3:部署探针。
Probekit 可以用于解决很多现实世界的调试和运行时分析问题。您现在已经了解编写和部署探针的基础,所以下列课程将不再重复这些活动,而是为您可能编写以调查自己的应用程序的探针提供一些建议。
假设 servlet 中的某个方法偶尔会抛出异常。生成的日志消息不太有帮助;它没有明确指示问题所在。 要获取更多信息,您可以编写方法抛出异常时执行的探针,并且探针片段中的 Java 代码可以记录异常消息以及方法的实参。 这将帮助您查看抛出异常的原因。
编写和部署探针时,注意以下几点:
System.out.println("[Exception probe triggered!]"); for (int i = 0; i < args.length; i++) { System.out.println("Argument " + i + "=" + args[i]); }
以下是一个相当复杂的探针示例。假设程序中有一个通常执行很快的方法,但是偶尔要耗费比较长时间(例如超过 100 毫秒)。
您可以编写一个探针,只要执行了此方法,该探针就会记下进入和退出的时间。当执行时间短时,探针不执行任何操作。 执行时间长时,探针报告方法的实参,以及其它与当时的程序状态有关的重要信息。这允许您确定导致执行时间较长的情况。
编写探针时,请注意以下几点:
您可以使用 Probekit 的“类作用域中的片段”功能编写 Java 代码片段,此片段将被编译进在任何方法之外生成的类。 在这种情况下,片段用于定义称为 entryTime 的静态字段,入口和出口片段都将用到此字段。
要向探针添加元素,请单击 Probekit 编辑器中的 Probe 元素; 此编辑器显示一个字段,供您为类作用域中的片段输入 Java 代码:
static long entryTime; static final long thresholdDuration = 100;
entryTime = System.currentTimeMillis();
long now = System.currentTimeMillis(); if ((now - entryTime) > thresholdDuration) { System.out.println( "[Spent a long time in " + className + "." + methodName + ": this=" + thisObject + "]"); }
在方法入口,探针报告当前时间。在方法出口,探针比较当前时间和记录的进入时间, 并且如果已过去的时间量多于特定时间量(在“thresholdDuration”中指定的量),将使用 System.out 打印报告。
刚才讨论的探针不能处理递归(直接或间接的)的方法,并且不能处理可能同时运行于多个线程的方法。 要解决递归问题,您可以在“堆栈”中记录进入时间;要解决多线程问题,您可以在 ThreadLocal 变量中存储堆栈。
下面是创建递归安全和线程安全版本的探针所需的内容:
static ThreadLocal tl = new ThreadLocal() { public Object initialValue() { return new Stack(); } }; static long thresholdDuration = 100; // Report if time is over 100ms
long now = System.currentTimeMillis(); Stack stk = (Stack)tl.get(); stk.push(new Long(now));
long now = System.currentTimeMillis(); Stack stk = (Stack)tl.get(); long entryTime = ((Long)stk.pop()).longValue(); if (now - entryTime > thresholdDuration) { System.out.println( "[Spent a long time in " + cName + "." + mName + ": this=" + thisObj + "]"); }