博客地址: dim.red
项目开源: DynamicLog
背景
当线上问题出现的时候. 往往需要之前的埋点进行问题定位. 但是埋点个数是有限的. 现有的埋点往往不够排查出具体的问题. 所以我们需要一个更灵活的埋点方案. 要求是可以动态的输出任何方法的入参和出参.
原理
拦截原有的业务方法的入口和出口.
原本:
public class O {
private static final String TAG = "O";
public int method1(int i) {
Log.d(TAG, "method1() called with: i = [" + i + "]");
return i * i;
}
}
处理后:
public class O {
private static final String TAG = "O";
public static volatile MethodMonitor s_Monitor_1;
public int method1(int i) {
//方法开始
int methodId = 11;
Point point = null;
if (s_Monitor_1 != null && s_Monitor_1.hotMethodEnter(methodId)) {
point = new Point();
point.setThisObject(this);
Object[] var2 = new Object[0];
point.setArg(var2);
s_Monitor_1.methodEnter(point);
}
//原有方法执行
Log.d(TAG, "method1() called with: i = [" + i + "]");
int result = i * i;
//方法结束
if (s_Monitor_1 != null && s_Monitor_1.hotMethodReturn(0)) {
if (point == null) {
point = new Point();
point.setThisObject(this);
Object[] var3 = new Object[0];
point.setArg(var3);
}
point.setReturnObject((Integer)result);
s_Monitor_1.methodReturn(point, methodId);
}
return result;
}
}
做法:
为所有的 class 分配一个静态的 s_Monitor_1 对象. 同时为 class 下所有的方法分配一个独有的 methodId . 当 s_Monitor_1 对象不为空, methodId 匹配即命中规则.
编译过程过程:
插件
Monitor Plugin
在编译期间进行字节码注入. 同时生成 monitorMapping.txt 用来描述方法和 methodId
的映射关系.
注: 在字节码过程中我们发现有些方法是不需要注入的. 比如 abstract , 桥接方法 和 access$方法.
服务端可以通过 mapping.txt 找到混淆以后的类名,再通过 monitorMapping.txt 获取到 methodId 两则组合成命令往客户端发送.
尾巴
是用热修复的思想来做做动态日志.