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]));
}
}
}