skywalking-agent初始化流程(二)-基于bytebuddy的增強(qiáng)機(jī)制

接上篇:
對(duì)于所有的skywalking-plugin.def配置文件中定義的agent插件,將通過(guò)如下代碼生效匹配規(guī)則和攔截邏輯。

agentBuilder
    .type(pluginFinder.buildMatch())//匹配規(guī)則:待增強(qiáng)的類
    .transform(new Transformer(pluginFinder))//根據(jù)匹配規(guī)則和增強(qiáng)邏輯創(chuàng)建Transformer:
    .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
    .with(new Listener())//回調(diào)接口,用來(lái)在debug模式下保存增強(qiáng)后的字節(jié)碼文件
    .installOn(instrumentation);

1.pluginFinder.buildMatch():匹配規(guī)則

//直接根據(jù)類名稱匹配的插件
private final Map<String, LinkedList<AbstractClassEnhancePluginDefine>> nameMatchDefine = new HashMap<String, LinkedList<AbstractClassEnhancePluginDefine>>();
//非名稱匹配:方法注解匹配类缤、類注解匹配等
private final List<AbstractClassEnhancePluginDefine> signatureMatchDefine = new ArrayList<AbstractClassEnhancePluginDefine>();
//返回bytebuddy元素匹配器,用戶判斷是否匹配
public ElementMatcher<? super TypeDescription> buildMatch() {
        ElementMatcher.Junction judge = new AbstractJunction<NamedElement>() {
            @Override
            public boolean matches(NamedElement target) {
                //直接根據(jù)類名稱匹配
                return nameMatchDefine.containsKey(target.getActualName());
            }
        };
        judge = judge.and(not(isInterface()));
        for (AbstractClassEnhancePluginDefine define : signatureMatchDefine) {
            ClassMatch match = define.enhanceClass();
            if (match instanceof IndirectMatch) {
                //其他規(guī)則匹配
                judge = judge.or(((IndirectMatch)match).buildJunction());
            }
        }
        return new ProtectiveShieldMatcher(judge);
    }

增強(qiáng)類匹配規(guī)則需要實(shí)現(xiàn)ClassMatch接口唤蔗,skywalking中預(yù)定義了一些常用匹配規(guī)則
ER圖如下:


image.png

NameMatch:直接根據(jù)類名稱匹配
ClassAnnotationMatch:類注解匹配
MethodAnnotationMatch:方法注解匹配
HierarchyMatch:父類或父接口匹配
MultiClassNameMatch:直接根據(jù)類名稱匹配(類名稱數(shù)組义锥、邏輯關(guān)系--或)

2.new Transformer(pluginFinder):根據(jù)匹配規(guī)則和增強(qiáng)邏輯創(chuàng)建AgentBuilder.Transformer

private static class Transformer implements AgentBuilder.Transformer {
        private PluginFinder pluginFinder;

        Transformer(PluginFinder pluginFinder) {
            this.pluginFinder = pluginFinder;
        }

        @Override
        public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
            ClassLoader classLoader, JavaModule module) {
            //當(dāng)前類typeDescription對(duì)應(yīng)的插件集合
            List<AbstractClassEnhancePluginDefine> pluginDefines = pluginFinder.find(typeDescription);
            if (pluginDefines.size() > 0) {
                DynamicType.Builder<?> newBuilder = builder;
                EnhanceContext context = new EnhanceContext();
                for (AbstractClassEnhancePluginDefine define : pluginDefines) {
                    //插件對(duì)應(yīng)的增強(qiáng)邏輯
                    DynamicType.Builder<?> possibleNewBuilder = define.define(typeDescription, newBuilder, classLoader, context);
                    if (possibleNewBuilder != null) {
                        newBuilder = possibleNewBuilder;
                    }
                }
                if (context.isEnhanced()) {
                    logger.debug("Finish the prepare stage for {}.", typeDescription.getName());
                }

                return newBuilder;
            }
            return builder;
        }
    }

此處需要重點(diǎn)解釋的是如何基于skywaling插件定義的增強(qiáng)邏輯構(gòu)建bytebuddy的DynamicType.Builder(動(dòng)態(tài)類型構(gòu)造器)
回顧上篇的插件定義ER圖:


image.png

依次次通過(guò)如下函數(shù)鏈構(gòu)建DynamicType.Builder
AbstractClassEnhancePluginDefine.define => ClassEnhancePluginDefine.enhance
=>ClassEnhancePluginDefine.enhanceClass=>ClassEnhancePluginDefine.enhanceInstance

