真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

java用BTrace實(shí)現(xiàn)在線動態(tài)診斷

這篇文章主要講解了“java用BTrace實(shí)現(xiàn)在線動態(tài)診斷”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“java用BTrace實(shí)現(xiàn)在線動態(tài)診斷”吧!

銅鼓ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:028-86922220(備注:SSL證書合作)期待與您的合作!

一句話概括:BTrace是一個是強(qiáng)大的java線上應(yīng)用檢測工具(動態(tài)追蹤工具),可以在不修改應(yīng)用代碼,不停應(yīng)用服務(wù)的前提下檢測代碼運(yùn)行情況,進(jìn)而診斷問題,是生產(chǎn)環(huán)境下必備神器,本文將對它的使用進(jìn)行講解。

1 引言

BTrace是一款開源軟件,github地址為:https://github.com/btraceio/btrace,官網(wǎng)的介紹是BTrace is a safe, dynamic tracing tool for the Java platform.,它是安全的動態(tài)追蹤java應(yīng)用的工具,即可以動態(tài)地向目標(biāo)應(yīng)用的字節(jié)碼注入追蹤代碼。何為動態(tài)?我們都知道,即在java應(yīng)用啟動的時候會把class文件加載到JVM運(yùn)行,此時class代碼功能是確定、靜態(tài)的(無法變更),要想修改,只能是修改代碼,重新編譯、部署、啟動。

而在處理線上應(yīng)用時,我們經(jīng)常需要查看代碼運(yùn)行情況,參數(shù)值、返回值查看,或者添加自己需要調(diào)試的日志等,在開發(fā)階段,添加日志,重新啟動沒有問題,但在生產(chǎn)環(huán)境就不適用了(生產(chǎn)環(huán)境一般不輕易關(guān)停服務(wù),而且即使可以重啟,可能發(fā)生問題的現(xiàn)場就破壞了,無法重現(xiàn)問題),那么是否有方法在java應(yīng)用運(yùn)行期間,不重啟程序的情況,動態(tài)加入自己想要監(jiān)測(追蹤)的內(nèi)容?Btrace就是這樣一個動態(tài)追蹤神器,可以在不用重啟的情況下監(jiān)控應(yīng)用運(yùn)行情況,可以獲取程序運(yùn)行時的數(shù)據(jù)信息,如方法參數(shù)、返回值、全局變量和堆棧信息等。本文就是對BTrace進(jìn)行運(yùn)行原理和使用進(jìn)行描述。

2 BTrace運(yùn)行原理

2.1 class文件的動態(tài)修改替換

