ArthasFunction.java 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. package io.github.qifan777.knowledge.code.arthas;
  2. import com.fasterxml.jackson.annotation.JsonProperty;
  3. import com.fasterxml.jackson.annotation.JsonPropertyDescription;
  4. import com.fasterxml.jackson.databind.JsonNode;
  5. import io.github.qifan777.knowledge.code.graph.entity.MethodNode;
  6. import io.github.qifan777.knowledge.code.graph.service.CodeGraphService;
  7. import io.github.qifan777.knowledge.infrastructure.code.CodeAssistantProperties;
  8. import lombok.AllArgsConstructor;
  9. import lombok.Data;
  10. import lombok.experimental.Accessors;
  11. import lombok.extern.slf4j.Slf4j;
  12. import org.springframework.ai.chat.model.ChatModel;
  13. import org.springframework.ai.chat.prompt.PromptTemplate;
  14. import org.springframework.context.annotation.Description;
  15. import org.springframework.http.HttpEntity;
  16. import org.springframework.http.HttpHeaders;
  17. import org.springframework.http.HttpMethod;
  18. import org.springframework.stereotype.Service;
  19. import org.springframework.web.client.RestTemplate;
  20. import java.nio.charset.StandardCharsets;
  21. import java.util.Base64;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.function.Function;
  25. import java.util.stream.Collectors;
  26. @Slf4j
  27. @Service
  28. @AllArgsConstructor
  29. @Description("诊断方法出现异常的原因")
  30. public class ArthasFunction implements Function<ArthasFunction.Request, String> {
  31. private final CodeGraphService codeGraphService;
  32. private final CodeAssistantProperties properties;
  33. private final ChatModel chatModel;
  34. public record Request(@JsonProperty(required = true)
  35. @JsonPropertyDescription("类名") String className,
  36. @JsonProperty(required = true)
  37. @JsonPropertyDescription("类名") String methodName) {
  38. }
  39. @Override
  40. public String apply(Request request) {
  41. String methodId = request.className + "#" + request.methodName;
  42. log.info("监听目标:{}", methodId);
  43. JobResult jobResult = startWatch(request.className, request.methodName);
  44. if (jobResult == null) {
  45. return "无异常信息";
  46. }
  47. String analyzeResult = jobResult
  48. .getBody()
  49. .getResults()
  50. .stream()
  51. .filter(r -> r.getType().equals("tt"))
  52. .findFirst()
  53. .map(result -> {
  54. String methods = codeGraphService.findChildMethods(methodId).stream().map(MethodNode::getContent)
  55. .distinct()
  56. .collect(Collectors.joining("\n"));
  57. TimeFragment timeFragment = result.getTimeFragmentList().get(0);
  58. String content = new PromptTemplate("""
  59. 根据下面提供的内容分析异常原因,回答结果用中文
  60. 方法名称:{methodName}
  61. 方法调用链: {methods}
  62. 方法参数:{params}
  63. 异常信息:{exp}
  64. """)
  65. .createMessage(Map.of("methodName", timeFragment.getMethodName(),
  66. "methods", methods,
  67. "params", timeFragment.getParams().stream().map(param -> param.getObject() == null ? "" : param.getObject().toString()).collect(Collectors.joining("\n")),
  68. "exp", timeFragment.getThrowExp()))
  69. .getContent();
  70. log.info("代码诊断prompt: {}", content);
  71. return chatModel.call(content);
  72. })
  73. .orElse("无异常信息");
  74. log.info("诊断结果: {}", analyzeResult);
  75. return analyzeResult;
  76. }
  77. public JobResult startWatch(String className, String method) {
  78. CodeAssistantProperties.ArthasProperties arthasProperties = properties.getArthas();
  79. RestTemplate restTemplate = new RestTemplate();
  80. String encode = Base64.getEncoder().encodeToString((arthasProperties.getUsername() + ":" + arthasProperties.getPassword()).getBytes(StandardCharsets.UTF_8));
  81. ArthasRequest arthasRequest = new ArthasRequest().setAction("exec").setCommand("tt -t " + className + " " + method + " -n 1");
  82. HttpHeaders httpHeaders = new HttpHeaders();
  83. httpHeaders.add("Authorization", "Basic " + encode);
  84. HttpEntity<ArthasRequest> requestHttpEntity = new HttpEntity<>(arthasRequest, httpHeaders);
  85. return restTemplate.exchange(arthasProperties.getUrl(), HttpMethod.POST, requestHttpEntity, JobResult.class).getBody();
  86. }
  87. @Accessors(chain = true)
  88. @Data
  89. public static class ArthasRequest {
  90. private String action;
  91. private String command;
  92. }
  93. @Data
  94. public static class JobResult {
  95. @JsonProperty("body")
  96. private Body body;
  97. private String message;
  98. private String sessionId;
  99. private String state;
  100. }
  101. @Data
  102. public static class Body {
  103. @JsonProperty("command")
  104. private String command;
  105. private int jobId;
  106. private String jobStatus;
  107. @JsonProperty("results")
  108. private List<Result> results;
  109. @JsonProperty("timeExpired")
  110. private boolean timeExpired;
  111. private int timeout;
  112. }
  113. @Data
  114. public static class Result {
  115. private Effect effect;
  116. private int jobId;
  117. private boolean success;
  118. private String type;
  119. @JsonProperty("timeFragmentList")
  120. private List<TimeFragment> timeFragmentList;
  121. private int statusCode;
  122. private boolean first;
  123. }
  124. @Data
  125. public static class Effect {
  126. private int classCount;
  127. private int cost;
  128. private int listenerId;
  129. private int methodCount;
  130. }
  131. @Data
  132. public static class TimeFragment {
  133. private String className;
  134. private double cost;
  135. private int index;
  136. private String methodName;
  137. private String object;
  138. private List<Param> params;
  139. @JsonProperty("return")
  140. private boolean isReturn;
  141. private String returnObj;
  142. @JsonProperty("throw")
  143. private boolean isThrow;
  144. @JsonProperty("throwExp")
  145. private String throwExp;
  146. @JsonProperty("timestamp")
  147. private String timestamp;
  148. }
  149. @Data
  150. public static class Param {
  151. private int expand;
  152. private JsonNode object;
  153. }
  154. }