灵活
清单 7 的输出看起来有用一些了,但是缺少选择性。如果对大的斐波纳契数列(比如说 12)使用这 段代码,将会看到长时间的滚动输出。作为开发一个有用的日志记录 aspect 的最后一步,我将实现对输 出的一些灵活的筛选。
最后我还想做成一个可以用来记录执行路径和相关性能度量的 aspect,这样就可以看到执行时间最长 的方法调用。我使用的方法类似于我在关于 Java 编程动态性的文章中所展示的方法,它基于 java.lang.System.currentTimeMillis() 时间值,使用直接 classwo***ng。 这个计时器不是非常精确 —— 在 Microsoft Windows 上,分辨率约为 17 毫秒,在我的 Linux 系统上,约为 1 毫秒 —— 但是 可以在所有版本的标准 Java 平台上找到它,这使得它至少在展示方面还是有用的。(下个月,我将讨论 获得更好的计时器分辨率。)根据方法调用所花的时间筛选执行踪迹会得出一些有趣的结果。
为了保持同样的输出结构,这个基于时间的筛选要稍微复杂一些。首先,需要延迟打印每个方法调用 的“进入”线(“entering”line),直到可以确定所用的时间足以使它通过我的筛选器。这本身并不困 难,但是我发现 真的 需要打印方法信息时,首先需要打印每一个包围方法调用的进入线。为此我需要保 持还没打印的方法的进入信息列表,然后在打印当前方法的进入信息之前将它们都转储出来,并带有正确 的缩进。清单 8 显示了修改后的新 advice 方法,以使它使用这个未打印的方法列表。
清单 8. 带时间筛选器的 Advice 方法
private ArrayList m_unprintedCalls = new ArrayList();
private void indent(int depth) {
for (int i = 0; i < depth; i++) {
System.out.print('' '');
}
}
public Object traceTimed(JoinPoint join) throws Throwable {
// add joinpoint to list pending print
m_pendingPrints.add(join);
// time joinpoint execution
long start = System.currentTimeMillis();
m_nestingDepth++;
Object result = join.proceed();
m_nestingDepth--;
// remove joinpoint if still on pending list
// (will always be last in list)
boolean entered = true;
if (m_pendingPrints.size() > 0) {
m_pendingPrints.remove(m_pendingPrints.size()-1);
entered = false;
}
// check if execution time above cutoff
long time = System.currentTimeMillis() - start;
if (time > 1) {
// print all unprinted "entering" lines
int count = m_pendingPrints.size();
for (int i = 0; i < count; i++) {
// print entering information for nesting call
// (nesting depth based on start of pendings)
indent(m_nestingDepth-count+i);
JoinPoint prior =
(JoinPoint)m_pendingPrints.get(i);
System.out.print("Entering ");
printCall(prior);
System.out.println();
}
// clear all pendings now that they''re printed
m_pendingPrints.clear();
// print exiting information including time
indent();
System.out.print("Leaving ");
printCall(j
|