BTrace是調試神器,可以通過自己編寫的腳本,獲取應用的一切調用信息。而不需要重啟應用!
Btrace 項目源碼信息(你行你上~)
項目地址:http://github.com/btraceio/btrace
但是應用下載地址是: https://github.com/btraceio/btrace/releases (因為一般你並不想編譯這些代碼)
小白的打開姿勢,操作步驟:
1. 打開 jvisualVm 工具;
2. 加載 BTrace 工具, 先把 插件中心地址更改掉: https://visualvm.github.io/uc/8u131/updates.xml.gz ;
3. 連接到想要 trace 的服務器, 如本地 tomcat;
4. 右擊tomcat進程,> Trace application...
5. 寫debug程序,樣例如下:(在IDE中編寫)
6. 直接使用 brace監控: btrace <pid> BtraceScript.java

來個例子:(攔截 spring 的 doService 方法)
/* BTrace Script Template */ import com.sun.btrace.AnyType; import com.sun.btrace.BTraceUtils; import com.sun.btrace.annotations.BTrace; import com.sun.btrace.annotations.Duration; import com.sun.btrace.annotations.Kind; import com.sun.btrace.annotations.Location; import com.sun.btrace.annotations.OnMethod; import com.sun.btrace.annotations.ProbeClassName; import com.sun.btrace.annotations.ProbeMethodName; import static com.sun.btrace.BTraceUtils.currentThread; import static com.sun.btrace.BTraceUtils.probeLine; import static com.sun.btrace.BTraceUtils.threadId; @BTrace public class TracingScript { /* put your code here */ @OnMethod( clazz = "/org.springframework.servlet.+/", method = "/doService.*/", location = @Location(Kind.RETURN) ) public static void traceExecute(AnyType[] args, @ProbeClassName String name, @ProbeMethodName String method, @Duration long time) { long durationTime = time/1000000; if(durationTime > 0){ String output = name + "." + method + "#" + probeLine() + " cost: " + durationTime + "ms, ThreadId:" + threadId(currentThread()); BTraceUtils.println(output); // 打印整體參數 BTraceUtils.printArray(args); // 調用應用的各字段進行反射調用 BTraceUtils.printFields(args[1]); BTraceUtils.printFields(args[2]); BTraceUtils.println("over..."); // 結束符 BTraceUtils.println(""); } } }
在IDE中編寫時,需要導入 pom.xml 如下:
<!-- https://mvnrepository.com/artifact/com.sun.tools.btrace/btrace-agent --> <dependency> <groupId>com.sun.tools.btrace</groupId> <artifactId>btrace-agent</artifactId> <version>1.1.3</version> </dependency> <!-- https://mvnrepository.com/artifact/com.sun.tools.btrace/btrace-boot --> <dependency> <groupId>com.sun.tools.btrace</groupId> <artifactId>btrace-boot</artifactId> <version>1.1.3</version> </dependency> <!-- https://mvnrepository.com/artifact/com.sun.tools.btrace/btrace-client --> <dependency> <groupId>com.sun.tools.btrace</groupId> <artifactId>btrace-client</artifactId> <version>1.1.3</version> </dependency>
2. 攔截方法定義,也說是 @OnMethod 注解的作用
Btrace使用@OnMethod注解定義需要分析的方法入口,在@OnMethod注解中,需要指定class、method以及location等,class表明需要監控的類,method表明需要監控的方法!
@OnMethod( clazz = "/org.springframework.servlet.+/", method = "/doService.*/", location = @Location(Kind.RETURN) )
1. 正則表達式定位(全匹配是正則的一種特例)
可以用表達式,批量定義需要監控的類與方法。正則表達式需要寫在兩個 "/" 中間。
通過在攔截函數的定義里注入@ProbeClassName String probeClass, @ProbeMethodName String probeMethod 參數,告訴腳本實際匹配到的類和方法名。
2. 按接口,父類,Annotation定位
比如我想匹配所有的Filter類,在接口或基類的名稱前面,加個+ 就行
@OnMethod(clazz="+com.vip.demo.Filter", method="doFilter")
也可以按類或方法上的annotaiton匹配,也就是注解匹配,前面加上@就行
@OnMethod(clazz="@javax.jws.WebService", method="@javax.jws.WebMethod")
3. 構造方法匹配 <init>
@OnMethod(clazz="java.net.ServerSocket", method="<init>")
4. 靜態內部類的寫法,在類與內部類之間加上"$"
@OnMethod(clazz="com.vip.MyServer$MyInnerClass", method="hello")
3. 攔截時機, 即@Location注解的作用
// Location 的定義 /** * This annotation specifies a particular "location" within a * traced/probed java method for BTrace probe specifications. * * @author A. Sundararajan */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Location { /** * Kind of the location. * * @see Kind */ Kind value() default Kind.ENTRY; /** * Specifies where do want to probe with * respect to the location of interest. * * @see Where */ Where where() default Where.BEFORE; /** * Specifies the fully qualified class name for * certain kind of probe locations. * * <p> * <h3>Since 1.3.11</h3> * The specification can contain references to user arguments. * These references are using Ant style substitution patterns. * If a reference is not resolvable the whole probe point will be effectively disabled. * <br> * <pre> * {@code @OnMethod(clazz = "MyClass", method = "myMethod", location = @Location(clazz = "${package}.OtherClass"))} * </pre> * </p> */ String clazz() default ""; /** * Specifies the method name for * certain kind of probe locations. * * <p> * <h3>Since 1.3.11</h3> * The specification can contain references to user arguments. * These references are using Ant style substitution patterns. * If a reference is not resolvable the whole probe point will be effectively disabled. * <br> * <pre> * {@code @OnMethod(clazz = "MyClass", method = "myMethod", location = @Location(clazz = "OtherClass", method = "${method}"))} * </pre> * </p> */ String method() default ""; /** * Specifies the field name for Kind.FIELD_SET * and Kind.FIELD_GET probes. * * @see Kind#FIELD_GET * @see Kind#FIELD_SET * * <p> * <h3>Since 1.3.11</h3> * The specification can contain references to user arguments. * These references are using Ant style substitution patterns. * If a reference is not resolvable the whole probe point will be effectively disabled. * <br> * <pre> * {@code @OnMethod(clazz = "MyClass", method = "myMethod", location = @Location(clazz = "OtherClass", field = "${field}"))} * </pre> * </p> */ String field() default ""; /** * Specifies field or method type for * certain kind of probe locations. The type * is specified like in Java source - except * the method or field name and parameter names * are not included. * * <p> * <h3>Since 1.3.11</h3> * The specification can contain references to user arguments. * These references are using Ant style substitution patterns. * If a reference is not resolvable the whole probe point will be effectively disabled. * <br> * <pre> * {@code @OnMethod(clazz = "MyClass", method = "myMethod", location = @Location(clazz = "OtherClass", type = "${ret} ()"))} * </pre> * </p> */ String type() default ""; /** * Specifies the line number for Kind.LINE probes. * * @see Kind#LINE */ int line() default 0; } // Kind 的定義 public enum Kind { /** * <h2>Array element load</h2> * * <h3>Unannotated probe handler parameters:</h3> * <ol> * <li>{@code type[]} - the array instance</li> * <li>{@link int int} - array index</li> * </ol> * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that method is static</li> * <li>{@linkplain Return} - the return value of the method call (only for {@linkplain Where#AFTER})</li> * </ul> */ ARRAY_GET, /** * <h2>Array element store</h2> * * <h3>Unannotated probe handler parameters:</h3> * <ol> * <li>{@code type[]} - the array instance</li> * <li>{@link int int} - array index</li> * <li>{@link java.lang.Object Object} - new value</li> * </ol> * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that method is static</li> * </ul> */ ARRAY_SET, /** * <h2>Method call</h2> * <p> * The order and number of unannotated parameters (if provided) must * fully match the called method signature. Instead of specific parameter * types one can use {@linkplain AnyType} to match any type. * <p> * If the only unannotated parameter is of type {@link AnyType AnyType[]} * it will contain the called method parameters in the order defined by * its signature. * * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that method is static</li> * <li>{@linkplain TargetInstance} - the target instance of the method call * or null if the method is static</li> * <li>{@linkplain TargetMethodOrField} - the name of the method which is called</li> * <li>{@linkplain Return} - the return value of the method call (only for {@linkplain Where#AFTER})</li> * <li>{@linkplain Duration} - the method call duration in nanoseconds (only for {@linkplain Where#AFTER}</li> * </ul> */ CALL, /** * <h2>Exception catch</h2> * * <p> * The order and number of unannotated parameters (if provided) must * fully match the probed method signature. Instead of specific parameter * types one can use {@linkplain AnyType} to match any type. * <p> * If the only unannotated parameter is of type {@link AnyType AnyType[]} * it will contain the probed method parameters in the order defined by * its signature. * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that method is static</li> * <li>{@linkplain TargetInstance} - the caught {@linkplain Throwable} (@since 1.3.11)</li> * </ul> */ CATCH, /** * <h2>Checkcast</h2> * * <h3>Unannotated probe handler parameters:</h3> * <ol> * <li>{@link java.lang.String String} - type to cast to</li> * </ol> * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that method is static</li> * <li>{@linkplain TargetInstance} - the casted instance ({@linkplain AnyType})</li> * </ul> */ CHECKCAST, /** * <h2>Method entry</h2> * <p> * The order and number of unannotated parameters (if provided) must * fully match the probed method signature. Instead of specific parameter * types one can use {@linkplain AnyType} to match any type. * <p> * If the only unannotated parameter is of type {@link AnyType AnyType[]} * it will contain the probed method parameters in the order defined by * its signature. * * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that method is static</li> * </ul> */ ENTRY, /** * <h2>"return" because of no-catch</h2> * * <p> * The order and number of unannotated parameters (if provided) must * fully match the probed method signature. Instead of specific parameter * types one can use {@linkplain AnyType} to match any type. * <p> * If the only unannotated parameter is of type {@link AnyType AnyType[]} * it will contain the probed method parameters in the order defined by * its signature. * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that method is static</li> * <li>{@linkplain Duration} - the method call duration in nanoseconds (only for {@linkplain Where#AFTER}</li> * <li>{@linkplain TargetInstance} - the {@linkplain Throwable} instance (@since 1.3.11)</li> * </ul> */ ERROR, /** * <h2>Getting a field value</h2> * * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that method is static</li> * <li>{@linkplain TargetInstance} - the field owner instance or null * if the field is static</li> * <li>{@linkplain TargetMethodOrField} - the name of the method which is called</li> * <li>{@linkplain Return} - the return value of the method call (only for {@linkplain Where#AFTER})</li> * </ul> */ FIELD_GET, /** * <h2>Setting a field value</h2> * * <h3>Unannotated probe handler parameters:</h3> * <ol> * <li>{@link java.lang.Object Object} - new field value</li> * </ol> * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that field is static</li> * <li>{@linkplain TargetInstance} - the field owner instance or null * if the field is static</li> * <li>{@linkplain TargetMethodOrField} - the name of the method which is called</li> * </ul> */ FIELD_SET, /** * <h2>instanceof check</h2> * * <h3>Unannotated probe handler parameters:</h3> * <ol> * <li>{@link java.lang.String String} - type to check against</li> * </ol> * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that method is static</li> * <li>{@linkplain TargetInstance} - the checked instance ({@linkplain AnyType})</li> * </ul> */ INSTANCEOF, /** * <h2>Source line number</h2> * * <h3>Unannotated probe handler parameters:</h3> * <ol> * <li>{@link int int} - line number</li> * </ol> * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that method is static</li> * </ul> */ LINE, /** * <h2>New object created</h2> * * <h3>Unannotated probe handler parameters:</h3> * <ol> * <li>{@link java.lang.String String} - object type name</li> * </ol> * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that method is static</li> * <li>{@linkplain Return} - the return value of the method call (only for {@linkplain Where#AFTER})</li> * </ul> */ NEW, /** * <h2>New array created</h2> * * <h3>Unannotated probe handler parameters:</h3> * <ol> * <li>{@link java.lang.String String} - array type name</li> * <li>{@link int int} - number of dimensions</li> * </ol> * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that method is static</li> * <li>{@linkplain Return} - the return value of the method call (only for {@linkplain Where#AFTER})</li> * </ul> */ NEWARRAY, /** * <h2>Return from method</h2> * <p> * The order and number of unannotated probe handler parameters (if provided) * must fully match the probed method signature. Instead of specific parameter * types one can use {@linkplain AnyType} to match any type. * <p> * If the only unannotated parameter is of type {@link AnyType AnyType[]} * it will contain the probed method parameters in the order defined by * its signature. * * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that method is static</li> * <li>{@linkplain Return} - the return value of the method call (only for {@linkplain Where#AFTER})</li> * <li>{@linkplain Duration} - the method call duration in nanoseconds (only for {@linkplain Where#AFTER}</li> * </ul> */ RETURN, /** * <h2>Entry into a synchronized block</h2> * * <h3>Unannotated probe handler parameters:</h3> * <ol> * <li>{@link java.lang.Object Object} - lock object</li> * </ol> * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that method is static</li> * </ul> */ SYNC_ENTRY, /** * <h2>Exit from a synchronized block</h2> * * <h3>Unannotated probe handler parameters:</h3> * <ol> * <li>{@link java.lang.Object Object} - lock object</li> * </ol> * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that method is static</li> * </ul> */ SYNC_EXIT, /** * <h2>Throwing an exception</h2> * * <p> * The order and number of unannotated parameters (if provided) must * fully match the probed method signature. Instead of specific parameter * types one can use {@linkplain AnyType} to match any type. * <p> * If the only unannotated parameter is of type {@link AnyType AnyType[]} * it will contain the probed method parameters in the order defined by * its signature. * <h3>Allowed probe handler parameter annotations:</h3> * <ul> * <li>{@linkplain ProbeClassName} - the name of the enclosing class</li> * <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li> * <li>{@linkplain Self} - the instance enclosing the declaring method or null * if that method is static</li> * <li>{@linkplain TargetInstance} - the thrown exception (@since 1.3.11)</li> * </ul> */ THROW };
Location 主要屬性有 value 和 where, 而 value 則是幾個常用的定義點!
定義Btrace對方法的攔截位置,通過@Location注解指定,默認為Kind.ENTRY。可以為同一個函數的不同的Location,分別定義多個攔截函數。
1. Kind.Entry與Kind.Return
Kind.ENTRY:在進入方法時,調用Btrace腳本
Kind.RETURN:方法執行完時,調用Btrace腳本,只有把攔截位置定義為Kind.RETURN,才能獲取方法的返回結果@Return和執行時間@Duration
duration的單位是納秒,要除以 1,000,000 才是毫秒。
2. Kind.Error, Kind.Throw和 Kind.Catch
異常拋出(Throw),異常被捕獲(Catch),異常沒被捕獲被拋出函數之外(Error),主要用於對某些異常情況的跟蹤。
在攔截函數的參數定義里注入一個Throwable的參數,代表異常。
3. Kind.Call與Kind.Line
Kind.CALL:分析方法中調用其它方法的執行情況,比如在execute方法中,想獲取add方法的執行耗時,必須把where設置成Where.AFTER.
Kind.LINE:通過設置line,可以監控代碼是否執行到指定的位置.
下例定義監控bind()函數里調用的所有其他函數:
@OnMethod(clazz = "java.net.ServerSocket", method = "bind", location = @Location(value = Kind.CALL, clazz = "/.*/", method = "/.*/", where = Where.AFTER)) public static void onBind(@Self Object self, @TargetInstance Object instance, @TargetMethodOrField String method, @Duration long duration)
所調用的類及方法名所注入到@TargetInstance與 @TargetMethodOrField中。
下例監控代碼是否到達了Socket類的第363行。
@OnMethod(clazz = "java.net.ServerSocket", location = @Location(value = Kind.LINE, line = 363))
4. 如何使用Btrace定位問題
1. 打印this,參數 與 返回值
@OnMethod(clazz = "java.io.File", method = "createTempFile", location = @Location(value = Kind.RETURN)) public static void o(@Self Object self, String prefix, String suffix, @Return AnyType result)
如果想打印它們,首先按順序定義用@Self 注釋的this, 完整的參數列表,以及用@Return 注釋的返回值。
需要打印哪個就定義哪個,不需要的就不要定義。但定義一定要按順序,比如參數列表不能跑到返回值的后面。
Self:
如果是靜態函數, self為空。
前面提到,如果上述使用了非JDK的類,命令行里要指定classpath。不過,如前所述,因為BTrace里不允許調用類的方法,所以定義具體類很多時候也沒意 思,所以self定義為Object就夠了。
參數數列表要么不要定義,要定義就要定義完整,否則BTrace無法處理不同參數的同名函數。
用AnyType來定義任意類型的參數,類似於 Object 。
2. 方法執行時,查看對象的實例屬性值
再次強調,為了保證性能不受影響,Btrace不允許調用任何實例方法。
比如不能調用getter方法(怕在getter里有復雜的計算),只會通過直接反射來讀取屬性名。
又比如,除了JDK類,其他類toString時只會打印其類名+System.IdentityHashCode。
println, printArray,都按上面的規律進行,所以只能打打基本類型。
如果想打印一個Object的屬性,用printFields()來反射。
如果只想反射某個屬性,參照下面打印Port屬性的寫法。從性能考慮,應把field用靜態變量緩存起來。
注意JDK類與非JDK類的區別:
import java.lang.reflect.Field; //JDK的類這樣寫就行 private static Field fdFiled = field("java.io,FileInputStream", "fd"); //非JDK的類,要給出ClassLoader,否則ClassNotFound private static Field portField = field(classForName("com.vip.demo.MyObject", contextClassLoader()), "port"); public static void onChannelRead(@Self Object self) { println("port:" + getInt(portField, self)); }
3.TLS,攔截函數間的通信機制
如果要多個攔截函數之間要通信,可以使用@TLS定義 ThreadLocal的變量來共享
@TLS private static int port = -1; @OnMethod(clazz = "java.net.ServerSocket", method = "<init>") public static void onServerSocket(int p){ port = p; } @OnMethod(clazz = "java.net.ServerSocket", method = "bind") public static void onBind(){ println("server socket at " + port); }
4. 誰調用了這個函數(原理:攔截到方法后,把堆棧打出來)
@OnMethod(clazz = "java.lang.System", method = "gc") public static void onSystemGC() { println("entered System.gc()"); jstack(); }
5. 統計方法的調用次數,且每隔1分鍾打印調用次數
@Export static AtomicLong counter = new AtomicLong(); @OnMethod(class="com.**.MyObject",method="add") public static void run(){ counter.getAndIncrement(); } @OnTimer(1000*60) public static void run(){ BTraceUtils.println("count: " + connter.get()); counter.set(0); }
5. linux 上使用 btrace!
1. 下載壓btrace縮包: wget https://github.com/btraceio/btrace/releases/download/v1.3.11.3/btrace-bin-1.3.11.3.zip ;
2. 解壓: unzip btrace-bin-1.3.11.3.zip -d btrace-bin-1.3.11.3;
3. cd btrace-bin-1.3.11.3, ./btrace <pid> TracingScript.java, 就可以看效果了;
4. 修改腳本以解決問題;
5. 通過反射機制,可以很方法的得到當前實例的屬性值;
//print one field Field oneFiled = BTraceUtils.field("com.xx.test", "name"); BTraceUtils.println("print one field: " + BTraceUtils.get(oneFiled, args[0]));
6. BTraceUtils.printFields(args[1]); 調用封裝好的打印復雜對象;
// 調用應用的各字段進行反射調用 BTraceUtils.printFields(args[1]);


注意: btrace 是字節碼注入,是可能導致jvm退出的,所以,應盡量先在測試環境驗證ok后,再到線上調用,或者把注入的類范圍盡量的縮小,而非大的正則匹配如: clazz="/com.xxx.*/", method="/.*/",就會導致大量的變更,影響性能也提高了運行風險!
快速定位你的線上問題!
最后,附上幾個調試過程的幾個經驗之坑:
1. BTRACE_HOME 的設置,在 /etc/profile.d/btraceenv.sh 中設置即可; 如果不想設置BTRACE_HOME, 可以直接切換到btrace的bin目錄操作即可;
2. 使用 JVisualVm 可以運行的trace代碼,不代表使用命令行也運行,最好都使用命令行驗證下腳本;
3. JVisualVm 及一些jdk環境,支持 unsafe=true, 而在其他環境則不一定允許; JVisualVm 中如果去除 unsafe=true, 則有些函數會受限制;
4. btrace 腳本如果正常情況無法運行如: btrace 7311 TracingScriptTemplate.java, 則可以切換到debug模式查看: btrace -v 7311 TracingScriptTemplate.java,這里面會出現很多btrace的調試日志,如果不想看這些日志,可以直接過濾掉: btrace -v 7311 TracingScriptTemplate.java | grep -v "DEBUG: ";
5. 借助IDE編寫的Trace腳本,可以直接運行,即 trace 腳本可包含package包名,也可以沒有包名;但文件必須要以.java結尾(沒有試過其他小語種),否則編譯時會報錯;
6. 代碼中如果本身就使用了一些開發腳本,如springboot的開發組件 spring-boot-devtools , 則可能導致 btrace 腳本無法注入, 最好先去掉這些組件再上線;
最后,再附一個完整的鏈路監控的 script 供參考:
/* BTrace Script Template */ import com.sun.btrace.AnyType; import com.sun.btrace.BTraceUtils; import com.sun.btrace.annotations.*; import static com.sun.btrace.BTraceUtils.currentThread; import static com.sun.btrace.BTraceUtils.probeLine; import static com.sun.btrace.BTraceUtils.threadId; @BTrace public class TracingScript { private static final String clazzForTracePattern = "/com.alipay.common.event.tbnotify.adapter.+/"; private static final String methodForTracePattern = "/.*/"; @OnMethod( clazz = clazzForTracePattern, method = methodForTracePattern, location = @Location(Kind.ENTRY) ) public static void traceExecuteEnter(AnyType[] args, @ProbeClassName String name, @ProbeMethodName String method) { String output = "enter in: " + name + "." + method + "#" + probeLine() + ", ThreadId:" + threadId(currentThread()); BTraceUtils.println(output); // 打印整體參數 BTraceUtils.printArray(args); // 調用應用的各字段進行反射調用 printPerFields(args); BTraceUtils.println("enter over..."); // 結束符 BTraceUtils.println(""); } @OnMethod( clazz = clazzForTracePattern, method = methodForTracePattern, location = @Location(Kind.RETURN) ) public static void traceExecuteReturn(AnyType[] args, @ProbeClassName String name, @ProbeMethodName String method, @Return AnyType result, @Duration long time) { long durationTime = time/1000000; if(durationTime > 0){ String output = "return from: " + name + "." + method + "#" + probeLine() + " cost: " + durationTime + "ms, ThreadId:" + threadId(currentThread()); BTraceUtils.println(output); // 打印整體參數 BTraceUtils.printArray(args); printPerFields(args); BTraceUtils.printFields(result); BTraceUtils.println("return over..."); // 結束符 BTraceUtils.println(""); } } // ----------------------------------------------------- // ================= 以下為捕獲異常代碼 ================ // ----------------------------------------------------- @TLS private static Throwable currentException; @OnMethod( clazz = "java.lang.Throwable", method = "<init>" ) public static void onthrow(@Self Throwable self) { currentException = self; } @OnMethod( clazz = "java.lang.Throwable", method = "<init>" ) public static void onthrow1(@Self Throwable self, String s) { currentException = self; } @OnMethod( clazz = "java.lang.Throwable", method = "<init>" ) public static void onthrow1(@Self Throwable self, String s, Throwable cause) { currentException = self; } @OnMethod( clazz = "java.lang.Throwable", method = "<init>" ) public static void onthrow2(@Self Throwable self, Throwable cause) { currentException = self; } @OnMethod( clazz = "java.lang.Throwable", method = "<init>", location = @Location(Kind.RETURN) ) public static void onThrownReturn() { if (currentException != null) { BTraceUtils.jstack(currentException); BTraceUtils.print("<--------------->"); currentException = null; } } private static void printPerFields(AnyType[] args) { // 調用應用的各字段進行反射調用 if(args.length > 0) { BTraceUtils.printFields(args[0]); } if(args.length >= 2) { BTraceUtils.printFields(args[1]); } if(args.length >= 3) { BTraceUtils.printFields(args[2]); } } }
