介绍
Btrace (Byte Trace)是sun推出的一款java 动态、安全追踪(监控)工具,可以不停机的情况下监控线上情况,并且做到最少的侵入,占用最少的系统资源。
In a way, BTrace scripts are very similar to AOP's aspects, but can be attached to any existing Java code (or better, bytecode) at runtime and without any configuration or modification at development time.
运行环境搭建
Steps to run BTrace
1.下载[btrace|http://kenai.com/projects/btrace/downloads/directory/releases]包 并把btrace的命令放到path中
2. jps命令查出需要监控的jvm pid
3. 编写BTrace程序 (安装包中带有大量的例子)
4. Run btrace tool by the following command line:
btrace <pid> <btrace-script>
BTrace能作的事情
记录以下事件:
* method calls;
* execution times;
* constructor invocation;
* available memory;
触发脚本执行的场景:
Script execution is tied to several situations
* reaching a specific line number;
* invoking a given method;
* returning from a method;
* invoking system calls (e.g. exit);
* recurring timer (period execution at fixed intervals);
BTrace不能作的事情
保证BTrace追踪的是只读操作,所以有一堆限制:
不仅强制要求java脚本需要提供public static方法.而且,脚本里无法实例化对象,数组,不能抛异常或捕捉,不能有循环,内部类等。
特别是不能调用任何的实例、静态方法,除了com.sun.btrace.BTraceUtils类static methods。*所以BTrace的功能全部在BTraceUtils中的方法。*
To guarantee that the tracing actions are "read-only" (i.e., the trace actions don't change the state of the program traced) and bounded (i.e., trace actions terminate in bounded time), a BTrace program is allowed to do only a restricted set of actions. In particular, a BTrace class
* can not create new objects.
* can not create new arrays.
* can not throw exceptions.
* can not catch exceptions.
* can not make arbitrary instance or static method calls - only the public static methods of com.sun.btrace.BTraceUtils class may be called from a BTrace program.
* can not assign to static or instance fields of target program's classes and objects. But, BTrace class can assign to it's own static fields ("trace state" can be mutated).
* can not have instance fields and methods. Only static public void returning methods are allowed for a BTrace class. And all fields have to be static.
* can not have outer, inner, nested or local classes.
* can not have synchronized blocks or synchronized methods.
* can not have loops (for, while, do..while)
* can not extend arbitrary class (super class has to be java.lang.Object)
* can not implement interfaces.
* can not contains assert statements.
* can not use class literals.
例子
监控某个方法的执行时间
监控所有HTTP request请求的执行时间
@TLS private static String invoke = ""; @OnMethod(clazz = "javax.servlet.http.HttpServlet", method = "service", type = "void service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)") public static void start(@ProbeClassName String probeClass, @ProbeMethodName String probeMethod) { invoke = concat(str(threadId(currentThread())), strcat("_", str(timeMillis()))); print(invoke); print(" "); print(probeClass); print(" "); print(probeMethod); print(" s "); println(timeMillis()); } @OnMethod(clazz = "javax.servlet.http.HttpServlet", method = "service", type = "void service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)", location = @Location(Kind.RETURN)) public static void end(@ProbeClassName String probeClass, @ProbeMethodName String probeMethod) { print(invoke); print(" "); print(probeClass); print(" "); print(probeMethod); print(" e "); println(timeMillis()); invoke = ""; }
监控所有HTTP request请求的具体java调用栈 call stack以及执行时间
在上面监控http request的基础上,增加你关心类的方法。然后通过ID(Thread ID, 时间戳)生成对应的调用图。
import static com.sun.btrace.BTraceUtils.name; import static com.sun.btrace.BTraceUtils.print; import static com.sun.btrace.BTraceUtils.println; import static com.sun.btrace.BTraceUtils.probeClass; import static com.sun.btrace.BTraceUtils.probeMethod; import static com.sun.btrace.BTraceUtils.str; import static com.sun.btrace.BTraceUtils.strcat; import static com.sun.btrace.BTraceUtils.timeMillis; import com.sun.btrace.annotations.BTrace; import com.sun.btrace.annotations.Kind; import com.sun.btrace.annotations.Location; import com.sun.btrace.annotations.OnMethod; import com.sun.btrace.annotations.TLS; /** * 监控方法耗时 * */ @BTrace public class PrintTimes { /** * 开始时间 */ @TLS private static long startTime = 0; /** * 方法开始时调用 */ @OnMethod(clazz = "/com\\.mike\\../", method = "/.+/") public static void startMethod() { startTime = timeMillis(); } /** * 方法结束时调用<br> * Kind.RETURN这个注解很重要 */ @SuppressWarnings("deprecation") @OnMethod(clazz = "/com\\.mike\\../", method = "/.+/", location = @Location(Kind.RETURN)) public static void endMethod() { print(strcat(strcat(name(probeClass()), "."), probeMethod())); print(" ["); print(strcat("Time taken : ", str(timeMillis() - startTime))); println("]"); } }