Problem description

A bug was encountered in a project running online. It is suspected that the bean in the configuration file failed to be read. What I usually do is add a line of log and start the code again. For example: log.info(“WxDomainProperties:{}”, WxDomainProperties);

Let’s see what the properties of this object look like.

But this is obviously a less efficient way. Here is a way to implement online debugging using Arthas, an open source service from Ali.

Let’s take a look at the code

The bean in question is this one, which is used to configure domain name information. The value configured through the configuration file. In different environments, such as dev,local, and online have different values.

/** * @author Yanghaolei * @date 2019/08/07 PM: 04 */ @Data @Component @ConfigurationProperties(prefix = WxDomainProperties.PREFIX) public class WxDomainProperties { public static final String PREFIX ="wx.domain"; /** * private List<String> requestDomainList = Lists. NewArrayList (); /** * WebSocket domain name */ private List<String> wsRequestDomainList = Lists. NewArrayList (); /** * private List<String> uploadDomainList = Lists. NewArrayList (); /** * download domain name */ private List<String> downloadDomainList = Lists. NewArrayList (); /** * private List<String> webviewDomainList = Lists. NewArrayList (); }Copy the code

Next we inject the bean into the business code.

@Slf4j
@AllArgsConstructor
@Service
public class MaDomainService {
    private final WxService wxService;
    private final WxDomainProperties wxDomainProperties;
Copy the code

In the setDoamin method, the bean stored values are automatically loaded by default. The bug I encountered was that by default, the values loaded were null. So I suspect there is something wrong with the values that this configuration reads.

 public WxOpenMaDomainResult setDomain(MaDomainSetDTO maDomainSetDTO) { JSONObject requestJson = new JSONObject(); Integer status = maDomainSetDTO.getStatus(); String appId = maDomainSetDTO.getAppId(); // 1 Enable default configuration --> force overwrite to default list [initialization operation]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 Default configuration is not enabled --> Can add/delete/get[customization is not allowedset]
        else if(StatusEnum. FALSE. GetValue (). The equals (status)) {/ / do not allow the customif (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());
            returnnew WxOpenMaDomainResult(); }}Copy the code

In order to verify that there is no problem with my suspicion, of course I need to debug to check this value. But this is the online code, as mentioned in the beginning by adding log restart check value is more troublesome things. So arthas came up with the idea for online debugging.

Arthas of actual combat

Most of the information on arthas on the web stops at how to install and look at the console. Here’s the official website, Arthas Introduction. I’m going to talk about how I tested my suspicions through Arthas and finally found out why.

The commands I’m using here are watch and trace.

The watch command performs data observation, allowing us to easily observe the invocation of the specified method. The format of watch is: watch + class name expression match + method name + expression + conditional expression. I want to see if the value of wxDomainProperties is successfully read by the configuration file during the call to the setDomain method. So my expression is: Watch + class name [. Com. Bjyt bange. Module. Wx. Middleware. MaDomainService] + method name [setDomain] [‘ target. WxDomainProperties] + expression. Here target represents the current object. Conditional expressions are often used to specify observation points and observation times, so they are not needed here. You can see the result of the execution:

You can see that my suspicions were wrong and that the bean has value. So this is why logging is inefficient, wasting a lot of time trying to verify a false suspicion.

I then used the trace command to try to track what was going on inside the method. The trace command traces the internal invocation path of a method and prints the time spent on each node along the method path. It differs from stack in that stack outputs the path where the current method was called. The format of trace is trace + class name + method name + expression. Here is the result:

You can clearly see that the exception was thrown in the second-to-last row. After analysis found that their code was written wrong… We finished an online debug based on arthas results.

Finally: Arthas for k8S or Docker

Arthas requires the JVM process running on the current machine to work. In a real production environment, all of our online machines would be deployed in K8S or Docker. This means that the machines on these lines also need arthas installed. This is also specifically mentioned in Ali’s official guidance regarding the deployment of Arthas in containers.

In fact, I think it would be better for developers to install Arthas locally. Then connect to Arthas on Docker via arthas’s webConsole. Then debug the remote online through the console. This point is also confirmed in the official User-case. Document how to use arthas for remote access #442. This allows for a more efficient development model. It also greatly reduces the amount of logging code such as log.info in your code. Given that Arthas does much more than that, it’s worth considering.