Btrace 进阶学习

Btrace 使用方法

设置环境变量 (BTRACE\_HOME) ,并 将(bin) 添加到PATH,随后在命令行输出以下命令即可运行。

btrace pid fileName.java 

或者将 (fileName.java) 放在 (/brace/bin) 目录下运行

进阶细节

1.若监测的类为接口类,则需要在类名前加 (+) ,例如:

+cn.freemethod.business.MyInterface


2.若监测的为注解,则需在注解名前加 (@) ,例如:

@javax.annotation.Resource


3.若要使用正则表达式,需要将类名或者方法名写在两个斜杠中间,如:

"/com\.ewan\..+/"

注意斜杠要被转义,所以特殊符号前要两个斜杠



4.若要指定监测方法的类型,可以在 (@OnMethod) 注解中使用 (type~=~...) 进行指定,也可以指定监测的位置,使用 (location= @Location(value = ...)) 指定,例如:

    @OnMethod(
            clazz = "/com\.mfp\.business\.cfg\.service\..+/",
            method = "/s.*/",
            type = "void (com.business.net.protocol.vo.RequestObject, com.business.net.protocol.vo.ResponseObject)",
            location = @Location(value = Kind.RETURN)
    )


5.(@Self 注释的this, 完整的参数列表,@Return 注释的返回值) 若要传入这三者,则一定要按照这样的顺序排列,若不需要可以省略,但参数列表一定要完整,否则无法定位到你要监测的函数



6.假如你要监测的类集合的传入参数或返回值不统一,则可以使用 (AnyType[]~args) 方法传入,返回值使用 (AnyType) ,例如:

    @OnMethod(clazz = "/cn\.freemethod\.business\..+/", method = "/.+/", location = @Location(value = Kind.RETURN))
    public static void dataCaptrue(@ProbeClassName String className, @ProbeMethodName String methodName, AnyType[] args){
        println(className);
        println(methodName);
        if(args.length > 0)
            printFields(args[0]);
        //printArray(args);
    }

若要打印参数列表,则使用 (printArray(args)) , 若 (args) 含有非JAVA基本类型,则会输出类名,不会输出实例。



7.若你要将监测到的结果输入到文件,只需加上 (-o~mydata.log) 参数即可,例如:

btrace -o mydata.log 17272 MyBtrace.java

建议不要使用此方法,这种方法产生的 mydata.log 会产生在你监测的 (pid) 的应用程序的启动目录下,而不是在你的脚本的目录下
实测!若你使用过 (-o) 方法运行过 (btrace~pid) 那么无论你是否加 (-o) 参数,(btrace) 都会默认输出到 (mydata.log) 中,这就导致无论怎么样你的 (console) 都不会有输出,为了解决这种情况,必须得重启整个项目。
因此最好的输出到文件的方法应该是使用liunx的重定向:

btrace 17272 MyBtrace.java > mydata.log


8.若你传入的参数不是JAVA基本类型,而是自定义的类,则需要用 (AnyType[]~args) 先传入,然后再通过 (printFields(args[0])) 方法来反射得到这个类,JAVA 中 (Object) 的继承类输出是 (JSON) 格式。



打印复杂类

有些同学可能会发现有时候如果你要监测的类里面有嵌套的类,(printFields(args[0])) 打印出来会是类对象地址(引用),而btrace中不允许调用其他类方法,只能使用包(com.sun.btrace.BTraceUtils) 中自带的方法,经过查找资料,发现 (com.sun.btrace.BTraceUtils) 中自带类解析相关的方法,例如: (classOf) , (field) 等方法。


还有一种打印复杂类的方法就是重写监测类的 (toString()) 方法。但需要监测的类很多时,这种方法不太实用。而且这种方法存在一种情况无法处理,经过查看 (BtraceUtils) 源代码发现:

public static String str(Object obj) {
      if (obj == null) {
        return "null";
      }
      else if (obj instanceof String) {
        return (String) obj;
      }
      else if (obj.getClass().getClassLoader() == null) {
        try {
          return obj.toString();
        } catch (NullPointerException e) {
          // NPE can be thrown from inside the toString() method we have no control over
          return "null";
        }
      } else {
        return identityStr(obj);
      }
}

private static void addFieldValues(
      StringBuilder buf, Object obj, Class<?> clazz, boolean classNamePrefix) {
    Field[] fields = getAllFields(clazz);
    for (Field f : fields) {
      int modifiers = f.getModifiers();
      if (!Modifier.isStatic(modifiers)) {
        if (classNamePrefix) {
          buf.append(f.getDeclaringClass().getName());
          buf.append('.');
        }
        buf.append(f.getName());
        buf.append('=');
        try {
          buf.append(Strings.str(f.get(obj)));
        } catch (Exception exp) {
          throw translate(exp);
        }
        buf.append(", ");
      }
    }
    Class<?> sc = clazz.getSuperclass();
    if (sc != null) {
      addFieldValues(buf, obj, sc, classNamePrefix);
    }
  }

public static void printFields(Object obj) {
    Reflective.printFields(obj, false);
}
public static void printFields(Object obj, boolean classNamePrefix) {
    Reflective.printFields(obj, classNamePrefix);
}


也就是说,只有当你的类对象满足 $obj.getClass().getClassLoader() == null$ 才会调用 $ toString $ 方法,因此必须得保证这个类是自己写的类而不是引入的外部类(即Extension Loader中加载的class)。

下面是我trace服务器项目中 (RequestObject)(ResponseObject) 类的方法,其中两者中都含有 (JSONObject) 类,因此要解析 (JSONObject) 中的内容必须得一层一层的进行 (field) 反射

import com.sun.btrace.AnyType;
import com.sun.btrace.annotations.*;

import java.lang.reflect.Field;
import java.lang.Class;

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

@BTrace
public class OnMethodTrace {

    @OnMethod(
            clazz = "/cn\.freemethod\.business\..+/",
            method = "/.*/",
            type = "void (cn.freemethod.business.RequestObject, cn.freemethod.business.ResponseObject)",
            location = @Location(value = Kind.RETURN)
    )
    public static void dataTrace(@ProbeClassName String className, @ProbeMethodName String methodName, AnyType[] args) {
        println(className);
        println(methodName);
        /*
         * RequestObject
         */
        Class cl = classOf(args[0]);
        Field fd = field(cl, "cpv", false);
        if(fd != null) {
            println(str(get(fd, args[0])));
        }
        fd = field(cl, "jsonObject", false);
        if(fd != null) {
            printFields(get(fd, args[0]));
        }

        /*
         * ResponseObject
         */
        cl = classOf(args[1]);
        fd = field(cl, "jsonObject", false);
        if(fd != null) {
            printFields(get(fd, args[1]));
        }
    }
}

原文地址:https://www.cnblogs.com/Yuzao/p/14592342.html