【字节码】基于JavaAgent的全链路监控六 开发应用级监控

本文介绍了基于JavaAgent实现全链路监控的第六部分,重点关注如何进行应用级监控。通过模块化设计,包括servlet、rpc、http、jdbc、redis和logic等模块,使用工厂模式便于监控程序的扩展和配置。文章详细阐述了环境准备、配置信息、核心代码实现以及测试过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

1.概述

上一篇文章:【字节码】基于JavaAgent的全链路监控五- ThreadLocal链路追踪

本章节转载:基于JavaAgent的全链路监控六《开发应用级监控》

在我们的监控程序中,需要对各种模块进行监控;servlet、rpc、http、jdbc、redis、logic等,那么我们在设计监控程序时就需要对监控的程序进行模块化开发,可以在需要的时候进行组装配置即可,以方便我们监控程序的扩展和可控制性。这一章节我们把监控模块剥离,采用工厂模式进行调用{目前是静态工厂在我们实际使用中可以把工厂做成动态配置化}。

2.环境准备

IntelliJ IDEA Community Edition
jdk1.8.0_45 64位

3.配置信息(路径相关修改为自己的)

配置位置:Run/Debug Configurations -> VM options
配置内容:-javaagent:/Users/lcc/IdeaProjects/lcc_work/test-byte-buddy/byte-buddy-v1x-javaagent-demo3/target/byte-buddy-v1x-javaagent-demo3-1.0-SNAPSHOT.jar=test

4. 核心代码

ApplicationAgent

package com.javaagent.bytebuddy.demo5;

import com.javaagent.bytebuddy.demo5.plugin.IPlugin;
import com.javaagent.bytebuddy.demo5.plugin.InterceptPoint;
import com.javaagent.bytebuddy.demo5.plugin.PluginFactory;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.utility.JavaModule;

import java.lang.instrument.Instrumentation;
import java.util.List;

/**
 * 博客:http://itstack.org
 * 论坛:http://bugstack.cn
 * 公众号:bugstack虫洞栈  {获取学习源码}
 * create by fuzhengwei on 2019
 */
public class ApplicationAgent {

    //JVM 首先尝试在代理类上调用以下方法
    public static void premain(String agentArgs, Instrumentation inst) {

        System.out.println("基于javaagent链路追踪{源码微信公众号:bugstack虫洞栈}");
        System.out.println("==========================================================\r\n");
        AgentBuilder agentBuilder = new AgentBuilder.Default();

        List<IPlugin> pluginGroup = PluginFactory.pluginGroup;
        for (IPlugin plugin : pluginGroup) {
            InterceptPoint[] interceptPoints = plugin.buildInterceptPoint();
            for (InterceptPoint point : interceptPoints) {

                AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> {
                    builder = builder.visit(Advice.to(plugin.adviceClass()).on(point.buildMethodsMatcher()));
                    return builder;
                };
                agentBuilder = agentBuilder.type(point.buildTypesMatcher())
                        .transform(transformer).asTerminalTransformation();
            }
        }

        //监听
        AgentBuilder.Listener listener = new AgentBuilder.Listener() {
            @Override
            public void onDiscovery(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {

            }

            @Override
            public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b, DynamicType dynamicType) {
                System.out.println("onTransformation:" + typeDescription);
            }

            @Override
            public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b) {

            }

            @Override
            public void onError(String s, ClassLoader classLoader, JavaModule javaModule, boolean b, Throwable throwable) {

            }

            @Override
            public void onComplete(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {

            }

        };

        agentBuilder.with(listener).installOn(inst);

    }

}

TrackManager

package com.javaagent.bytebuddy.demo5.track;

import java.util.Stack;

/**
 * 追踪管控
 * 博客:http://itstack.org
 * 论坛:http://bugstack.cn
 * 公众号:bugstack虫洞栈  {获取学习源码}
 * create by fuzhengwei on 2019
 */
public class TrackManager {

    private static final ThreadLocal<Stack<Span>> track = new ThreadLocal<>();

    private static Span createSpan() {
        Stack<Span> stack = track.get();
        if (stack == null) {
            stack = new Stack<>();
            track.set(stack);
        }
        String linkId;
        if (stack.isEmpty()) {
            linkId = TrackContext.getLinkId();
            if (linkId == null) {
                linkId = "nvl";
                TrackContext.setLinkId(linkId);
            }
        } else {
            Span span = stack.peek();
            linkId = span.getLinkId();
            TrackContext.setLinkId(linkId);
        }
        return new Span(linkId);
    }

    public static Span createEntrySpan() {
        Span span = createSpan();
        Stack<Span> stack = track.get();
        stack.push(span);
        return span;
    }


