在线上运行的项目中遇到bug。排查中怀疑是配置文件中的bean读取没有成功。一般个人作法是加一行日志,再从新上线代码。好比这样: log.info("WxDomainProperties:{}",wxDomainProperties);
git
来看一下这个对象的属性究竟是怎么样的。github
不过这显然是一种比较低效的方式。下面介绍一种用阿里开源的arthas来实现线上debug的方式。web
被怀疑出现问题的bean就是下面这个bean,主要是用来配置域名信息。经过配置文件配置的值。在不一样环境中,好比dev,local,online会有不一样值。docker
/**
* 微信第三方平台域名配置
*
* @author yanghaolei
* @date 2019/08/07 下午4:04
*/
@Data
@Component
@ConfigurationProperties(prefix = WxDomainProperties.PREFIX)
public class WxDomainProperties {
public static final String PREFIX = "wx.domain";
/**
* 请求域名
*/
private List<String> requestDomainList = Lists.newArrayList();
/**
* WebSocket域名
*/
private List<String> wsRequestDomainList = Lists.newArrayList();
/**
* 上传域名
*/
private List<String> uploadDomainList = Lists.newArrayList();
/**
* 下载域名
*/
private List<String> downloadDomainList = Lists.newArrayList();
/**
* 业务域名
*/
private List<String> webviewDomainList = Lists.newArrayList();
}
复制代码
接下来咱们注入这个bean到业务代码中。bash
@Slf4j
@AllArgsConstructor
@Service
public class MaDomainService {
private final WxService wxService;
private final WxDomainProperties wxDomainProperties;
复制代码
在方法setDoamin中,bean储存的值会在默认状况下被自动载入。而我遇到的bug就是发如今默认状况下,载入的值都是null。因此我怀疑这个配置读取的值有问题。微信
public WxOpenMaDomainResult setDomain(MaDomainSetDTO maDomainSetDTO) {
JSONObject requestJson = new JSONObject();
Integer status = maDomainSetDTO.getStatus();
String appId = maDomainSetDTO.getAppId();
// 1 启用默认配置 --> 强制覆盖成默认列表[初始化操做]
if (StatusEnum.TRUE.getValue().equals(status)) {
requestJson.put("action", SET_ACTION);
requestJson.put("requestdomain", JSONArray.parse(JSON.toJSONString(wxDomainProperties.getRequestDomainList())));
requestJson.put("wsrequestdomain", JSONArray.parseArray(JSON.toJSONString(wxDomainProperties.getWsRequestDomainList())));
requestJson.put("uploaddomain", JSONArray.parseArray(JSON.toJSONString(wxDomainProperties.getUploadDomainList())));
requestJson.put("downloaddomain", JSONArray.parseArray(JSON.toJSONString(wxDomainProperties.getDownloadDomainList())));
}
// 2 未启用默认配置 --> 能够add/delete/get[不容许自定义set]
else if (StatusEnum.FALSE.getValue().equals(status)) {
//不容许自定义覆盖
if (SET_ACTION.equals(maDomainSetDTO.getAction())) {
return new WxOpenMaDomainResult();
}
requestJson.put("requestdomain", getJsonArray(maDomainSetDTO.getRequestDomainList()));
requestJson.put("wsrequestdomain", getJsonArray(maDomainSetDTO.getWsRequestDomainList()));
requestJson.put("uploaddomain", getJsonArray(maDomainSetDTO.getUploadDomainList()));
requestJson.put("downloaddomain", getJsonArray(maDomainSetDTO.getDownloadDomainList()));
} else {
return new WxOpenMaDomainResult();
}
try {
String response = wxService.getMaService(appId).post(API_MODIFY_DOMAIN, requestJson.toJSONString());
return JSON.parseObject(response, WxOpenMaDomainResult.class);
} catch (Exception e) {
log.error("Error message:{},Error stackTrace:{}", e.getMessage(), e.getStackTrace());
return new WxOpenMaDomainResult();
}
}
复制代码
为了验证个人怀疑没有问题,固然是须要debug去检查下这个值。可是这个是线上代码,像开始说的经过加日志重启检查值是比较麻烦的事情。因此想到了用arthas来作线上debug。app
网上大部分资料对于arthas的介绍都停留于怎么安装和看一些控制台的信息。这里给出官网,Arthas 入门。我主要会讲我经过arthas验证个人怀疑和最后找到缘由的过程。dom
我这里用到的命令是watch和trace。jvm
watch命令执行数据观测,让咱们能方便的观察到指定方法的调用状况。watch的格式是: watch + 类名表达式匹配 + 方法名 + 表达式 + 条件表达式。 我这里是想观察方法setDomain在调用过程当中wxDomainProperties的值是否成功由配置文件读取。因此个人表达式是:watch + 类名[com.bjyt.bange.module.wx.middleware.MaDomainService] + 方法名[setDomain] + 表达式['target.wxDomainProperties' ]。这里target表示当前对象。条件表达式每每用来指定观察点和观察时间,这里不须要因此就没填。能够看到执行的结果:post
能够看到个人怀疑是错的,这个bean是有值的。因此这也是为何加日志效率低的缘由,会浪费不少时间去验证一个错的怀疑。
接下来我用到了trace命令,但愿经过跟踪方法内部究竟是怎么跑的。trace命令能够跟踪方法内部调用路径,并输出方法路径上的每一个节点上耗时。它与stack不同的地方是stack输出的是当前方法的被调用路径。trace的格式一样是trace + 类名 + 方法名 + 表达式。下面是执行的结果:
这里能够明显看到倒数第二行的位置抛出了异常。通过分析发现是本身的代码写错了。。。最终根据arthas的结果咱们完成了一次线上debug。
arthas是须要获取当前机器运行的jvm进程才能够工做的。在实际生产环境中,咱们线上的机器应该都是部署在k8s或者docker中的。也就是说这些线上的机器一样须要安装arthas。阿里的官方指导中也特别提到了这一点,关于在容器中部署arthas。
实际上,我认为比较好的方式是开发人员在本身的本地安装arthas。而后经过arthas的webConsole链接到docker上的arthas。而后经过控制台进行远程线上的debug。关于这一点也在官方的user-case中找到了印证。记录如何使用arthas进行远程访问 #442。这样就能搭建一个更高效率的开发模式。并且还能大大减小代码中的诸如log.info这样的日志代码量。再考虑到arthas的功能远远不止于此,应该说仍是值得去考虑的。