APM - 零侵入监控Http服务

在这里插入图片描述


Pre

APM - 零侵入监控Service服务


HTTP采集入口

  1. DispacherServlet .doServer() ?
  2. @Control ?
  3. javax.servlet.http.HttpServlet ?

很显然第三种更具有通用性,不管是DispacherServlet 还是@Control 都会是基于HttpServlet#service

在这里插入图片描述


Code

package com.artisan.collects;

import com.artisan.ApmContext;
import com.artisan.intf.ICollect;
import com.artisan.model.HttpStatistics;
import javassist.*;

import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * Http采集器
 */
public class HttpCollect extends AbstractByteTransformCollect implements ICollect {
    // 采集目标
    // 1.DispatchServlet
    // 2.@control 下的方法
    // 3.采集 javax.servlet.service()  ---- 采用这种通用的方式
    private static final String TARGET_CLASS = "javax.servlet.http.HttpServlet";
    private static final String TARGET_METHOD = "service";
    private static ApmContext context;

    public HttpCollect(ApmContext context, Instrumentation instrumentation) {
        super(instrumentation);
        this.context = context;
    }


    public byte[] buildClass(ClassLoader loader) throws Exception {
        ClassPool pool = new ClassPool();
        pool.insertClassPath(new LoaderClassPath(loader));
        CtClass ctClass = pool.get(TARGET_CLASS);
        CtMethod oldMethod = ctClass.getDeclaredMethod(TARGET_METHOD);
        CtMethod newMethod = CtNewMethod.copy(oldMethod, ctClass, null);
        oldMethod.setName(oldMethod.getName() + "$agent");
        //HttpServlet.service()'

        String beginSrc = "Object stat=com.artisan.collects.HttpCollect.begin($args);";
        String errorSrc = "com.artisan.collects.HttpCollect.error(e,stat);";
        String endSrc = "com.artisan.collects.HttpCollect.end(stat);";
        newMethod.setBody(String.format(voidSource, beginSrc, TARGET_METHOD, errorSrc, endSrc));
        ctClass.addMethod(newMethod);
        return ctClass.toBytecode();
    }

    // url,client IP
    public static HttpStatistics begin(Object args[]) {
        HttpStatistics httpStatistics = new HttpStatistics();
        httpStatistics.setBeginTime(System.currentTimeMillis());
        // 采用适配器的方式 ,避免在tomcat下  agent【appLauncher装载】无法获取到HttpServletRequest【common加载】(classLoader机制)
        HttpServletRequestAdapter adapter = new HttpServletRequestAdapter(args[0]);
        httpStatistics.setUrl(adapter.getRequestURI());
        httpStatistics.setClientIp(adapter.getClientIp());
        return httpStatistics;
    }

    public static void end(Object obj) {
        HttpStatistics stat = (HttpStatistics) obj;
        ((HttpStatistics) obj).setUseTime(System.currentTimeMillis() - stat.getBeginTime());
        context.submitCollectResult(stat);
    }

    public static void error(Throwable error, Object obj) {
        HttpStatistics stat = (HttpStatistics) obj;
        stat.setError(error.getMessage());
        System.out.println(stat);
    }

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

    @Override
    public byte[] transform(ClassLoader loader, String className) throws Exception {
        if (!TARGET_CLASS.equals(className)) {
            return null;
        }
        try {
            return buildClass(loader);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static class HttpServletRequestAdapter {
        private final Object target;
        private final Method _getRequestURI;
        private final Method _getRequestURL;
        private final Method _getParameterMap;
        private final Method _getMethod;
        private final Method _getHeader;
        private final Method _getRemoteAddr;
        private final static String targetClassName = "javax.servlet.http.HttpServletRequest";

        public HttpServletRequestAdapter(Object target) {
            this.target = target;
            try {
                Class<?> targetClass = target.getClass().getClassLoader().loadClass(targetClassName);
                _getRequestURI = targetClass.getMethod("getRequestURI");
                _getParameterMap = targetClass.getMethod("getParameterMap");
                _getMethod = targetClass.getMethod("getMethod");
                _getHeader = targetClass.getMethod("getHeader", String.class);
                _getRemoteAddr = targetClass.getMethod("getRemoteAddr");
                _getRequestURL = targetClass.getMethod("getRequestURL");
            } catch (NoSuchMethodException e) {
                throw new IllegalArgumentException("error :" + e.getMessage() + ". probable cause the target is not belong javax.servlet.http.HttpServletRequest ");
            } catch (ClassNotFoundException e) {
                throw new IllegalArgumentException("error :" + e.getMessage() + ". probable cause the target is not belong javax.servlet.http.HttpServletRequest ");
            }
        }


        public String getRequestURI() {
            try {
                return (String) _getRequestURI.invoke(target);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public String getRequestURL() {
            try {
                return _getRequestURL.invoke(target).toString();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public Map<String, String[]> getParameterMap() {
            try {
                return (Map<String, String[]>) _getParameterMap.invoke(target);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public String getMethod() {
            try {
                return (String) _getMethod.invoke(target);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public String getHeader(String name) {
            try {
                return (String) _getHeader.invoke(target, name);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public String getRemoteAddr() {
            try {
                return (String) _getRemoteAddr.invoke(target);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public String getClientIp() {
            String ip = getHeader("x-forwarded-for");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = getRemoteAddr();
            }
            return ip;
        }

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小工匠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值