    public static Span getExitSpan() {
        Stack<Span> stack = track.get();
        if (stack == null || stack.isEmpty()) {
            TrackContext.clear();
            return null;
        }
        return stack.pop();
    }

    public static Span getCurrentSpan() {
        Stack<Span> stack = track.get();
        if (stack == null || stack.isEmpty()) {
            return null;
        }
        return stack.peek();
    }


}

TrackContext

package com.javaagent.bytebuddy.demo5.track;

/**
 * 博客:http://itstack.org
 * 论坛:http://bugstack.cn
 * 公众号:bugstack虫洞栈  {获取学习源码}
 * create by fuzhengwei on 2019
 */
public class TrackContext {

    private static final ThreadLocal<String> trackLocal = new ThreadLocal<>();

    public static void clear(){
        trackLocal.remove();
    }

    public static String getLinkId(){
        return trackLocal.get();
    }

    public static void setLinkId(String linkId){
        trackLocal.set(linkId);
    }

}

Span

package com.javaagent.bytebuddy.demo5.track;

import java.util.Date;

/**
 * 博客:http://itstack.org
 * 论坛:http://bugstack.cn
 * 公众号:bugstack虫洞栈  {获取学习源码}
 * create by fuzhengwei on 2019
 */
public class Span {

    private String linkId;  //链路ID
    private Date enterTime; //方法进入时间

    public Span(){}

    public Span(String linkId){
        this.linkId = linkId;
        this.enterTime = new Date();
    }

    public String getLinkId() {
        return linkId;
    }

    public void setLinkId(String linkId) {
        this.linkId = linkId;
    }

    public Date getEnterTime() {
        return enterTime;
    }

    public void setEnterTime(Date enterTime) {
        this.enterTime = enterTime;
    }
}

PluginFactory

package com.javaagent.bytebuddy.demo5.plugin;

import com.javaagent.bytebuddy.demo5.plugin.impl.jvm.JvmPlugin;
import com.javaagent.bytebuddy.demo5.plugin.impl.link.LinkPlugin;

import java.util.ArrayList;
import java.util.List;

/**
 * 博客:http://itstack.org
 * 论坛:http://bugstack.cn
 * 公众号:bugstack虫洞栈  {获取学习源码}
 * create by fuzhengwei on 2019
 */
public class PluginFactory {

    public static List<IPlugin> pluginGroup = new ArrayList<>();

    static {
        //链路监控
        pluginGroup.add(new LinkPlugin());
        //Jvm监控
        pluginGroup.add(new JvmPlugin());
    }

}

IPlugin

package com.javaagent.bytebuddy.demo5.plugin;

/**
 * 监控组件
 * 博客:http://itstack.org
 * 论坛:http://bugstack.cn
 * 公众号:bugstack虫洞栈  {获取学习源码}
 * create by fuzhengwei on 2019
 */
public interface IPlugin {

    //名称
    String name();

    //监控点
    InterceptPoint[] buildInterceptPoint();

    //拦截器类
    Class adviceClass();

}

InterceptPoint

package com.javaagent.bytebuddy.demo5.plugin;

import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

/**
 * 拦截点
 * 博客:http://itstack.org
 * 论坛:http://bugstack.cn
 * 公众号:bugstack虫洞栈  {获取学习源码}
 * create by fuzhengwei on 2019
 */
public interface InterceptPoint {

    //类匹配规则
    ElementMatcher<TypeDescription> buildTypesMatcher();

    //方法匹配规则
    ElementMatcher<MethodDescription> buildMethodsMatcher();

}

LinkPlugin

package com.javaagent.bytebuddy.demo5.plugin.impl.link;

import com.javaagent.bytebuddy.demo5.plugin.IPlugin;
import com.javaagent.bytebuddy.demo5.plugin.InterceptPoint;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;

/**
 * 博客:http://itstack.org
 * 论坛:http://bugstack.cn
 * 公众号:bugstack虫洞栈  {获取学习源码}
 * create by fuzhengwei on 2019
 */
public class LinkPlugin implements IPlugin {

    @Override
    public String name() {
        return "link";
    }

    @Override
    public InterceptPoint[] buildInterceptPoint() {
        return new InterceptPoint[]{
                new InterceptPoint() {
                    @Override
                    public ElementMatcher<TypeDescription> buildTypesMatcher() {
                        return ElementMatchers.nameStartsWith("com.javaagent.bytebuddy.demo5_1");
                    }

                    @Override
                    public ElementMatcher<MethodDescription> buildMethodsMatcher() {
                        return ElementMatchers.isMethod()
                                .and(ElementMatchers.any())
                                .and(ElementMatchers.not(ElementMatchers.nameStartsWith("main")));
                    }
                }
        };
    }