@Override
    protected DynamicType.Builder<?> enhance(TypeDescription typeDescription,
        DynamicType.Builder<?> newClassBuilder, ClassLoader classLoader,
        EnhanceContext context) throws PluginException {
        //根據(jù)getStaticMethodsInterceptPoints(待增強(qiáng)的靜態(tài)方法和攔截處理器)
        //在DynamicType.Builder構(gòu)建中嵌入增強(qiáng)邏輯
        newClassBuilder = this.enhanceClass(typeDescription, newClassBuilder, classLoader);
        
        //根據(jù)getConstructorsInterceptPoints(待增強(qiáng)的構(gòu)造方法和攔截處理器)
        //根據(jù)getInstanceMethodsInterceptPoints(待增強(qiáng)的實(shí)例方法和攔截處理器)
        //在DynamicType.Builder構(gòu)建中嵌入增強(qiáng)邏輯
        newClassBuilder = this.enhanceInstance(typeDescription, newClassBuilder, classLoader, context);

        return newClassBuilder;
    }

以靜態(tài)方法增強(qiáng)邏輯嵌入為例子:

StaticMethodsInterceptPoint[] staticMethodsInterceptPoints = getStaticMethodsInterceptPoints();
for (StaticMethodsInterceptPoint staticMethodsInterceptPoint : staticMethodsInterceptPoints) {
            String interceptor = staticMethodsInterceptPoint.getMethodsInterceptor();
            newClassBuilder =         
//指定方法匹配規(guī)則  
newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
                        .intercept(
                            MethodDelegation.withDefaultConfiguration()
                                .withBinders(
                                    Morph.Binder.install(OverrideCallable.class)
                                )
                                //攔截處理器
                                .to(new StaticMethodsInterWithOverrideArgs(interceptor))
                        );

3.new Listener() 回調(diào)接口柳沙,用來(lái)在debug模式下保存增強(qiáng)后的字節(jié)碼文件
實(shí)現(xiàn)bytebuddy的AgentBuilder.Listener接口,基于事件回調(diào)

private static class Listener implements AgentBuilder.Listener {
        @Override
        public void onDiscovery(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {

        }

        @Override
        public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module,
            boolean loaded, DynamicType dynamicType) {
            if (logger.isDebugEnable()) {
                logger.debug("On Transformation class {}.", typeDescription.getName());
            }
            //在debug模式下保存增強(qiáng)后的字節(jié)碼文件
            InstrumentDebuggingClass.INSTANCE.log(dynamicType);
        }

        @Override
        public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module,
            boolean loaded) {

        }

        @Override
        public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded,
            Throwable throwable) {
            logger.error("Enhance class " + typeName + " error.", throwable);
        }

        @Override
        public void onComplete(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載拌倍,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者赂鲤。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市贰拿,隨后出現(xiàn)的幾起案子蛤袒,更是在濱河造成了極大的恐慌,老刑警劉巖膨更,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妙真,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡荚守,警方通過(guò)查閱死者的電腦和手機(jī)珍德,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)矗漾,“玉大人锈候,你說(shuō)我怎么就攤上這事〕ü保” “怎么了泵琳?”我有些...
    開(kāi)封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)誊役。 經(jīng)常有香客問(wèn)我获列,道長(zhǎng),這世上最難降的妖魔是什么蛔垢? 我笑而不...
    開(kāi)封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任击孩,我火速辦了婚禮,結(jié)果婚禮上鹏漆,老公的妹妹穿的比我還像新娘巩梢。我一直安慰自己,他們只是感情好艺玲,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布括蝠。 她就那樣靜靜地躺著,像睡著了一般饭聚。 火紅的嫁衣襯著肌膚如雪忌警。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天若治,我揣著相機(jī)與錄音慨蓝,去河邊找鬼。 笑死端幼,一個(gè)胖子當(dāng)著我的面吹牛礼烈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播婆跑,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼此熬,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了滑进?” 一聲冷哼從身側(cè)響起犀忱,我...
    開(kāi)封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扶关,沒(méi)想到半個(gè)月后阴汇,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡节槐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年搀庶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铜异。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡哥倔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出揍庄,到底是詐尸還是另有隱情咆蒿,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布蚂子,位于F島的核電站沃测,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏缆镣。R本人自食惡果不足惜芽突,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望董瞻。 院中可真熱鬧寞蚌,春花似錦、人聲如沸钠糊。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)抄伍。三九已至艘刚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間截珍,已是汗流浹背攀甚。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工箩朴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秋度。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓炸庞,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親荚斯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子埠居,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354