- 你们项目依赖是否环境复杂,经常没有本地环境可供调试
- 使用remote debug经常阻塞他人请求
- 使用arthas watch命令无法获得局部变量的变量值
- 类似debug我想要看到当前执行方法内的各个参数的信息
- 方便快速watch对应方法,并看到方法执行的结果
解决问题1,字节码插桩,在进入方法执行前和执行后分别插入方法,记录执行前后信息
public void test(Object request) {
methodEnter();
try {
// do something ...
methodExit();
} catch (Exception exception) {
methodException();
}
}
解决问题2,塞入一个轻量级http服务器,使用接口watch方法和取得对应执行信息
new NanoHTTPD(port).start(5000, false);
-javaagent:/x-ray-agent.jar=X_RAY_INCLUDES=xxx.xxx
必填
X_RAY_INCLUDES=<需要扫描的包名eg: xxx.xxx>
选填 (默认8000)
X_RAY_PORT=<xray.http端口号>
默认目录: ~/logs/x-ray/x-ray.log
功能:监听需要watch的方法
参数:watchName<需要被watch的方法>
格式:<类名>#<方法名>
curl --request GET \
--url 'http://127.0.0.1:8000/xray/watch?watchName=io.github.x.ray.xrayspringbootstarter.controller.CheckController%23method1' \
--header 'User-Agent: insomnia/8.4.2'
watchName=io.github.x.ray.xrayspringbootstarter.controller.CheckController#method1
watch success
功能:查看当前监听的方法集合 参数:无
curl --request GET \
--url http://127.0.0.1:8000/xray/watch/list \
--header 'User-Agent: insomnia/8.4.2'
[
"io.github.x.ray.xrayspringbootstarter.controller.CheckController#method1"
]
功能:查看监听方法的具体执行信息 参数:watchName<被watch的方法> 格式:<类名>#<方法名>
curl --request GET \
--url 'http://127.0.0.1:8000/xray/list?watchName=io.github.x.ray.xrayspringbootstarter.controller.CheckController%23method1' \
--header 'User-Agent: insomnia/8.4.2'
watchName=io.github.x.ray.xrayspringbootstarter.controller.CheckController#method1
methodName: 方法名称
enter: 方法执行入口
req: 方法入参
exit: 方法执行出口
lineNumber: 方法出行数
localVars: 方法内局部变量信息
returnVar: 方法出参信息
exceptionMsg: 异常信息
exceptionStack: 异常堆栈
[
{
"clazz": "io.github.x.ray.xrayspringbootstarter.controller.CheckController",
"enter": {
"methodVars": {
"req": {
"poCode": "123421",
"poName": "测试res",
"poType": 12138972,
"requestId": "idgsahasgd"
}
}
},
"exit": {
"lineNumber": 55,
"localVars": {
"req": {
"poCode": "123421",
"poName": "测试res",
"poType": 12138972,
"requestId": "idgsahasgd"
},
"i": 1,
"j": 2,
"sum": 3
},
"returnVar": "{\"poCode\":\"123421\",\"poName\":\"测试res\",\"poType\":12138972,\"requestId\":\"idgsahasgd\"}"
},
"methodDesc": "(Lio/github/x/ray/xrayspringbootstarter/model/XRayTestModel;)Lio/github/x/ray/xrayspringbootstarter/model/XRayTestModel;",
"methodName": "method1"
}
]
[
{
"clazz": "io.github.x.ray.xrayspringbootstarter.controller.CheckController",
"enter": {
"methodVars": {
"req": {
"poCode": "123",
"poName": "xxx",
"poType": 123
}
}
},
"exit": {
"exceptionMsg": "TEST EXCEPTION...",
"exceptionStack": [
{
"fileName": "CheckController.java",
"lineNumber": 49,
"className": "io.github.x.ray.xrayspringbootstarter.controller.CheckController",
"methodName": "method1"
},
{
"fileName": "NativeMethodAccessorImpl.java",
"lineNumber": -2,
"className": "jdk.internal.reflect.NativeMethodAccessorImpl",
"methodName": "invoke0"
},
{
"fileName": "NativeMethodAccessorImpl.java",
"lineNumber": 62,
"className": "jdk.internal.reflect.NativeMethodAccessorImpl",
"methodName": "invoke"
},
{
"fileName": "DelegatingMethodAccessorImpl.java",
"lineNumber": 43,
"className": "jdk.internal.reflect.DelegatingMethodAccessorImpl",
"methodName": "invoke"
},
{
"fileName": "Method.java",
"lineNumber": 566,
"className": "java.lang.reflect.Method",
"methodName": "invoke"
},
{
"fileName": "InvocableHandlerMethod.java",
"lineNumber": 205,
"className": "org.springframework.web.method.support.InvocableHandlerMethod",
"methodName": "doInvoke"
},
{
"fileName": "InvocableHandlerMethod.java",
"lineNumber": 150,
"className": "org.springframework.web.method.support.InvocableHandlerMethod",
"methodName": "invokeForRequest"
},
{
"fileName": "ServletInvocableHandlerMethod.java",
"lineNumber": 117,
"className": "org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod",
"methodName": "invokeAndHandle"
},
{
"fileName": "RequestMappingHandlerAdapter.java",
"lineNumber": 895,
"className": "org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter",
"methodName": "invokeHandlerMethod"
},
{
"fileName": "RequestMappingHandlerAdapter.java",
"lineNumber": 808,
"className": "org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter",
"methodName": "handleInternal"
},
{
"fileName": "AbstractHandlerMethodAdapter.java",
"lineNumber": 87,
"className": "org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter",
"methodName": "handle"
},
{
"fileName": "DispatcherServlet.java",
"lineNumber": 1072,
"className": "org.springframework.web.servlet.DispatcherServlet",
"methodName": "doDispatch"
},
{
"fileName": "DispatcherServlet.java",
"lineNumber": 965,
"className": "org.springframework.web.servlet.DispatcherServlet",
"methodName": "doService"
},
{
"fileName": "FrameworkServlet.java",
"lineNumber": 1006,
"className": "org.springframework.web.servlet.FrameworkServlet",
"methodName": "processRequest"
},
{
"fileName": "FrameworkServlet.java",
"lineNumber": 909,
"className": "org.springframework.web.servlet.FrameworkServlet",
"methodName": "doPost"
},
{
"fileName": "HttpServlet.java",
"lineNumber": 555,
"className": "javax.servlet.http.HttpServlet",
"methodName": "service"
},
{
"fileName": "FrameworkServlet.java",
"lineNumber": 883,
"className": "org.springframework.web.servlet.FrameworkServlet",
"methodName": "service"
},
{
"fileName": "HttpServlet.java",
"lineNumber": 623,
"className": "javax.servlet.http.HttpServlet",
"methodName": "service"
},
{
"fileName": "ApplicationFilterChain.java",
"lineNumber": 209,
"className": "org.apache.catalina.core.ApplicationFilterChain",
"methodName": "internalDoFilter"
},
{
"fileName": "ApplicationFilterChain.java",
"lineNumber": 153,
"className": "org.apache.catalina.core.ApplicationFilterChain",
"methodName": "doFilter"
},
{
"fileName": "WsFilter.java",
"lineNumber": 51,
"className": "org.apache.tomcat.websocket.server.WsFilter",
"methodName": "doFilter"
},
{
"fileName": "ApplicationFilterChain.java",
"lineNumber": 178,
"className": "org.apache.catalina.core.ApplicationFilterChain",
"methodName": "internalDoFilter"
},
{
"fileName": "ApplicationFilterChain.java",
"lineNumber": 153,
"className": "org.apache.catalina.core.ApplicationFilterChain",
"methodName": "doFilter"
},
{
"fileName": "RequestContextFilter.java",
"lineNumber": 100,
"className": "org.springframework.web.filter.RequestContextFilter",
"methodName": "doFilterInternal"
},
{
"fileName": "OncePerRequestFilter.java",
"lineNumber": 117,
"className": "org.springframework.web.filter.OncePerRequestFilter",
"methodName": "doFilter"
},
{
"fileName": "ApplicationFilterChain.java",
"lineNumber": 178,
"className": "org.apache.catalina.core.ApplicationFilterChain",
"methodName": "internalDoFilter"
},
{
"fileName": "ApplicationFilterChain.java",
"lineNumber": 153,
"className": "org.apache.catalina.core.ApplicationFilterChain",
"methodName": "doFilter"
},
{
"fileName": "FormContentFilter.java",
"lineNumber": 93,
"className": "org.springframework.web.filter.FormContentFilter",
"methodName": "doFilterInternal"
},
{
"fileName": "OncePerRequestFilter.java",
"lineNumber": 117,
"className": "org.springframework.web.filter.OncePerRequestFilter",
"methodName": "doFilter"
},
{
"fileName": "ApplicationFilterChain.java",
"lineNumber": 178,
"className": "org.apache.catalina.core.ApplicationFilterChain",
"methodName": "internalDoFilter"
},
{
"fileName": "ApplicationFilterChain.java",
"lineNumber": 153,
"className": "org.apache.catalina.core.ApplicationFilterChain",
"methodName": "doFilter"
},
{
"fileName": "CharacterEncodingFilter.java",
"lineNumber": 201,
"className": "org.springframework.web.filter.CharacterEncodingFilter",
"methodName": "doFilterInternal"
},
{
"fileName": "OncePerRequestFilter.java",
"lineNumber": 117,
"className": "org.springframework.web.filter.OncePerRequestFilter",
"methodName": "doFilter"
},
{
"fileName": "ApplicationFilterChain.java",
"lineNumber": 178,
"className": "org.apache.catalina.core.ApplicationFilterChain",
"methodName": "internalDoFilter"
},
{
"fileName": "ApplicationFilterChain.java",
"lineNumber": 153,
"className": "org.apache.catalina.core.ApplicationFilterChain",
"methodName": "doFilter"
},
{
"fileName": "StandardWrapperValve.java",
"lineNumber": 168,
"className": "org.apache.catalina.core.StandardWrapperValve",
"methodName": "invoke"
},
{
"fileName": "StandardContextValve.java",
"lineNumber": 90,
"className": "org.apache.catalina.core.StandardContextValve",
"methodName": "invoke"
},
{
"fileName": "AuthenticatorBase.java",
"lineNumber": 481,
"className": "org.apache.catalina.authenticator.AuthenticatorBase",
"methodName": "invoke"
},
{
"fileName": "StandardHostValve.java",
"lineNumber": 130,
"className": "org.apache.catalina.core.StandardHostValve",
"methodName": "invoke"
},
{
"fileName": "ErrorReportValve.java",
"lineNumber": 93,
"className": "org.apache.catalina.valves.ErrorReportValve",
"methodName": "invoke"
},
{
"fileName": "StandardEngineValve.java",
"lineNumber": 74,
"className": "org.apache.catalina.core.StandardEngineValve",
"methodName": "invoke"
},
{
"fileName": "CoyoteAdapter.java",
"lineNumber": 342,
"className": "org.apache.catalina.connector.CoyoteAdapter",
"methodName": "service"
},
{
"fileName": "Http11Processor.java",
"lineNumber": 390,
"className": "org.apache.coyote.http11.Http11Processor",
"methodName": "service"
},
{
"fileName": "AbstractProcessorLight.java",
"lineNumber": 63,
"className": "org.apache.coyote.AbstractProcessorLight",
"methodName": "process"
},
{
"fileName": "AbstractProtocol.java",
"lineNumber": 928,
"className": "org.apache.coyote.AbstractProtocol$ConnectionHandler",
"methodName": "process"
},
{
"fileName": "NioEndpoint.java",
"lineNumber": 1794,
"className": "org.apache.tomcat.util.net.NioEndpoint$SocketProcessor",
"methodName": "doRun"
},
{
"fileName": "SocketProcessorBase.java",
"lineNumber": 52,
"className": "org.apache.tomcat.util.net.SocketProcessorBase",
"methodName": "run"
},
{
"fileName": "ThreadPoolExecutor.java",
"lineNumber": 1191,
"className": "org.apache.tomcat.util.threads.ThreadPoolExecutor",
"methodName": "runWorker"
},
{
"fileName": "ThreadPoolExecutor.java",
"lineNumber": 659,
"className": "org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker",
"methodName": "run"
},
{
"fileName": "TaskThread.java",
"lineNumber": 61,
"className": "org.apache.tomcat.util.threads.TaskThread$WrappingRunnable",
"methodName": "run"
},
{
"fileName": "Thread.java",
"lineNumber": 834,
"className": "java.lang.Thread",
"methodName": "run"
}
],
"localVars": {},
"methodVars": {
"req": {
"poCode": "123",
"poName": "xxx",
"poType": 123
}
}
},
"methodDesc": "(Lio/github/x/ray/xrayspringbootstarter/model/XRayTestModel;)Lio/github/x/ray/xrayspringbootstarter/model/XRayTestModel;",
"methodName": "method1"
}
]
功能:清除对应方法的具体执行信息 参数:watchName<被watch的方法> 格式:<类名>#<方法名>
curl --request GET \
--url 'http://127.0.0.1:8000/xray/list/clear?watchName=io.github.x.ray.xrayspringbootstarter.controller.CheckController%23method1' \
--header 'User-Agent: insomnia/8.4.2'
watchName=io.github.x.ray.xrayspringbootstarter.controller.CheckController#method1
clear success