    @Override
    public Class adviceClass() {
        return LinkAdvice.class;
    }

}

LinkAdvice

package com.javaagent.bytebuddy.demo5.plugin.impl.link;

import com.javaagent.bytebuddy.demo5.track.Span;
import com.javaagent.bytebuddy.demo5.track.TrackContext;
import com.javaagent.bytebuddy.demo5.track.TrackManager;
import net.bytebuddy.asm.Advice;

import java.util.UUID;

/**
 * 博客:http://itstack.org
 * 论坛:http://bugstack.cn
 * 公众号:bugstack虫洞栈  {获取学习源码}
 * create by fuzhengwei on 2019
 */
public class LinkAdvice {

    @Advice.OnMethodEnter()
    public static void enter(@Advice.Origin("#t") String className, @Advice.Origin("#m") String methodName) {
        Span currentSpan = TrackManager.getCurrentSpan();
        if (null == currentSpan) {
            String linkId = UUID.randomUUID().toString();
            TrackContext.setLinkId(linkId);
        }
        TrackManager.createEntrySpan();
    }

    @Advice.OnMethodExit()
    public static void exit(@Advice.Origin("#t") String className, @Advice.Origin("#m") String methodName) {
        Span exitSpan = TrackManager.getExitSpan();
        if (null == exitSpan) return;
        System.out.println("链路追踪(MQ):" + exitSpan.getLinkId() + " " + className + "." + methodName + " 耗时:" + (System.currentTimeMillis() - exitSpan.getEnterTime().getTime()) + "ms");
    }

}

JvmAdvice

package com.javaagent.bytebuddy.demo5.plugin.impl.jvm;

import net.bytebuddy.asm.Advice;

/**
 * 博客:http://itstack.org
 * 论坛:http://bugstack.cn
 * 公众号:bugstack虫洞栈  {获取学习源码}
 * create by fuzhengwei on 2019
 */
public class JvmAdvice {

    @Advice.OnMethodExit()
    public static void exit() {
        JvmStack.printMemoryInfo();
        JvmStack.printGCInfo();
    }

}

JvmPlugin

package com.javaagent.bytebuddy.demo5.plugin.impl.jvm;

import com.javaagent.bytebuddy.demo5.plugin.IPlugin;
import com.javaagent.bytebuddy.demo5.plugin.InterceptPoint;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;

/**
 * 博客:http://itstack.org
 * 论坛:http://bugstack.cn
 * 公众号:bugstack虫洞栈  {获取学习源码}
 * create by fuzhengwei on 2019
 */
public class JvmPlugin implements IPlugin {

    @Override
    public String name() {
        return "jvm";
    }

    @Override
    public InterceptPoint[] buildInterceptPoint() {
        return new InterceptPoint[]{
                new InterceptPoint() {
                    @Override
                    public ElementMatcher<TypeDescription> buildTypesMatcher() {
                        return ElementMatchers.nameStartsWith("org.itstack.demo.test");
                    }

                    @Override
                    public ElementMatcher<MethodDescription> buildMethodsMatcher() {
                        return ElementMatchers.isMethod()
                                .and(ElementMatchers.any())
                                .and(ElementMatchers.not(ElementMatchers.nameStartsWith("main")));
                    }
                }
        };
    }

    @Override
    public Class adviceClass() {
        return JvmAdvice.class;
    }
    
}

JvmStack

package com.javaagent.bytebuddy.demo5.plugin.impl.jvm;

import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.util.Arrays;
import java.util.List;

/**
 * 博客:http://itstack.org
 * 论坛:http://bugstack.cn
 * 公众号:bugstack虫洞栈  {获取学习源码}
 * create by fuzhengwei on 2019
 */
public class JvmStack {

    private static final long MB = 1048576L;

    public static void printMemoryInfo() {
        MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
        MemoryUsage headMemory = memory.getHeapMemoryUsage();

        String info = String.format("\ninit: %s\t max: %s\t used: %s\t committed: %s\t use rate: %s\n",
                headMemory.getInit() / MB + "MB",
                headMemory.getMax() / MB + "MB", headMemory.getUsed() / MB + "MB",
                headMemory.getCommitted() / MB + "MB",
                headMemory.getUsed() * 100 / headMemory.getCommitted() + "%"

        );

        System.out.print(info);

        MemoryUsage nonheadMemory = memory.getNonHeapMemoryUsage();

        info = String.format("init: %s\t max: %s\t used: %s\t committed: %s\t use rate: %s\n",
                nonheadMemory.getInit() / MB + "MB",
                nonheadMemory.getMax() / MB + "MB", nonheadMemory.getUsed() / MB + "MB",
                nonheadMemory.getCommitted() / MB + "MB",
                nonheadMemory.getUsed() * 100 / nonheadMemory.getCommitted() + "%"

        );
        System.out.println(info);

    }

