美文网首页
Gain Running State by Btrace Scr

Gain Running State by Btrace Scr

作者: whthomas | 来源:发表于2017-05-28 22:59 被阅读173次

    Btrace在github上对自己的介绍是:

    BTrace is a safe, dynamic tracing tool for the Java platform.
    

    Btrace是一款利用了Java动态织入技术来追踪已经部署在线上的应用信息状态的工具。

    在线上因为应用已经被部署,一旦发生故障想排查应用里面的信息非常困难,这个时候停掉服务再去追加一些日志也不合适,而Btrace则可以用脚本和应用的进程id获取用户自己希望得到的信息。

    .______   .___________..______          ___       ______  _______  __  
    |   _  \  |           ||   _  \        /   \     /      ||   ____||  | 
    |  |_)  | `---|  |----`|  |_)  |      /  ^  \   |  ,----'|  |__   |  | 
    |   _  <      |  |     |      /      /  /_\  \  |  |     |   __|  |  | 
    |  |_)  |     |  |     |  |\  \----./  _____  \ |  `----.|  |____ |__| 
    |______/      |__|     | _| `._____/__/     \__\ \______||_______|(__) 
                                                                           
    

    安装btrace

    Btrace的源码在github上:https://github.com/btraceio/btrace

    截止这篇文章发布前,最新版本为1.3.9.

    下载之后,需要在环境变量中配置BTRACE_HOME,将btrace的安装目录赋值给BTRACE_HOME

    BTRACE_HOME=export BTRACE_HOME="/usr/local/btrace/"
    

    在命令行中输入btrace,能打印出以下内容,则说明安装成功:

    ➜  ~ btrace
    Usage: btrace <options> <pid> <btrace source or .class file> <btrace arguments>
    where possible options include:
      --version             Show the version
      -v                    Run in verbose mode
      -o <file>             The path to store the probe output (will disable showing the output in console)
    -u                    Run in unsafe mode
      -d <path>             Dump the instrumented classes to the specified path
      -pd <path>            The search path for the probe XML descriptors
      -classpath <path>     Specify where to find user class files and annotation processors
      -cp <path>            Specify where to find user class files and annotation processors
      -I <path>             Specify where to find include files
      -p <port>             Specify port to which the btrace agent listens for clients
      -statsd <host[:port]> Specify the statsd server, if any
    

    举个栗子

    假设我们有这样的一个web入口:

    package demo.spring.web.action;
    
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @author whthomas
     * @date 2017/5/28
     */
    @RestController
    @RequestMapping("/")
    public class MainController {
    
        @RequestMapping("/{name}")
        public String hello(@PathVariable("name") String name) {
            return "hello " + name;
        }
    
    }
    

    以上这段代码在线上运行之后,假设在不停服务的情况下来获取这个接口中的参数,就可以利用Btrace来截获信息。

    import com.sun.btrace.annotations.*;
    import static com.sun.btrace.BTraceUtils.*;
    
    @BTrace
    public class Sample{
        @OnMethod(
            clazz="demo.spring.web.action.MainController",
            method="hello",
            location= @Location(Kind.RETURN)
        )
        public static void func(@ProbeClassName String pcn,
                                @Duration long duration){
    
            // get the param from the method
            println("我抓到你了" + pcn + ",duration:" + duration);
        }
    }
    

    运行脚本时,按照格式执行脚本:

    btrace [Application's PID] Script.java

    就像这样,当MainControllerhello方法被调用到之后,脚本里面的内容就会被打印出来。

    ➜ btrace 29338 Sample.java
    我抓到你了demo.spring.web.action.MainController,duration:12354
    

    喜极而泣。

    Btrace的具体配置

    所有的Btrace脚本都不能忘了把@BTrace注解加到类的头部。然后透过以下这些配置,我们可以构建非常丰富的脚本来获取线上应用的信息。

    @OnMethod

    刚刚的Btrace脚本中的func()使用了一个OnMethod注解。

    在这个注解中可以定义这几个参数:

    • clazz:目标对象所在的类的类型
    • method:目前方法的方法名
    • type:
    • location:脚本执行的时间点

    clazz不仅仅能指定特定目标,还可以使用正则表达式,拦截一系列的函数。

    location参数中它可以通过@Location配置脚本被执行的时间点,比如:

    • @Location(Kind.RETURN): 在函数返回时执行
    • @Location(Kind.ENTER): 在函数进入时执行
    • @Location(value = Kind.LINE, line = 10): 函数被执行到第10行时触发
    • @Location(value = Kind.CALL, clazz = "/./", method = "/./", where = Where.AFTER): 查询当前被拦截函数中其他函数的调用情况

    Kind类中其实还有很多执行点,比如Kind.Error, Kind.Throw和 Kind.Catch异常抛出(Throw),异常被捕获(Catch),异常没被捕获被抛出函数之外(Error),主要用于对某些异常情况的跟踪。详见: https://github.com/btraceio/btrace/blob/master/src/share/classes/com/sun/btrace/annotations/Kind.java

    @OnTimer

    这个注解,被用于执行一些定时任务。它只接受一个参数:

    • interval:执行的时间间隔

    比如要求某个方法两秒被执行一次:

    @OnTimer(2000)
    public static void print() {
        // print the counter
        println("component count = " + count);
    }
    

    @OnTimer注解的函数和被@OnMethod注解的函数相结合,可以定期打印出某些函数的执行状态。

    @OnExit

    @OnExit注解的函数会在Btrace脚本停止的时候触发,而非被监控的进程停止。

    @OnExit
    public static void onexit(int code) {
        println("BTrace program exits!");
    }
    

    除了这种方法上的注解,在上面的例子中还能看到参数上可以使用的注解。

    @ProbeClassName

    获取被拦截的方法所属类的类名,因为有时候,@OnMethod会利用正则表达式拦截到一组方法。

    @ProbeMethodName

    获取被拦截方法的方法名,原因同上。

    @Duration

    @Duration注解的参数,会被注入函数执行的时间。如果要使用@Duration,被标记的参数只能是long类型,而且函数上需要被标记成location=@Location(Kind.RETURN)

    @Return

    @Return注解的参数,会被注入函数执行的返回值。跟@Duration一样,如果要使用@Return注解函数也需要被标记成location=@Location(Kind.RETURN)

    @Self

    @Self注解的参数表示被拦截的方法实例。可以通过被@Self注解的参数获取方法中的入参(param)

    JDK原有类非JDK类获取参数的方式还有一些区别,非JDK类获取参数需要先用类加载器(classLoader)将类加载出来。

    // JDK类
    Field fdFiled = field("java.io.FileInputStream", "fd");
    
    // 非JDK类
    Field customField = field(classForName("demo.spring.web.MyObject", contextClassLoader()), "customField");
    
    @OnMethod(
        clazz="demo.spring.web.action.MainController",
        method="hello",
        location= @Location(Kind.RETURN)
    )
    public static void func(@Self Object obj,
                            @ProbeClassName String pcn,
                            @Duration long duration){
    
        // get the param from the method
        println("拿到参数name:" + field("java.lang.String", "name"));
        println("我抓到你了" + pcn + ", duration:" + duration);
    }
    

    @TargetInstance 和 @TargetMethodOrField

    这两个注解需要放到一起才好理解,假设我们有如下两个类。

    class A {
        public void methodA(){
            // statement
        }
    }
    
    class B {
    
        A a = new A();
    
        public void methodB(){
            // statement
            a.methodA();
            
        }
    }
    

    Btrace中,我们构建这样的脚本:

    @BTrace
    public class Sample{
        @OnMethod(
            clazz="B",
            method="methodB",
            location= @Location(
                value = Kind.CALL,
                clazz = "/.*/", 
                method = "/.*/", 
                where = Where.AFTER
            )
        )
        public static void func(@Self self,
                                @TargetInstance targetInstance,
                                @TargetMethodOrField targetMethod, 
                                @Duration long duration){
    
            // get the param from the method
        }
    }
    
    • targetInstance表示在methodB()方法中其他引用类对象实例。
    • targetMethod表示在methodB()方法中其他对象实例执行的方法。

    值得注意的是,这两个注解都只能在这个配置下使用:

    location=@Location([Kind.CALL|Kind.FIELD_GET|Kind.FIELD_SET)

    当然生产环境中不建议查询一个方法下所有的调用方法,这样JVM的大概会被拖崩溃掉。

    相关文章

      网友评论

          本文标题:Gain Running State by Btrace Scr

          本文链接:https://www.haomeiwen.com/subject/iftkfxtx.html