利用javassist,javaagent 打造个简单的性能监控 APM(学习)

此文章要有一定的基础,至少对javassist,javaagent 有所了解,

如果不是很熟悉的同学,请看下

javaagent使用指南 - rickiyang - 博客园

项目地址: https://github.com/MJFuture/m-javassist.git​​​​​​​

​​​​​​​GitHub - MJFuture/m-javassist: 性能监控apm,用作于学习使用。如果有研究可二次开发用于自己公司所有项目上。

Service + Control + jdbc 插桩埋点实现

一、项目架构介绍

二、采集端执行流程说明

需求与目标

采集指定数据,服务响应能、WEB响应性能、JDBC响应性能

处理流程

1、判定谁是采集目标类

2、构建插桩后的Class字节

3、采集方法运行时信息

4、上传运行时信息

结论:(绝对必须这么去做)

所有采集器必须要有判断是否监控目标的方法。

所有的采集器必须对我们的Class进行改造,生成插桩之后的字节码。

得出一个接口:Collects

记录开始信息、结束信息、异常信息、统计上传信息。(一般情况都会这么去做)

得出一个抽象类:AbstracetCollect

通用的方法:开始信息、结束信息、异常信息、统计上传信息

三、采集端架构UML类图及介绍

AgentMain:

监听器入口方法,所有采集器注册至该对象。由该对象的transform 来传递改造后的Class byte 至 ClassLoader进行加载。

Collect:

采集器接口,isTarget方法判定指类是否为采集目录,transform 构建插桩后的Class

AbstractCollects:

采集器的通用方法实现:begin 采集方法执行开始信息,error 采集异常信息 ,end 采集方法的结束信息。sendStatisticByHttp 基于Http 上传统计信息。

AgentLoader:

采集类修改器:updateMethod 改造指定方法已插入监听代码。toBytecote() 构建修改后的类字节。

四、Service 采集

问题:怎么判定目标类?基于XML 的配置如何 判定Service 为采集目标?

只能基于配置完成service 服务的判断。

判定目标方法?

公共的、非静态、非本地

监听方法代码构建

MethodSrcBuild: 开始代码、异常时执行代码、结束时执行代码。

统计信息传递

ServiceStatistics

异常堆栈传递

sendErrorStackByHttp("", throwable);

五、Control 采集

判定目标类?

基于@Control判定是否为采集目标。

判定目标方法?

屏蔽非公共方法、屏蔽静态方法、屏蔽本地方法、必须带上 RequestMapping 注解

获取URL地址

  • 类 @RequestMapping 注解获取 value值.
  • 方法 @RequestMapping 注解获取value值.
  • 以上value值基于正则表达示获取.

六、JDBC 采集

判定目标类?

基于NonRegisteringDriver类下的

判断方法?

接下来展示一些相关代码类

=========================================分割线==============================================

AgentMain 类

package com.apm.init;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.apm.collects.JdbcCommonCollects;
import com.apm.collects.SpringControllerCollects;
import com.apm.collects.SpringServiceCollects;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.LoaderClassPath;
/**
 * https://www.cnblogs.com/rickiyang/p/11368932.html   可看
 * 监听器入口方法,所有采集器注册至该对象。
 * 由该对象的transform 来传递改造后的Class byte 至 ClassLoader进行加载。
 */
public class AgentMain implements ClassFileTransformer {
    protected static AgentMain agentMain;
    private static Collect[] collects; // 采集器集合
    private Map<ClassLoader, ClassPool> classPoolMap = new ConcurrentHashMap<ClassLoader, ClassPool>();

    // 上传地址
    // 参数:
    // pro.key=
    // 访问远程服务 获取属性配置
    /**
     * mian执行后在去执行
     * @param args
     * @param inst
     */
    public static void agentmain(String args, Instrumentation inst) {
    	inst.addTransformer(new DefineTransformer(), true);
    }

