package io.github.qifan777.knowledge.code.arthas; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; import com.fasterxml.jackson.databind.JsonNode; import io.github.qifan777.knowledge.code.graph.entity.MethodNode; import io.github.qifan777.knowledge.code.graph.service.CodeGraphService; import io.github.qifan777.knowledge.infrastructure.code.CodeAssistantProperties; import lombok.AllArgsConstructor; import lombok.Data; import lombok.experimental.Accessors; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.context.annotation.Description; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; @Slf4j @Service @AllArgsConstructor @Description("诊断方法出现异常的原因") public class ArthasFunction implements Function { private final CodeGraphService codeGraphService; private final CodeAssistantProperties properties; private final ChatModel chatModel; public record Request(@JsonProperty(required = true) @JsonPropertyDescription("类名") String className, @JsonProperty(required = true) @JsonPropertyDescription("类名") String methodName) { } @Override public String apply(Request request) { String methodId = request.className + "#" + request.methodName; log.info("监听目标:{}", methodId); JobResult jobResult = startWatch(request.className, request.methodName); if (jobResult == null) { return "无异常信息"; } String analyzeResult = jobResult .getBody() .getResults() .stream() .filter(r -> r.getType().equals("tt")) .findFirst() .map(result -> { String methods = codeGraphService.findChildMethods(methodId).stream().map(MethodNode::getContent) .distinct() .collect(Collectors.joining("\n")); TimeFragment timeFragment = result.getTimeFragmentList().get(0); String content = new PromptTemplate(""" 根据下面提供的内容分析异常原因,回答结果用中文 方法名称:{methodName} 方法调用链: {methods} 方法参数:{params} 异常信息:{exp} """) .createMessage(Map.of("methodName", timeFragment.getMethodName(), "methods", methods, "params", timeFragment.getParams().stream().map(param -> param.getObject() == null ? "" : param.getObject().toString()).collect(Collectors.joining("\n")), "exp", timeFragment.getThrowExp())) .getContent(); log.info("代码诊断prompt: {}", content); return chatModel.call(content); }) .orElse("无异常信息"); log.info("诊断结果: {}", analyzeResult); return analyzeResult; } public JobResult startWatch(String className, String method) { CodeAssistantProperties.ArthasProperties arthasProperties = properties.getArthas(); RestTemplate restTemplate = new RestTemplate(); String encode = Base64.getEncoder().encodeToString((arthasProperties.getUsername() + ":" + arthasProperties.getPassword()).getBytes(StandardCharsets.UTF_8)); ArthasRequest arthasRequest = new ArthasRequest().setAction("exec").setCommand("tt -t " + className + " " + method + " -n 1"); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add("Authorization", "Basic " + encode); HttpEntity requestHttpEntity = new HttpEntity<>(arthasRequest, httpHeaders); return restTemplate.exchange(arthasProperties.getUrl(), HttpMethod.POST, requestHttpEntity, JobResult.class).getBody(); } @Accessors(chain = true) @Data public static class ArthasRequest { private String action; private String command; } @Data public static class JobResult { @JsonProperty("body") private Body body; private String message; private String sessionId; private String state; } @Data public static class Body { @JsonProperty("command") private String command; private int jobId; private String jobStatus; @JsonProperty("results") private List results; @JsonProperty("timeExpired") private boolean timeExpired; private int timeout; } @Data public static class Result { private Effect effect; private int jobId; private boolean success; private String type; @JsonProperty("timeFragmentList") private List timeFragmentList; private int statusCode; private boolean first; } @Data public static class Effect { private int classCount; private int cost; private int listenerId; private int methodCount; } @Data public static class TimeFragment { private String className; private double cost; private int index; private String methodName; private String object; private List params; @JsonProperty("return") private boolean isReturn; private String returnObj; @JsonProperty("throw") private boolean isThrow; @JsonProperty("throwExp") private String throwExp; @JsonProperty("timestamp") private String timestamp; } @Data public static class Param { private int expand; private JsonNode object; } }