    public static void printGCInfo() {
        List<GarbageCollectorMXBean> garbages = ManagementFactory.getGarbageCollectorMXBeans();
        for (GarbageCollectorMXBean garbage : garbages) {
            String info = String.format("name: %s\t count:%s\t took:%s\t pool name:%s",
                    garbage.getName(),
                    garbage.getCollectionCount(),
                    garbage.getCollectionTime(),
                    Arrays.deepToString(garbage.getMemoryPoolNames()));
            System.out.println(info);
        }
        System.out.println("-------------------------------------------------------------------------------------------------");
    }

}

5.maven设置如下


    <properties>
        <asm.version>9.0</asm.version>
        <slf4j-api.version>1.7.28</slf4j-api.version>
        <byte-buddy.version>1.12.11</byte-buddy.version>
<!--        <byte-buddy.version>1.8.20</byte-buddy.version>-->
        <fastjson.version>1.2.76</fastjson.version>
        <lombok.version>1.18.16</lombok.version>
    </properties>

    <dependencies>

        <!-- 日志 相关 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j-api.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j-api.version}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>

        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
            <version>${byte-buddy.version}</version>
        </dependency>
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy-agent</artifactId>
            <version>${byte-buddy.version}</version>
        </dependency>

        <dependency>
            <groupId>com.bytebuddy</groupId>
            <artifactId>byte-buddy-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>


    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <addClasspath>true</addClasspath>
<!--                            <Premain-Class>com.javaagent.bytebuddy.MyPreMainAgent</Premain-Class>-->
<!--                            <Premain-Class>com.javaagent.bytebuddy.demo.MyAgent</Premain-Class>-->
<!--                            <Premain-Class>com.javaagent.bytebuddy.demo4.TraceAgent</Premain-Class>-->
                            <Premain-Class>com.javaagent.bytebuddy.demo5.ApplicationAgent</Premain-Class>
                            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                            <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>

6.测试如下

注意这里的包名不一样

package com.javaagent.bytebuddy.demo5_1;


public class ApplicationAgentTest {

    public static void main(String[] args) {

        //线程一
        new Thread(() -> new ApplicationAgentTest().http_lt1("哪咤")).start();

        //线程二
        new Thread(() -> {
            new ApplicationAgentTest().http_lt2("悟空");
        }).start();

    }

    public void http_lt1(String name) {
        try {
            Thread.sleep((long) (Math.random() * 500));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("测试结果:hi1 " + name);
        http_lt2(name);
    }

    public void http_lt2(String name) {
        try {
            Thread.sleep((long) (Math.random() * 500));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("测试结果:hi2 " + name);
        http_lt3(name);
    }

    public void http_lt3(String name) {
        try {
            Thread.sleep((long) (Math.random() * 500));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("测试结果:hi3 " + name);
    }

}

运行结果如下

基于javaagent链路追踪{源码微信公众号:bugstack虫洞栈}
==========================================================

onTransformation:class com.javaagent.bytebuddy.demo5_1.ApplicationAgentTest
测试结果:hi2 悟空
测试结果:hi1 哪咤
测试结果:hi3 悟空
链路追踪(MQ)6adec5ea-4ecb-4069-8211-af9d168075df com.javaagent.bytebuddy.demo5_1.ApplicationAgentTest.http_lt3 耗时:299ms
链路追踪(MQ)6adec5ea-4ecb-4069-8211-af9d168075df com.javaagent.bytebuddy.demo5_1.ApplicationAgentTest.http_lt2 耗时:556ms
测试结果:hi2 哪咤
测试结果:hi3 哪咤
链路追踪(MQ)3c1a664d-0d1e-40e0-9d2d-2cc4c131dc5a com.javaagent.bytebuddy.demo5_1.ApplicationAgentTest.http_lt3 耗时:347ms
链路追踪(MQ)3c1a664d-0d1e-40e0-9d2d-2cc4c131dc5a com.javaagent.bytebuddy.demo5_1.ApplicationAgentTest.http_lt2 耗时:767ms
链路追踪(MQ)3c1a664d-0d1e-40e0-9d2d-2cc4c131dc5a com.javaagent.bytebuddy.demo5_1.ApplicationAgentTest.http_lt1 耗时:1085ms

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值