目录
一、原因
二、watch主要功能
三、使用
1、下载
2、测试类代码
四、示例
1、查看方法出参和返回值
2、查看类对象的属性
3、过滤不需要的请求
4、没有办法输出局部变量的值
5、查看方法耗时
五、arthas idea插件
一、原因
曾经,线上出了问题又定位不到问题原因,就开始抓耳挠腮,一遍一遍仔细看代码,到底是哪里可能出错了呢?然后在可能出错的地方加上日志,然后重新部署,再看输出日志;发现不行,还需要输出这个方法中的这个参数,然后再重新部署;一遍一遍循环往复,甚是繁琐。心想就没有一个想debug那样可以在线时时查看的功能么?
自从我接触了arthas,我被它强大的功能所折服。当我第一次看到同事使用arthas输出线上程序中任意方法的请求参数时心中一惊,还有这么神奇的工具呢?自此开启了我对arthas的崇拜之路。
但是刚开始感觉还是会有点繁琐,不会过滤请求、不会看成员变量,有时候想看方法中某一参数的值,但是又不知道该怎么看,还是很苦恼。
因此决定仔细学习一下arthas到底有哪些功能,都能做什么,这么好用的神器,不会用不是白白可惜了。
二、watch主要功能
1、查看方法入参、返回值
2、过滤无用的输出
3、查看类中的成员变量
4、无法查看方法中的局部变量
首先附上官方文档,更详细、更多的资料可以查看官方文档
参数名称 | 参数说明 |
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
express | 观察表达式 |
condition-express | 条件表达式 |
[b] | 在方法调用之前观察 |
[e] | 在方法异常之后观察 |
[s] | 在方法返回之后观察 |
[f] | 在方法结束之后(正常返回和异常返回)观察 |
[E] | 开启正则表达式匹配,默认为通配符匹配 |
x | 指定输出结果的属性遍历深度,默认为 1 |
n | 指定执行次数 |
这部分可以先跳过,看完下面例子回过头来再看,这里重点要说明的是观察表达式,观察表达式的构成主要由 ognl 表达式组成,所以你可以这样写"{params,returnObj,
target,throwExp}"
,只要是一个合法的 ognl 表达式,都能被正常支持。
params:表示方法入参,可以通过
params[0]获取指定位置的参数,可以用于过滤请求条件使用;
returnObj:表示方法返回参数;
target:表示类对象,可以通过target查看成员变量的值;
throwExp:表示异常;
特别说明:
- watch 命令定义了4个观察事件点,即
-b
方法调用前,-e
方法异常后,-s
方法返回后,-f
方法结束后 - 4个观察事件点
-b
、-e
、-s
默认关闭,-f
默认打开,当指定观察点被打开后,在相应事件点会对观察表达式进行求值并输出 - 这里要注意
方法入参
和方法出参
的区别,有可能在中间被修改导致前后不一致,除了-b
事件点params
代表方法入参外,其余事件都代表方法出参 - 当使用
-b
时,由于观察事件点是在方法调用前,此时返回值或异常均不存在
三、使用
1、windows下载
下载地址
2、linux下载
wget https://arthas.aliyun.com/arthas-boot.jar
直接用java -jar
的方式启动,首先启动我们的demo项目,然后在启动arthas
2、测试类代码
DemoController类
package com.demo.controller;
import com.demo.model.UserModel;
import com.demo.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@Autowired
private DemoService demoService;
@RequestMapping("/user")
@GetMapping
public String list(String name){
UserModel userModel = new UserModel();
userModel.setName(name);
return demoService.getUser(name, userModel);
}
}
DemoService类
package com.demo.service;
import com.demo.model.UserModel;
import org.springframework.stereotype.Service;
@Service
public class DemoService {
private static String PATH = "/home";
private String user;
public String getUser(String name, UserModel userModel) {
user = "zhangsan";
return userModel.getName();
}
}
UserModel类
package com.demo.model;
import lombok.Data;
@Data
public class UserModel {
public UserModel(){}
public UserModel(String id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
private String id;
private String name;
private Integer age;
}
四、示例
1、查看方法入参和返回值
watch com.demo.service.DemoService getUser '{params,returnObj}' -x 3
2、查看类对象的属性
watch com.demo.service.DemoService getUser 'target' -x 2
查看类中静态变量,PATH为类中的静态成员变量
getstatic com.demo.service.DemoService PATH -x 2
3、过滤不需要的请求
实际使用中会有不相关的请求调用我们的目标方法,可以通过添加过滤条件,只输出我们自己请求的日志,params[0]=='demo'表示只有当第一个参数等于demo时才输出。
watch com.demo.service.DemoService getUser "{params,returnObj}" "params[0]=='demo'" -x 4
当我们请求参数为demo1时,控制台并没有输出日志,当参数为demo时,可以正常输出。
采用参数UserModel对象中的属性过滤也是可以的,如下面示例:
watch com.demo.service.DemoService getUser "{params,returnObj}" "params[1].name=='demo'" -x 4
4、没有办法输方法中出局部变量的值(这点需要强调一下)
public void A(){
String index = 0;
String b = B();
String index = b;
}
public String B(){
return "B";
}
比如上边的,想查看index最后的值是不可以的,我们只能通过拦截B()方法获取到b的值,却没有办法获取不是通过方法调用赋值的局部变量。
很多时候,我们想输出方法中某一个变量的值,但是到目前为止还没有找到好的方法,只能获取赋值给这个参数的方法的返回值来确定,这是一个比较遗憾的地方。
watch 虽然很方便和灵活,但需要提前想清楚观察表达式的拼写,这对排查问题而言要求太高,因为很多时候我们并不清楚问题出自于何方,只能靠蛛丝马迹进行猜测。
这个时候如果能记录下当时方法调用的所有入参和返回值、抛出的异常会对整个问题的思考与判断非常有帮助。
同时还有更多命令和方法协同使用来定位问题,比如:
1)trace获得一个方法的调用路径;
2)tt: 记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测。
我也还在进一步学习、使用中,以后有了更多的总结再来补充。
5、查看方法耗时
当遇到执行时间比较长的接口,我们想知道时间到底消耗在哪里,但是手动去输出每个方法又太繁琐,这时候arthas告诉你,让我来!一个trace命令轻松搞定。
比如我们想查看DemoController#list()方法的内部耗时,只需要执行下面的命令,就可以看的一清二楚。
trace com.demo.controller.DemoController list
五、arthas idea插件
有了arthas这种神器可以线上输出日志,但是watch语法还是不够简单,因此Idea arthas 插件就此横空出世,插件安装成功后,只需要将光标放置在具体的类、字段、方法上面 右键选择需要执行的命令,部分会有窗口弹出、根据界面操作获取命令;部分直接获取命令复制到了剪切板 ,自己启动arthas 后粘贴即可执行。
复制出来的命令:
watch com.demo.service.DemoService getUser '{params,returnObj,throwExp}' -v -n 5 -x 3 '1==1'