BTrace是基于java的動態(tài)追蹤技術(shù)來實(shí)現(xiàn)的。對于java開發(fā)人員,都清楚java程序的開發(fā)流程是寫java代碼,把它編譯為class文件,然后在JVM中加載class運(yùn)行。若此時想要在不停止應(yīng)用的情況下對class進(jìn)行修改來添加追蹤內(nèi)容,如在某個方法(method)中添加輸出信息,主要是兩件事情:

  • (1)修改已經(jīng)加載到JVM中的class,添加自定義輸出

  • (2)替換運(yùn)行在JVM`中的class

第一步,修改,由于JVM運(yùn)行的都是class文件,是不是可以直接修改字節(jié)碼class文件就行了(當(dāng)然,字節(jié)碼文件的可讀性遠(yuǎn)遠(yuǎn)沒有Java代碼高),但是已經(jīng)有相應(yīng)的框架可以做這件事,就是ASM,利用這框架,可以直接編輯字節(jié)碼的框架,它也提供接口可以讓我們方便地操作字節(jié)碼文件,進(jìn)行注入修改類的方法,動態(tài)創(chuàng)造一個新的類等等。Spring就是使用這種技術(shù)來實(shí)現(xiàn)動態(tài)代理的。

第二步,替換,如果對它進(jìn)行替換,則需要用到j(luò)ava提供的java.lang.instrument.Instrumentation,它有兩個接口redefineClassesretransformClasses,redefineClasses是自己提供字節(jié)碼文件替換掉已存在的class文件,retransformClasses是在已存在的字節(jié)碼文件上修改后再替換。不過需要注意的是instrument的使用有限制的(不能添加、修改、刪除已經(jīng)有字段和方法,不能改變方法簽名,改變繼承屬性等):

The redefinition must not add, remove or rename fields or methods, change the signatures of methods, or change inheritance

2.2 BTrace的模塊與運(yùn)行流程

BTrace就是基于前面的技術(shù)來實(shí)現(xiàn)的,文章《Java動態(tài)追蹤技術(shù)探究》(https://mp.weixin.qq.com/s/_hSaI5yMvPTWxvFgl-UItA)對動態(tài)追蹤技術(shù)進(jìn)行了詳細(xì)說明,下面簡要說明一下。

2.2.1 主要模塊

  • BTrace腳本:利用BTrace定義的注解,我們可以很方便地根據(jù)需要進(jìn)行腳本的開發(fā)。

  • Compiler:將BTrace腳本編譯成BTrace class文件。

  • Client:將class文件發(fā)送到Agent。

  • Agent:基于Java的Attach API,Agent可以動態(tài)附著到一個運(yùn)行的JVM上,然后開啟一個BTrace Server,接收client發(fā)過來的BTrace腳本;解析腳本,然后根據(jù)腳本中的規(guī)則找到要修改的類;修改字節(jié)碼后,調(diào)用Java Instrumentretransform接口,完成對對象行為的修改并使之生效。

2.2.2 運(yùn)行流程

運(yùn)行流程圖如下:

java用BTrace實(shí)現(xiàn)在線動態(tài)診斷

跟java源碼一樣,先編寫Btrace腳本(也是java文件),編譯(compiler),通過client發(fā)送給agentagent通過attach api添加到JVM并啟動agent server來接收client發(fā)送過來的內(nèi)容,然后底層是使用ASM修改字節(jié)碼文件,之后使用Java Instrumentretransform接口替換修改后的class文件,運(yùn)行后的輸出再通過agent發(fā)送到client進(jìn)行顯示。

3 BTrace安裝

知道了BTrace的運(yùn)行原理,現(xiàn)在可以安裝實(shí)踐一下。本文用的示例還是java-monitor-example。BTrace的安裝很簡單,開箱即用。

  • 下載地址(當(dāng)前最新版本是[v1.3.11.3]):https://github.com/btraceio/btrace/releases

  • 解壓到需要監(jiān)測的java應(yīng)用所在服務(wù)器

  • btrace的命令在bin目錄 下

  • 若需要在任意目錄可執(zhí)行,需要把btrace設(shè)置到環(huán)境變量中(export)

4 BTrace適用場景

基本上,BTrace只適用于動態(tài)追蹤類的輸出信息,不能添加屬性、刪除方法,修改繼承等,這跟前面提到的Instrument的限制是一致的。一般來說,使用Btrace進(jìn)行線上應(yīng)用監(jiān)測,基于都屬于日志輸出類,多數(shù)包括以下幾大場景:

  • 查看某一個方法中入?yún)⒑头祷刂?/p>

  • 查看某一個方法的響應(yīng)時間

  • 查看某行代碼是否有執(zhí)行到

  • 打印系統(tǒng)參數(shù)或JVM啟動參數(shù)

  • 打印方法調(diào)用的線程堆棧

  • 出現(xiàn)異常時打印出現(xiàn)異常信息

5 BTrace使用

Btrace作為一個獨(dú)立運(yùn)行的工具,默認(rèn)只能在本地運(yùn)行,也就是說,想要監(jiān)測哪個正在運(yùn)行的java應(yīng)用,就需要把它解壓到對應(yīng)的服務(wù)器。本示例中運(yùn)行的是java-monitor-example作為需要監(jiān)測的java應(yīng)用,然后就是根據(jù)監(jiān)測業(yè)務(wù)需求,寫腳本,運(yùn)行腳本,查看輸出了。

5.1 腳本編寫

5.1.1 注解與BTraceUtils

Btrace的腳本與編寫java代碼無異,不過相對簡單很多,主要是使用Btrace提供的注解和BTraceUtils,注解用于告訴Btrace需要攔截的類、攔截時機(jī)、攔截位置等,BTraceUtils用于提供打印輸出種信息的功能。如官網(wǎng)給出的示例如下:

package samples;

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

/**
 * This script traces method entry into every method of 
 * every class in javax.swing package! Think before using 
 * this script -- this will slow down your app significantly!!
 */
@BTrace public class AllMethods {
    @OnMethod(
        clazz="/javax\\.swing\\..*/",
        method="/.*/"
    )
    public static void m(@ProbeClassName String probeClass, @ProbeMethodName String probeMethod) {
        print(Strings.strcat("entered ", probeClass));
        println(Strings.strcat(".", probeMethod));
    }
}

以上代碼,表示,會攔截所有調(diào)用以javax.swing開頭的方法,然后打印出類名和方法名??梢宰⒁獾阶⒔庥?code>@BTrace、@OnMethod、@ProbeClassName,@ProbeMethodName,而printprintlnBTraceUtils提供的靜態(tài)方法。BTraceUtils還提供了很多打印方法(后面示例會提到)。另外,還要注意的是跟蹤操作都需要在靜態(tài)方法體內(nèi)指定,因此都需要static方法。

另外,關(guān)于BTrace提供的注解,詳細(xì)可以參考官方文檔(https://github.com/btraceio/btrace/wiki/BTrace-Annotations)。主要包括以下:

/**Class Annotations*/
@com.sun.btrace.annotations.DTrace
@com.sun.btrace.annotations.DTraceRef
@com.sun.btrace.annotations.BTrace
/**Method Annotations*/
@com.sun.btrace.annotations.OnMethod
@com.sun.btrace.annotations.OnTimer
@com.sun.btrace.annotations.OnError
@com.sun.btrace.annotations.OnExit
@com.sun.btrace.annotations.OnEvent
@com.sun.btrace.annotations.OnLowMemory
@com.sun.btrace.annotations.OnProbe
/**Argument Annotations*/
@com.sun.btrace.annotations.Self
@com.sun.btrace.annotations.Return
@com.sun.btrace.annotations.CalledInstance
@com.sun.btrace.annotations.CalledMethod
/**Field Annotations*/
@com.sun.btrace.annotations.Export
@com.sun.btrace.annotations.Property
@com.sun.btrace.annotations.TLS

其中,@OnMethod用得比較多,需要重點(diǎn)說明一下,它主要是三個屬性clazz,methodlocation。

  • clazz:類的全路徑名,如me.mason.monitor.controller.UserController

  • method:要監(jiān)測的方法名,如getUsers

  • location:攔截時機(jī),使用@Location注解。

@Location又有以下幾種:

  • Kind.ENTRY:在進(jìn)入方法時調(diào)用

  • Kind.RETURN:方法執(zhí)行完時調(diào)用,只有把攔截位置定義為Kind.RETURN,才能獲取方法的返回結(jié)果@Return和執(zhí)行時間@Duration

  • Kind.CALL:方法中調(diào)用其它方法時調(diào)用

  • Kind.LINE:通過設(shè)置line,可以監(jiān)控代碼是否執(zhí)行到指定的位置

  • Kind.ERROR, Kind.THROW, Kind.CATCH:異常情況的跟蹤

5.1.2 關(guān)于編寫

建議還是使用java的maven項(xiàng)目的開發(fā)環(huán)境進(jìn)行編寫,可以使用代碼提示功能。寫好后再放到對應(yīng)需要監(jiān)測的服務(wù)器中。不過編輯時需要引用對應(yīng)的jar包(btrace-agent,btrace-boot,btrace-client),對應(yīng)的jar在下載的安裝下的build目錄下。通過pom.xml引入即可使用。如下所示:



    com.sun.btrace
    btrace-agent
    1.3.11.3
    jar
    system
    E:/btrace-bin-1.3.11.3/build/btrace-agent.jar


    com.sun.btrace
    btrace-boot
    1.3.11.3
    jar
    system
    E:/btrace-bin-1.3.11.3/build/btrace-boot.jar


    com.sun.btrace
    btrace-client
    1.3.11.3
    jar
    system
    E:/btrace-bin-1.3.11.3/build/btrace-client.jar

5.2 腳本運(yùn)行

打印幫助信息如下:

java用BTrace實(shí)現(xiàn)在線動態(tài)診斷

一般來說,在服務(wù)器上,直接是btrace PID btraceFile.java,然后查看輸出(也可以把內(nèi)容輸出到文件中再查看,如btrace PID btraceFile.java > info.txt)。如果有使用到特定的jar包,則需要把參數(shù)cpclasspath加上。如下示例是把調(diào)用方法的返回值進(jìn)行輸出:

java用BTrace實(shí)現(xiàn)在線動態(tài)診斷

5.3 腳本示例

下面通過幾個常用的示例來說明一下BTrace腳本的使用,腳本在示例工程java-monitor-example中的btrace目錄下。java-monitor-example中,分別是一個controllerservice,有如下方法定義,下面會根據(jù)這些方法進(jìn)行動態(tài)追蹤。

/**
  * UserController.java
  **/
@GetMapping("/user")
public ResponseResult getUser() {
    User user = userService.getUser();
    return ResponseResult.ok(user);
}

@GetMapping("/users")
public ResponseResult getUsers(int num) {
    List users = userService.getUsers(num);
    return ResponseResult.ok(users);
}

/**
  * UserService.java
  * 根據(jù)ID獲取用戶
  *
  * @return
  */
public User getUser() {
    return mockUser();
}

/**
  * 獲取用戶數(shù)組
  *
  * @return
  */
public List getUsers(int num) {
    userList.clear();
    for(int i=0 ; i < num; i++){
        userList.add(mockUser());
    }
    return userList;
}

5.3.1 打印方法相關(guān)信息

  • 打印調(diào)用方法時的參數(shù)(調(diào)用UserControllergetUsers方法時打印)

@OnMethod(clazz = "me.mason.monitor.controller.UserController"
          ,method = "getUsers",location = @Location(Kind.ENTRY))
public static void readFunction(@ProbeClassName String className, @ProbeMethodName String methodName, AnyType[] args) {
    // 打印時間
    BTraceUtils.println(BTraceUtils.Time.timestamp("yyyy-MM-dd HH:mm:ss"));
    BTraceUtils.println("method controller");
    BTraceUtils.printArray(args);
    BTraceUtils.println(className + "," + methodName);
    BTraceUtils.println("==========================");
}
  • 打印調(diào)用方法時的返回值

@OnMethod(clazz = "me.mason.monitor.service.UserService"
,method = "getUsers",location = @Location(Kind.RETURN))
public static void printReturnData1(@Return AnyType result){
    BTraceUtils.println(BTraceUtils.Time.timestamp("yyyy-MM-dd HH:mm:ss"));
    BTraceUtils.printFields(result);
    BTraceUtils.println("==========================");
    BTraceUtils.println(BTraceUtils.str(result));
    BTraceUtils.println("==========================");
}
  • 執(zhí)行到的行數(shù)(查看是否執(zhí)行到UserService的39行)

@OnMethod(clazz = "me.mason.monitor.service.UserService"
,method = "getUsers",location = @Location(value = Kind.LINE,line = 39))
public static void printLineData(@ProbeClassName String className, @ProbeMethodName String methodName,int line){
    BTraceUtils.println(BTraceUtils.Time.timestamp("yyyy-MM-dd HH:mm:ss"));
    BTraceUtils.println(className + "," + methodName + ","+line);
    BTraceUtils.println("==========================");
 }
  • 執(zhí)行方法的用時(UserControllergetUsers方法用時多長)

@OnMethod(clazz = "me.mason.monitor.controller.UserController"
,method = "getUsers",location = @Location(Kind.RETURN))
public static void getUsersDuration(@Duration long duration){
    BTraceUtils.println(BTraceUtils.Time.timestamp("yyyy-MM-dd HH:mm:ss"));
    BTraceUtils.println("time(ns):" + duration);
    BTraceUtils.println("time(ms):" + BTraceUtils.str(duration / 1000000));
    BTraceUtils.println("time(s):" + BTraceUtils.str(duration / 1000000000));
    BTraceUtils.println("==========================");
}

5.3.2 打印系統(tǒng)屬性及JVM屬性

類似JDK的命令行工具jinfo,另外jmapjstatck可查詢官方示例。

@BTrace
public class JInfo {
    static {
        println("System Properties:");
        printProperties();
        println("VM Flags:");
        printVmArguments();
        println("OS Enviroment:");
        printEnv();
        exit(0);
    }
}

5.3.3 打印異常輸出

java開發(fā)人員應(yīng)該都知道,java的異常分為ErrorException,而它們都是Throwable的子類,即java中所有異常的父類都Throwable,因此追蹤這個的構(gòu)造函數(shù),然后把堆棧打印出來即可。如下:

//局部變量存儲異常
@TLS static Throwable currentException;
//異常構(gòu)造函數(shù)開始
@OnMethod(
    clazz="java.lang.Throwable",
    method=""
)
public static void onthrow(@Self Throwable self) {
    currentException = self;
}
//異常構(gòu)造函數(shù)結(jié)束,輸出堆棧
@OnMethod(
    clazz="java.lang.Throwable",
    method="",
    location=@Location(Kind.RETURN)
)
public static void onthrowreturn() {
    if (currentException != null) {
        Threads.jstack(currentException);
        println("=====================");
        currentException = null;
    }
}

5.4 腳本限制

BTrace對JVM來說是“只讀的”,BTrace要做的是,雖然修改了字節(jié)碼,但是主要是輸出需要的信息,對整個程序的正常運(yùn)行并沒有影響。需要注意的是,由于是動態(tài)替換class文件,被修改的字節(jié)碼是不會自動還原的。官方文檔也有說明,BTrace腳本會有以下限制:

  • 不允許創(chuàng)建對象

  • 不允許創(chuàng)建數(shù)組

  • 不允許拋異常

  • 不允許catch異常

  • 不允許隨意調(diào)用其他對象或者類的方法,只允許調(diào)用com.sun.btrace.BTraceUtils中提供的靜態(tài)方法(一些數(shù)據(jù)處理和信息輸出工具)

  • 不允許改變類的屬性

  • 不允許有成員變量和方法,只允許存在static public void方法

  • 不允許有內(nèi)部類、嵌套類

  • 不允許有同步方法和同步塊

  • 不允許有循環(huán)

  • 不允許隨意繼承其他類(當(dāng)然,java.lang.Object除外)

  • 不允許實(shí)現(xiàn)接口

  • 不允許使用assert

  • 不允許使用Class對象

6 一些經(jīng)驗(yàn)

  • 搭建使用java的maven項(xiàng)目的開發(fā)環(huán)境進(jìn)行腳本編寫,引入相應(yīng)的jar,以提供代碼提示功能。

  • 查看官方提供的例子,在下載包中已提供例子,位置:btrace-bin-1.3.11.3\samples目錄

  • BTrace腳本中追蹤的輸入?yún)?shù),返回值類型是簡單類型直接使用(如int ,float等),復(fù)雜類型可以使用AnyType,但如果是使用自定義包中的類型(如User),則需要運(yùn)行腳本時添加cpclasspath參數(shù),指定自定義包。

  • 一般簡單類型或字符串,直接使用printprintln,打印對象屬性可使用printFields,打印List,可以使用BTraceUtils.println(BTraceUtils.str(list))

  • 在探查方法的最后一行打印分隔,強(qiáng)烈建議。可能是由于輸出有緩沖區(qū)延遲,如果不輸出分隔,有可能會無法輸出或者輸出后內(nèi)容沒有分隔。分隔可使用BTraceUtils.printlnBTraceUtils.println("============")。

7 總結(jié)

對于線上的java應(yīng)用,如果想不停服務(wù)進(jìn)行日志輸出來診斷問題,動態(tài)追蹤技術(shù)是必不可少的技術(shù),而Btrace是使用此技術(shù)來實(shí)現(xiàn)動態(tài)追蹤的有力工具。本文從Btrace的運(yùn)行原理、安裝、適用場景、腳本編寫、運(yùn)行等方面進(jìn)行了詳細(xì)描述,希望可以幫助大家加深Btrace的了解,更方便、有效率地解決線上問題。

感謝各位的閱讀,以上就是“java用BTrace實(shí)現(xiàn)在線動態(tài)診斷”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對java用BTrace實(shí)現(xiàn)在線動態(tài)診斷這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!


分享名稱:java用BTrace實(shí)現(xiàn)在線動態(tài)診斷
文章地址:http://weahome.cn/article/jhshos.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部