    static class DefineTransformer implements ClassFileTransformer {
	    @Override
	    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
	        System.out.println("AgentMainTest --  premain load Class:" + className);
	        return classfileBuffer;
	    }
    }
    
    
    private static final ArrayList<String> keys;

    static {
        String paramKesy[] = {"server", "key", "secret"};
        keys = new ArrayList<String>();
        keys.addAll(Arrays.asList(paramKesy));
    }
    
    // 在应用启动前调用
    public static void premain(String agentArgs, Instrumentation inst) {
//        if (agentArgs != null) {
//            String[] paramGroup = agentArgs.split(",");
//            for (String param : paramGroup) {
//                String[] keyValue = param.split("=");
//                if (keys.contains(keyValue[0])) {
//                    System.setProperty("$bit_" + keyValue[0], keyValue[1]);
//                }
//            }
//        }
//        // 验主验置
//        if (System.getProperty("$bit_server") == null) {
//            System.setProperty("$bit_server", "http://api.ibitedu.com/receive");
//        }
//        Assert.checkNull(System.getProperty("$bit_key"),"param key is not null");
//        Assert.checkNull(System.getProperty("$bit_secret"),"param key is not null");

        //主要監控那個目標 
        collects = new Collect[]{
        		SpringServiceCollects.INSTANCE,
                JdbcCommonCollects.INSTANCE,
                SpringControllerCollects.INSTANCE
        };
        agentMain = new AgentMain();
        inst.addTransformer(agentMain);
    }
    
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
//    	System.out.println("transform --  premain load Class:" + className);
        if (className == null || loader == null
                || loader.getClass().getName().equals("sun.reflect.DelegatingClassLoader")
                || loader.getClass().getName().equals("org.apache.catalina.loader.StandardClassLoader")
                || loader.getClass().getName().equals("javax.management.remote.rmi.NoCallStackClassLoader")
                || loader.getClass().getName().equals("com.alibaba.fastjson.util.ASMClassLoader")
                || className.indexOf("$Proxy") != -1
                || className.startsWith("java")
                ) {
            return null;
        }
        
        if(className.startsWith("com/eprintServer/controller")  || className.startsWith("com/alibaba/druid/proxy/jdbc/ConnectionProxyImpl")) {
        	if (!classPoolMap.containsKey(loader)) {
        		ClassPool classPool = new ClassPool();
        		classPool.insertClassPath(new LoaderClassPath(loader));
        		classPoolMap.put(loader, classPool);
        	}
        	ClassPool cp = classPoolMap.get(loader);
        	try {
        		className = className.replaceAll("/", ".");
        		CtClass cclass = cp.get(className);
        		for (Collect c : collects) {
        			if (c.isTarget(className, loader, cclass)) { // 仅限定只能转换一次.
        				byte[] bytes = c.transform(loader, className, classfileBuffer, cclass);
        				//File f = new File("/Users/tommy/git/bit-monitoring-agent/target/" + cclass.getSimpleName() + ".class");
        				//Files.write(f.toPath(), bytes);
//        				System.out.println(String.format("%s bit APM agent insert success", className));
        				return bytes;
        			}
        		}
        	} catch (Throwable e) {
        		new Exception(String.format("%s  APM agent insert fail", className), e).printStackTrace();
        	}
        }
        return new byte[0];
    }
    
    public static void main(String[] args) {
		int i=10;System.out.println(i++);
		String str1 = "通话";
		String str2 = "重地";
		String str3 = new String("通話");
		System. out. println(String. format("str1:%d | str2:%d",  str1.hashCode(),str2.hashCode()));
		System. out. println(str1. equals(str2));
		System.out.println(Math.round(-1.5));

	}
}

Collect 接口类

package com.apm.init;

import javassist.CtClass;

/**
 * 采集接口 
 */
public interface Collect {
	/**
	 * 判断是否为采集目录
	 * 
	 * @param className
	 * @param loader
	 * @param ctclass
	 * @return
	 */
	public boolean isTarget(String className, ClassLoader loader,
			CtClass ctclass);

