首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

BTrace 實戰入門:不重啟、不改代碼動態追蹤線上 Java 應用運行細節

     編輯:Java 相關知識

線上應用出現問題時,往往需要更多運行細節,但無法預先在代碼埋點、不可重啟部署、第三方庫更無法修改。BTrace 正是為此而生:零侵入、零重啟、動態掛載,通過 Java 編寫腳本攔截目標方法,實時輸出參數、返回值、耗時、調用堆疊、指定行執行情況。官網:kenai.com/projects/btrace/。本文以實例演示四大核心追蹤場景。

基礎語法與運行方式

btrace -cp [btrace.jar路徑] [目標進程PID] [腳本文件.java]

腳本核心註解:

  • @BTrace:標記腳本類
  • @OnMethod(clazz="目標類", method="目標方法", location=@Location(Kind.RETURN/ENTRY/LINE)):定義攔截點
  • @Self:當前實例引用
  • @Return:返回值
  • @ProbeClassName/@ProbeMethodName:動態獲取類/方法名
  • @TLS:線程本地變量(用於跨攔截點傳遞狀態,如計時起點)
  • BTraceUtils 工具:println/strcat/str/timeMillis/jstack/get(field, instance) 等

目標類示例


public class Case1 {
    public static void main(String[] args) throws Exception {
        Random random = new Random();
        CaseObject object = new CaseObject();
        boolean result = true;
        while (result) {
            result = object.execute(random.nextInt(1000));
            Thread.sleep(1000);
        }
    }
}

public class CaseObject {
    private static int sleepTotalTime = 0;

    public boolean execute(int sleepTime) throws Exception {
        System.out.println("sleep: " + sleepTime);
        sleepTotalTime += sleepTime;
        Thread.sleep(sleepTime);
        return true;
    }
}

四大核心追蹤場景實戰

1. 獲取方法參數、返回值、靜態/實例字段值


@BTrace public class TraceMethodArgsAndReturn {
    @OnMethod(clazz="CaseObject", method="execute", location=@Location(Kind.RETURN))
    public static void traceExecute(@Self CaseObject instance, int sleepTime, @Return boolean result) {
        println("call CaseObject.execute");
        println(strcat("sleepTime is:", str(sleepTime)));
        println(strcat("sleepTotalTime is:", str(get(field("CaseObject", "sleepTotalTime"), instance))));
        println(strcat("return value is:", str(result)));
    }
}

2. 測量方法執行耗時


@BTrace public class TraceMethodExecuteTime {
    @TLS static long beginTime;

    @OnMethod(clazz="CaseObject", method="execute")
    public static void traceExecuteBegin() {
        beginTime = timeMillis();
    }

    @OnMethod(clazz="CaseObject", method="execute", location=@Location(Kind.RETURN))
    public static void traceExecute(int sleepTime, @Return boolean result) {
        println(strcat(strcat("CaseObject.execute time is:", str(timeMillis() - beginTime)), "ms"));
    }
}

3. 定位方法調用者(輸出調用堆疊)


@BTrace public class TraceMethodCallee {
    @OnMethod(clazz="CaseObject", method="execute")
    public static void traceExecute() {
        println("who call CaseObject.execute :");
        jstack();
    }
}

4. 追蹤指定行號執行情況


@BTrace public class TraceMethodLine {
    @OnMethod(clazz="CaseObject", location=@Location(value=Kind.LINE, line=5))
    public static void traceExecute(@ProbeClassName String pcn, @ProbeMethodName String pmn, int line) {
        println(strcat(strcat(strcat("call ", pcn), "."), pmn));
    }
}

進階技巧與注意事項

  • 條件過濾:在腳本中用 if 判斷參數值/線程名/堆疊特徵,僅輸出關注案例,避免輸出洪水。
  • 正則匹配方法:method="/get.*/" 攔截所有 get 開頭方法。
  • 字段訪問:get(field("類名", "字段名"), instance) 可讀取私有字段。
  • 安全性:BTrace 運行於沙箱,禁止副作用操作(無 new、無 IO、無同步、無循環),僅允許讀取與輸出。
  • 性能影響:極低,僅在攔截點執行輕量邏輯,生產環境可安全使用。

