java agent的简单使用/基于java agent的完全无侵入的监控系统

JAVA AGENT的基本使用

本片文章将给出一个完全无侵入的使用java agent的进行业务监控的简单实例。

先来看一个网上的例子。https://blog.csdn.net/catoop/article/details/51034739

package com.shanhy.demo.agent;

import java.lang.instrument.Instrumentation;

/**
 * 我的Java代理
 *
 * @author   单红宇(365384722)
 * @myblog  http://blog.csdn.net/catoop/
 * @create    2016年3月30日
 */
public class MyAgent {

    /**
     * 该方法在main方法之前运行,与main方法运行在同一个JVM中
     * 并被同一个System ClassLoader装载
     * 被统一的安全策略(security policy)和上下文(context)管理
     *
     * @param agentOps
     * @param inst
     * @author SHANHY
     * @create  2016年3月30日
     */
    public static void premain(String agentOps, Instrumentation inst) {
        System.out.println("=========premain方法执行========");
        System.out.println(agentOps);
    }

    /**
     * 如果不存在 premain(String agentOps, Instrumentation inst) 
     * 则会执行 premain(String agentOps)
     *
     * @param agentOps
     * @author SHANHY
     * @create  2016年3月30日
     */
    public static void premain(String agentOps) {
        System.out.println("=========premain方法执行2========");
        System.out.println(agentOps);
    }
}

修改MANIFEST文件后
以如下命令启动

java -javaagent:G:\myagent.jar=Hello1 -javaagent:G:\myagent.jar=Hello2 -javaagent:G:\myagent.jar=Hello3 -jar myapp.ja

上述代码是采用在JVM启动时加载java agent的。但是我们显然希望动态改变我们的代码。

被监控代码

一个简单的JAVA类,没有任何复杂处理

public class Main {

    private static Integer count = 10;

    public static void main(String[] args) {
        System.out.println("begin");
        while (true){
            try {
                count ++;
                Thread.sleep(1000);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

注入Agent的代码

import com.sun.tools.attach.VirtualMachine;
public class AgentMain
{
    public static void main(String[] args) throws Exception
    {
        VirtualMachine vm = null;
        String agentjarpath = "/Users/gdl/Desktop/java_pro/JavaAgentDemo/target/JavaAgentDemo-1.0-SNAPSHOT.jar"; //agentjar路径
        vm = VirtualMachine.attach("96089");//JVM的PID
        vm.loadAgent(agentjarpath, "This is Args to the Agent.");
        vm.detach();
    }
}

监控代码

public class Agent
{
    public static void agentmain(String args, Instrumentation inst) throws Exception
    {
        System.out.println("Args:" + args);
        System.out.println("Args2:" + args);
        //下面检查监控
        System.out.println(inst.getAllLoadedClasses().toString());
        for (Class cls: inst.getAllLoadedClasses()
             ) {
            //System.out.println(cls.getName());
            if(cls.getName().equals("Main")){
                Field f = cls.getDeclaredField("count");
                f.setAccessible(true);
                Object obj = f.get(null);
                Integer count = (Integer)obj;
                System.out.println(count.toString());
            }
        }
    }

}

请在pom中添加如下内容来修改MANIFEST文件的内容,使JVM能识别该JAR包

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-jar-pluginartifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Built-By>gdlBuilt-By>
                            <Agent-Class>com.gdl.AgentAgent-Class>
                            <Class-Path>tools.jarClass-Path>
                            <Can-Retransform-Classes>trueCan-Retransform-Classes>
                        manifestEntries>
                    archive>
                configuration>
            plugin>
        plugins>
    build>
  • 你的agentmain千万不要做成不退出的。否则会导致负责attach的进程永远不死掉。
  • attach agent不会改变之前加载的类!

如何监控一个对象的字段?

显然,上面的例子只能应用于监控一个类的静态字段。这点是不够的。我们希望监控任意一个对象的字段。但是,对象的字段取出来并不难,如何获取指定对象的引用是一个大问题。这里,我们没有什么好办法,只能采取一个用static的字段来获取其引用。

你可能感兴趣的:(java)