	/**
	 * 对目标类进行转
	 * @param loader
	 * @param className
	 * @param classfileBuffer
	 * @param ctclass
	 * @return
	 * @throws Exception
	 */
	public byte[] transform(ClassLoader loader, String className,
			byte[] classfileBuffer, CtClass ctclass) throws Exception;
}

AbstractCollects 类

package com.apm.init;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.apm.collects.ErrorLog;
import com.apm.common.NetUtils;
import com.apm.json.JsonWriter;

/**
 * 统计信息
 */
public abstract class AbstractCollects {
    // 统一线程池
    private final static ExecutorService threadService;

    private static final String localIp;
    private static long rejectedCount = 0;

    static {
    	
    	// 采样率配置
    	// 采样率 自动调节\手动调 节
    	// 	随机的方式
    	
        /**
         *  核心线程:20
         *  最大线程:200
         *  最大队例:1000
         */
        threadService = new ThreadPoolExecutor(20, 200,
                20000L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(1000),
                new RejectedExecutionHandler() {
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        rejectedCount++;
                        System.err.println("upload Task  rejected from " +
                                executor.toString() + " rejectedCount:" + rejectedCount);
                    }
                });
        localIp = NetUtils.getLocalHost();
    }

    @NotProguard
    public Statistics begin(String className, String method) {
        Statistics s = new Statistics();
        s.begin = System.currentTimeMillis();
        s.createTime = System.currentTimeMillis();
        return s;
    }

    @NotProguard
    public void end(Statistics stat) {
        stat.end = System.currentTimeMillis();
        stat.userTime = stat.end - stat.begin;
        sendStatistics(stat);
        //  System.out.println("代理结束:" + stat.toString());
    }

    @NotProguard
    public void error(Statistics stat, Throwable throwable) {
        if (stat != null) {
            stat.errorMsg = throwable.getMessage();
            stat.errorType = throwable.getClass().getName();
            if (throwable instanceof InvocationTargetException) {
                stat.errorType = ((InvocationTargetException) throwable).getTargetException().getClass().getName();
                stat.errorMsg = ((InvocationTargetException) throwable).getTargetException().getMessage();
            }
        }
        if (throwable != null) {
            sendErrorStackByHttp("", throwable);
        }
    }

    /**
     * 发送统计信息
     *
     * @param stat
     */
    public abstract void sendStatistics(final Statistics stat);


    protected void sendErrorStackByHttp(String errorMsg, Throwable throwable) {
        ErrorLog errorLog = new ErrorLog();
        if (throwable instanceof InvocationTargetException) {
            errorLog.setErrorType(((InvocationTargetException) throwable).getTargetException().getClass().getName());
            errorLog.setErrorMsg(((InvocationTargetException) throwable).getTargetException().getMessage());
        } else {
            errorLog.setErrorType(throwable.getClass().getName());
            errorLog.setErrorMsg(throwable.getMessage());
        }
        errorLog.setKeyId(System.getProperty("$bit_key"));
        errorLog.setIp(localIp);
        errorLog.setLogType("error");
        errorLog.setCreateTime(System.currentTimeMillis());
        // 计算异常堆栈
        {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            PrintStream s = new PrintStream(out);
            throwable.printStackTrace(s);
            errorLog.setStatck(out.toString());
        }
        execHttp("errorLog", errorLog);
    }

    protected void execHttp(final String type, final Object data) {
    	System.err.println("---------"+toJson(data));
        Runnable runn = new Runnable() {
            public void run() {
                try {
                    String remoteUrl = System.getProperty("$bit_server");
                    if(remoteUrl!=null) {
                    	//  remoteUrl += "?";
                    	String key = System.getProperty("$bit_key");
                    	String secret = System.getProperty("$bit_secret");
                    	long currentTime = System.currentTimeMillis();
                    	// 计算签名
                    	String sign = secret + key + type + currentTime + secret;
                    	sign = getMD5(sign.toUpperCase());
                    	String params = "";
                    	params += "type=" + type;
                    	params += "&sign=" + sign;
                    	params += "&key=" + key;
                    	params += "&time=" + currentTime;
                    	params += "&data=" + URLEncoder.encode(toJson(data),"UTF-8");
                    	URL url = new URL(remoteUrl);
                    	PrintWriter out = null;
                    	BufferedReader in = null;
                    	String result = "";
                    	try {
                    		URL realUrl = new URL(remoteUrl);
                    		// 打开和URL之间的连接
                    		URLConnection conn = realUrl.openConnection();
                    		// 设置通用的请求属性
                    		conn.setRequestProperty("accept", "*/*");
//                        conn.setRequestProperty("connection", "Keep-Alive");
                    		conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                    		conn.setUseCaches(false);
                    		// 发送POST请求必须设置如下两行
                    		conn.setDoOutput(true);
                    		conn.setDoInput(true);
                    		conn.setConnectTimeout(5000);
                    		conn.setReadTimeout(5000);
                    		// 获取URLConnection对象对应的输出流
                    		out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(),"UTF-8"));
                    		
                    		// 发送请求参数
                    		out.print(params);
                    		// flush输出流的缓冲
                    		out.flush();
                    		// 定义BufferedReader输入流来读取URL的响应
                    		in = new BufferedReader(
                    				new InputStreamReader(conn.getInputStream(), "UTF-8"));
                    		String line;
                    		while ((line = in.readLine()) != null) {
                    			result += line;
                    		}
                    		if(!"ok".equals(result)){
                    			System.err.println("bit apm upload fail :"+result);
                    		}
                    	} catch (Exception e) {
                    		throw new RuntimeException("上传失败", e);
//            logger.error("发送 POST 请求出现异常!",e);
                    	}
                    	//使用finally块来关闭输出流、输入流
                    	finally {
                    		try {
                    			if (out != null) {
                    				out.close();
                    			}
                    			if (in != null) {
                    				in.close();
                    			}
                    		} catch (IOException ex) {
                    			ex.printStackTrace();
                    		}
                    	}
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };
        threadService.execute(runn);
    }

    protected void sendStatisticByHttp(final Statistics stat, final String type) {
        // 通过后台线程发送至监控中心
        stat.keyId = System.getProperty("$bit_key");
        execHttp(type, stat);
    }

    public static String getAnnotationValue(String key, String annotationDesc) {
        String regex = String.format("value=\\{\".*\"\\}");
        Pattern r = Pattern.compile(regex);
        Matcher matcher = r.matcher(annotationDesc);
        if (matcher.find()) {
            return matcher.group().substring(key.length() + 3, matcher.group().length() - 2);
        }
        return null;
    }
    // 统计信息
    @NotProguard
    public static class Statistics {
        public Long begin;
        public Long end;
        public Long userTime;
        public String errorMsg;
        public String errorType;
        public Long createTime;
        public String keyId;
        public String ip = localIp;
        public String logType;


        public Statistics() {

        }

        public Statistics(Statistics copy) {
            this.begin = copy.begin;
            this.createTime = copy.createTime;
            this.end = copy.end;
            this.errorMsg = copy.errorMsg;
            this.errorType = copy.errorType;
            this.keyId = copy.keyId;
            this.ip = copy.ip;
            this.logType = copy.logType;
            this.userTime = userTime;
        }


        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("{");
            if (begin != null)
                sb.append("\"begin\":").append(begin);
            if (end != null)
                sb.append(", \"end\":").append(end);
            if (errorMsg != null)
                sb.append(", \"errorMsg\":\"").append(errorMsg).append('\"');
            if (errorType != null)
                sb.append(", \"errorType\":\"").append(errorType).append('\"');
            if (createTime != null)
                sb.append(", \"createTime\":").append(createTime);
            if (keyId != null)
                sb.append(", \"key\":\"").append(keyId).append('\"');
            if (sb.substring(1, 1).equals(",")) {
                sb.delete(1, 2);
            }
            sb.append('}');
            return sb.toString();
        }

        public String toJsonString() {
            final StringBuilder sb = new StringBuilder("{");
            if (begin != null)
                sb.append("\"begin\":").append(begin);
            if (end != null)
                sb.append(", \"end\":").append(end);
            if (errorMsg != null)
                sb.append(", \"errorMsg\":\"").append(errorMsg).append('\"');
            if (errorType != null)
                sb.append(", \"errorType\":\"").append(errorType).append('\"');
            if (createTime != null)
                sb.append(", \"createTime\":").append(createTime);
            if (sb.substring(1, 2).equals(",")) {
                sb.delete(1, 2);
            }
            sb.append('}');
            return sb.toString();
        }

    }

    private static String toJson(Object obj) {
        Map<String, Object> item = new HashMap<String, Object>();
        item.put("TYPE", false);
        item.put(JsonWriter.SKIP_NULL_FIELDS, true);
        String json = JsonWriter.objectToJson(obj, item);
        return json;
    }

    public static String getMD5(String content) {
        try {
            // 生成一个MD5加密计算摘要
            MessageDigest md = MessageDigest.getInstance("MD5");
            // 计算md5函数
            md.update(content.getBytes());
            return new BigInteger(1, md.digest()).toString(16);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

SpringControllerCollects 采集类其他类看github

package com.apm.collects;

import com.apm.init.AbstractCollects;
import com.apm.init.AgentLoader;
import com.apm.init.Collect;
import com.apm.init.NotProguard;

import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;

/**
 * 对 Controller 层进行采集
 *
 */
@NotProguard
public class SpringControllerCollects extends AbstractCollects implements Collect {
    @NotProguard
    public static SpringControllerCollects INSTANCE = new SpringControllerCollects();
    private static final String beginSrc;
    private static final String endSrc;
    private static final String errorSrc;

    private String rootRequestUrl = "";

    static {
        StringBuilder sbuilder = new StringBuilder();
        sbuilder.append("com.apm.collects.SpringControllerCollects instance= ");
        sbuilder.append("com.apm.collects.SpringControllerCollects.INSTANCE;\r\n");
        sbuilder.append("com.apm.collects.SpringControllerCollects.WebStatistics statistic =(com.apm.collects.SpringControllerCollects.WebStatistics)instance.begin(\"%s\",\"%s\");");
        sbuilder.append("statistic.urlAddress=\"%s\";");
        beginSrc = sbuilder.toString();
//        sbuilder = new StringBuilder();
        sbuilder.setLength(0);
        sbuilder.append("instance.end(statistic);");
        endSrc = sbuilder.toString();
        sbuilder.setLength(0);
//        sbuilder = new StringBuilder();
        sbuilder.append("instance.error(statistic,e);"); //父类的方法
        errorSrc = sbuilder.toString();
    }

    /**
     * 判断是否是采集对象
     */
    public boolean isTarget(String className, ClassLoader loader, CtClass ctclass) {
        boolean result = false;
        try {
            for (Object obj : ctclass.getAnnotations()) {
                // 通过正则表达示计算出RequestMapping 地址
                if (obj.toString().startsWith("@org.springframework.web.bind.annotation.RequestMapping")) {
                    rootRequestUrl = getAnnotationValue("value", obj.toString());
                } else if (obj.toString().startsWith("@org.springframework.stereotype.Controller")) {
                    result = true;
                }
            }
        } catch (ClassNotFoundException e) {
            System.err.println(String.format("bit apm run error targetClassName=%s errorMessage=%s",className,e.getClass().getSimpleName()+":"+e.getMessage()));
        }
        return result;
    }

    @NotProguard
    @Override
    public Statistics begin(String className, String method) {
        WebStatistics webStat = new WebStatistics(super.begin(className, method));
        webStat.controlName = className;
        webStat.methodName = method;
        webStat.logType="web";
        return  webStat;
    }

    @Override
    public void sendStatistics(Statistics stat) {
        sendStatisticByHttp(stat,"webLog");
    }
    /**
     * 对其转换 (插入监控代码)
     */
    public byte[] transform(ClassLoader loader, String className, byte[] classfileBuffer, CtClass ctclass) throws Exception {
    	AgentLoader byteLoade = new AgentLoader(className, loader, ctclass);
        CtMethod[] methods = ctclass.getDeclaredMethods();
        for (CtMethod m : methods) {
            String requestUrl;
            // 屏蔽非公共方法
            if (!Modifier.isPublic(m.getModifiers())) {
                continue;
            }
            // 屏蔽静态方法
            if (Modifier.isStatic(m.getModifiers())) {
                continue;
            }
            // 屏蔽本地方法
            if (Modifier.isNative(m.getModifiers())) {
                continue;
            }
            // 必须带上 RequestMapping 注解
            if ((requestUrl = getRequestMappingValue(m)) == null) {
                continue;
            }

            AgentLoader.MethodSrcBuild build = new AgentLoader.MethodSrcBuild();
            build.setBeginSrc(String.format(beginSrc, className, m.getName(), rootRequestUrl + requestUrl));
            build.setEndSrc(endSrc);
            build.setErrorSrc(errorSrc);
            byteLoade.updateMethod(m, build);
        }
        return byteLoade.toBytecote();
    }


    private String getRequestMappingValue(CtMethod m) throws ClassNotFoundException {
        for (Object s : m.getAnnotations()) {
            if (s.toString().startsWith("@org.springframework.web.bind.annotation.RequestMapping")) {
                String val = getAnnotationValue("value", s.toString());
                return val==null?"/":val;
            }
        }
        return null;
    }

    @NotProguard
    public static class WebStatistics extends Statistics { 
        public String urlAddress; //url 地址
        public String controlName; //服务名称
        public String methodName;// 方法名称
        public WebStatistics(Statistics s) {
            super(s);
        }
    }
}

采集类修改器 AgentLoader

package com.apm.init;

import java.io.IOException;

import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;

/**
 * agent 装载器 主要作用 
 * 1:构建代理监听环境 
 * 2:为目标类载入代理监听
 */
public class AgentLoader {
    @SuppressWarnings("unused")
	private final String className;
    @SuppressWarnings("unused")
	private final ClassLoader loader;
    private final CtClass ctclass;

    public AgentLoader(String className,
                       ClassLoader loader,
                       CtClass ctclass) {
        this.className = className;
        this.loader = loader;
        this.ctclass = ctclass;
    }

    /*
    * 插入 监听 method
    */
    public void updateMethod(CtMethod method, MethodSrcBuild srcBuild) throws CannotCompileException, NotFoundException {
        CtMethod ctmethod = method;
        String methodName = method.getName();
        // 重构被代理的方法名称

        // 基于原方法复制生成代理方法
        CtMethod agentMethod = CtNewMethod.copy(ctmethod, methodName, ctclass, null);
        agentMethod.setName(methodName + "$agent");
        ctclass.addMethod(agentMethod);

        // 原方法重置为代理执行
        ctmethod.setBody(srcBuild.buildSrc(ctmethod));
    }


    /**
     * 生成新的class 字节码 ,
     *
     * @param className
     * @param loader
     * @return
     * @throws NotFoundException
     * @throws Exception
     */
    public byte[] toBytecote() throws IOException, CannotCompileException {
        return ctclass.toBytecode();
    }

    /**
     * 內部類 插莊 
     *
     */
    public static class MethodSrcBuild {
        private String beginSrc;
        private String endSrc;
        private String errorSrc;


        public MethodSrcBuild setBeginSrc(String beginSrc) {
            this.beginSrc = beginSrc;
            return this;
        }

        public MethodSrcBuild setEndSrc(String endSrc) {
            this.endSrc = endSrc;
            return this;
        }

        public MethodSrcBuild setErrorSrc(String errorSrc) {
            this.errorSrc = errorSrc;
            return this;
        }

        public String buildSrc(CtMethod method) {
            String result;
            try {
                String template = method.getReturnType().getName().equals("void") ? voidSource : source;
                String bsrc = beginSrc == null ? "" : beginSrc;
                String eSrc = errorSrc == null ? "" : errorSrc;
                String enSrc = endSrc == null ? "" : endSrc;
                String src = String.format(template, bsrc, method.getName(), eSrc, enSrc);
                return src;
            } catch (NotFoundException e) {
                throw new RuntimeException(e);
            }
        }
       //参数写法
       //$$  表示   arg1 arg2 arg3 ...
       //$1 表示 arg1
       //$2 表示 arg2
       //$args  表示  Object[]
       //$w 表示自动返回原来的类型 
        final static String source = "{\n"
                + "%s"
                + "        Object result=null;\n"
                + "       try {\n"
                + "            result=($w)%s$agent($$);\n"
                + "        } catch (Throwable e) {\n"
                + "%s"
                + "            throw e;\n"
                + "        }finally{\n"
                + "%s"
                + "        }\n"
                + "        return ($r) result;\n"
                + "}\n";

        final static String voidSource = "{\n"
                + "%s"
                + "        try {\n"
                + "            %s$agent($$);\n"
                + "        } catch (Throwable e) {\n"
                + "%s"
                + "            throw e;\n"
                + "        }finally{\n"
                + "%s"
                + "        }\n"
                + "}\n";
    }


}

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.apm</groupId>
  <artifactId>m-javassist</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>m-javassist</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
   		<dependency>
			<groupId>org.javassist</groupId>
			<artifactId>javassist</artifactId>
			<version>3.24.1-GA</version>
		</dependency>
		<!--<dependency>
			<groupId>javax.xml.ws</groupId>
			<artifactId>jaxws-api</artifactId>
			<version>2.0</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
		</dependency>
		 <dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>4.0.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>4.0.4.RELEASE</version>	
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.0.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.0.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.30</version>
		</dependency>
    	<dependency>
	        <groupId>cglib</groupId>
	        <artifactId>cglib</artifactId>
	        <version>3.2.5</version>
	    </dependency>
	    <dependency>
	        <groupId>oro</groupId>
	        <artifactId>oro</artifactId>
	        <version>2.0.8</version>
    	</dependency> -->
    	
  </dependencies>
  
  <build>
   <finalName>m-javassist</finalName>
   <plugins>
	<plugin>
	    <groupId>org.apache.maven.plugins</groupId>
	    <artifactId>maven-assembly-plugin</artifactId>
	    <version>3.1.0</version>
	    <configuration>
	        <archive>
	            <!--自动添加META-INF/MANIFEST.MF -->
	            <manifest>
	                <addClasspath>true</addClasspath>
	            </manifest>
	            <manifestEntries>
	                <Premain-Class>com.apm.init.AgentMain</Premain-Class>
	                <Agent-Class>com.apm.init.AgentMain</Agent-Class>
	                <Can-Redefine-Classes>false</Can-Redefine-Classes>
	                <Can-Retransform-Classes>false</Can-Retransform-Classes>
	            </manifestEntries>
	        </archive>
	        <descriptorRefs>
				<descriptorRef>jar-with-dependencies</descriptorRef>
			</descriptorRefs>
	    </configuration>
		<executions>
<!-- 			配置执行器 -->
			<execution>
<!-- 				this is used for inheritance merges -->
				<id>make-assembly</id> 
<!-- 				指定在打包节点执行jar包合并操作绑定到package生命周期阶段上 -->
				<phase>package</phase> 
				<goals>
<!-- 					单例 只运行一次    -->
					<goal>single</goal>
				</goals>
			</execution>
		</executions>
	</plugin>
	</plugins> 
  </build>
  
  
  
</project>

最终打包成jar   

在 VM 上加上参数   -javaagent:F:\myworkspace\m-javassist\target\m-javassist-jar-with-dependencies.jar=canshu

指定打包后的目录,   canshu  ==参数 

然后启动后看日志,会有输出json格式的日志

比如 

{"urlAddress":"/index/main","controlName":"com.eprintServer.controller.IndexController","methodName":"mian","begin":1602056793201,"end":1602056793201,"userTime":0,"createTime":1602056793201,"ip":"192.168.41.58","logType":"web"}

 到这里就差将数据上传到统计服务器做处理了

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值