更多資源

詳細語法與 API 請參閱 BTrace User Guide。掌握 BTrace,讓線上疑難雜症無處遁形。

 

很多時候在online的應用出現問題時,很多時候我們需要知道更多的程序的運行細節,但又不可能在開發的時候就把程序中所有的運行細節都打印到日志上,通常這個時候能采取的就是修改代碼,重新部署,然後再觀察,但這種方法對於online應用來說不是很好,另外一方面如果碰到不好改的代碼,例如引用的其他的外部的包什麼的,就很麻煩了,BTrace就是一個可以在不改代碼、不重啟應用的情況下,動態的查看程序運行細節的工具,其官方網站在此:https://kenai.com/projects/btrace/,在這篇blog中,就來看看如何用BTrace來動態的監測方法的一些運行細節狀況。
BTrace通過動態的掛接用java寫的代碼到運行時上來獲取到一些運行細節,例如典型的使用btrace的方法為:
btrace -cp [btrace的jar所在的路徑,默認為btrace/build下] [pid] [需要運行的java代碼]
例如一段這樣的代碼:
import java.util.Random;
public class Case1{

public static void main(String[] args) throws Exception{
Random random=new Random();
CaseObject object=new CaseObject();
boolean result=true;
while(result){
result=object.execute(random.nextInt(1000));
Thread.sleep(1000);
}
}

}
public class CaseObject{

私有的靜態int sleepTotalTime=0;

public boolean execute(int sleepTime) throws Exception{
System.out.println("sleep: "+sleepTime);
sleepTotalTime+=sleepTime;
Thread.sleep(sleepTime);
return true;
}

}


如在程序運行的情況下,想知道調用CaseObject的execute方法的以下幾種情況,在BTrace中可以這麼做:
1、調用此方法時傳入的是什麼參數,返回的是什麼值,當時sleepTotalTime是多少?
BTrace腳本如下:
import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;

@BTrace public class TraceMethodArgsAndReturn{
@OnMethod(
clazz="CaseObject",
method="execute",
location=@Location(Kind.RETURN)
)
public static void traceExecute(@Self CaseObject instance,int sleepTime,@Return boolean result){
println("call CaseObject.execute");
println(strcat("sleepTime is:",str(sleepTime)));
println(strcat("sleepTotalTime is:",str(get(field("CaseObject","sleepTotalTime"),instance))));
println(strcat("return value is:",str(result)));
}
}
 

然後直接執行btrace -cp btrace/build [pid] TraceMethodArgsAndReturn.java就可以了。
當程序中調用到caseobject的execute方法時,就會在btrace的console中輸出相應的信息。
2、execute方法執行耗時是多久?
BTrace腳本如下:
import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;

@BTrace public class TraceMethodExecuteTime{

@TLS static long beginTime;

@OnMethod(
clazz="CaseObject",
method="execute"
)
public static void traceExecuteBegin(){
beginTime=timeMillis();
}

@OnMethod(
clazz="CaseObject",
method="execute",
location=@Location(Kind.RETURN)
)
public static void traceExecute(int sleepTime,@Return boolean result){
println(strcat(strcat("CaseObject.execute time is:",str(timeMillis()-beginTime)),"ms"));
}
}
 

3、誰調用了execute方法?
BTrace腳本如下:
import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;

@BTrace public class TraceMethodCallee{
@OnMethod(
clazz="CaseObject",
method="execute"
)
public static void traceExecute(){
println("who call CaseObject.execute :");
jstack();

}

}

 

4、有沒有人調用CaseObject中的哪一行代碼?
BTrace腳本如下:
import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;

@BTrace public class TraceMethodLine{
@OnMethod(
clazz="CaseObject",
location=@Location(value=Kind.LINE,line=5)
)
public static void traceExecute(@ProbeClassName String pcn,@ProbeMethodName String pmn,int line){
println(strcat(strcat(strcat("call ",pcn),"."),pmn));
}
}
 

從上面可看出,在有了BTrace後,要動態的跟蹤代碼的運行細節還是非常爽的,更多的細節的操作請大家查看BTrace的User Guide。
 

 
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved