Btrace监控调试基本教程

文章目录
  1. 1. 拦截方法
    1. 1.1. 普通方法
    2. 1.2. 构造函数
    3. 1.3. 同名函数,用参数区分
  2. 2. 拦截返回值、异常、行号
    1. 2.1. 拦截返回值
    2. 2.2. 拦截异常
    3. 2.3. 拦截行号

拦截方法

  • 普通方法
1
@OnMethod( clazz = "", method = "")

上一篇文章我们已经介绍,不清楚的小伙伴,可自行前往查看

  • 构造函数
1
@OnMethod( clazz = "", method = "<init>")

代码示例

新建User类

1
2
3
4
5
6
7
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String name;
private int age;
}

编写待追踪代码 ConstructorController.java

1
2
3
4
5
6
7
@RestController
public class ConstructorController {
@GetMapping("/constructor")
public User constructor(User user) {
return user;
}
}

编写追踪代码脚本 BtracePrintConstructorArgSimple.java

1
2
3
4
5
6
7
8
9
@BTrace
public class BtracePrintConstructorArgSimple {

@OnMethod(clazz = "com.gulj.monitor.domain.User", method = "<init>")
public static void andyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) {
BTraceUtils.printArray(args);
BTraceUtils.println("className=" + pcn + "," + "methodName=" + pmn);
}
}

运行追踪代码脚本

1
btrace pid BtracePrintConstructorArgSimple.java

发送请求

1
curl http://localhost:8080/constructor?name=gulj&age=27

执行命令 btrace pid BtracePrintConstructorArgSimple.java (一起处于监听状态,当我们发送请求,会有监听结果输出) 还有笔者在测试的过程中有的时候没有监测到输出,当遇到这种情况,有的时候是因为慢的缘故,有的时候还请小伙伴多执行几次请求

  • 同名函数,用参数区分

代码示例

编写待追踪代码 SameMethodController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
public class SameMethodController {

@GetMapping("/method1")
public String method(@RequestParam("name") String name) {
return name;
}

@GetMapping("/method2")
public String method(@RequestParam("name") String name, @RequestParam("age") int age) {
return name + age;
}

}

编写追踪代码脚本 BtracePrintConstructorArgSimple.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@BTrace
public class BtracePrintSameMethodArgSimple {

/**
* 注意,这个两个方法,除了最后的参数个数不同,其它都一样,读者在测试的时候可自行选择放开测试
*/

// curl:http://localhost:8080/method2?name=xxx&age=100
@OnMethod(clazz = "com.gulj.monitor.controller.SameMethodController", method = "method")
public static void method(@ProbeClassName String pcn, @ProbeMethodName String pmn,String name,int age) {
BTraceUtils.println("className=" + pcn + "," + "methodName=" + pmn + "," + "name=" + name +"," + "age=" + age);
}



/*
//访问:curl:http://localhost:8080/method1?name=xxx
@OnMethod(clazz = "com.gulj.monitor.controller.SameMethodController", method = "method")
public static void method(@ProbeClassName String pcn, @ProbeMethodName String pmn,String name) {
BTraceUtils.println("className=" + pcn + "," + "methodName=" + pmn + "," + "name=" + name );
}
*/

}

发送请求

1
curl:http://localhost:8080/method1?name=gulj&age=12

监控结果

通过笔者以上的介绍,小伙伴们想必已经知道btrace怎么玩了,下面为了避免篇幅过长,笔者只给出示例代码中的待追踪代码和追踪代码,小伙伴们自行运行查看效果。

拦截返回值、异常、行号

拦截返回值
拦截线上代码方法的返回值

编写待追踪代码 ReturnValueController.java

1
2
3
4
5
6
7
8
@RestController
public class ReturnValueController {

@GetMapping("/returnValue")
public String returnValue(@RequestParam("name") String name) {
return name;
}
}

编写追踪代码脚本 BtracePrintConstructorArgSimple.java

1
2
3
4
5
6
7
8
9
10
11
12
13
@BTrace
public class BtracePrintReturnValueSimple {

@OnMethod(
clazz = "com.gulj.monitor.controller.ReturnValueController",
method = "returnValue",
location = @Location(Kind.RETURN)
)
public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, @Return AnyType result) {
BTraceUtils.println("className=" + pcn + "," + "methodName=" + pmn + "," + "result=" + result);
}

}

使用 @Location(Kind.RETURN) 指定监控返回值的地方,使用 @Return AnyType result 存放监控的返回值

拦截异常
拦截线上代码指定方法即使吞并异常,仍能异常栈信息被监控输出

编写待追踪代码 OnThrowController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestController
public class OnThrowController {

@GetMapping("/exception")
public String exception() {
try {
//TODO 业务代码

int i = 1 / 0;

//TODO 业务代码
} catch (Exception e) {
e.printStackTrace();
}

return "success";
}

}

编写追踪代码脚本 BtracePrintOnThrowSimple.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@BTrace
public class BtracePrintOnThrowSimple {
/**
* 代码从 usersguide.html 中 copy btrace/samples/OnThrow.java
*/

// store current exception in a thread local
// variable (@TLS annotation). Note that we can't
// store it in a global variable!
@TLS
static Throwable currentException;

// introduce probe into every constructor of java.lang.Throwable
// class and store "this" in the thread local variable.
@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;
}

// when any constructor of java.lang.Throwable returns
// print the currentException's stack trace.
@OnMethod(
clazz = "java.lang.Throwable",
method = "<init>",
location = @Location(Kind.RETURN)
)
public static void onthrowreturn() {
if (currentException != null) {
BTraceUtils.Threads.jstack(currentException);
println("=====================");
currentException = null;
}
}
}

监控结果

虽然我们在程序中把异常吞闭了,但是通过btrace的异常监控脚本我们仍能输出异常栈信息,这无疑很好地帮助我们解决线上问题(btrace的异常监控脚本参考 usersguide.html 中的 btrace/samples/OnThrow.java文件)

拦截行号
拦截线上代码指定行是否被执行到