AI智能日志分析系统
文章目录
- 1.combinations-intelligent-analysis-starter
- 1.目录结构
- 2.pom.xml
- 3.自动配置
- 1.IntelligentAnalysisAutoConfiguration.java
- 2.spring.factories
- 2.combinations-intelligent-analysis-starter-demo
- 1.目录结构
- 2.pom.xml
- 3.application.yml
- 4.IntelligentAnalysisApplication.java 启动类
- 5.工具类
- 1.MailUtil.java 发送邮件
- 2.MethodCallChainUtil.java 根据堆栈信息从Gitee获取源码并提取每个方法的调用链
- 3.StringUtils.java 合并然后截取指定长度字符串
- 6.ELKEntity.java ELK映射实体类
- 7.RabbitMQConfig.java
- 8.ElkListener.java 监听从Logstash中发送过来的日志消息
- 9.DlxQueueListener.java 监听死信队列,确保消费者可靠性
- 10.结果展示
- 1.combinations-elk-starter-demo 直接抛出异常
- 2.combinations-intelligent-analysis-starter-demo 开始监听,一旦发生异常,就进行ai分析
- 3.AI分析的邮件
- 3.Logstash的配置以及系统执行流程
- 1.这个配置可以将消息发送到RabbitMQ
- 2.AI智能日志分析系统执行流程
- 1.Logstash采集日志,当日志为ERROR的时候发送到RabbitMQ
- 2.RabbitMQ监听到日志进行处理
- 1.通过javaparser根据异常堆栈来解析出所有自己项目的groupId下的类路径和方法名
- 2.通过仓库名字+日志中的moudle名+类路径就可以从Gitee中获取这个类的代码
- 3.再使用javaparser去获取到这个方法的调用链,就是当前方法以及调用了当前方法的内容
- 4.将方法调用链和异常堆栈进行截取后交给AI智能分析日志,给出解决方案
- 5.为了解决OpenAI的接口调用速率限制,采用消费者指数退避重试机制加上死信队列的方式确保消息正常消费
- 6.考虑成本问题,只有当方法调用链不为空的时候才进行AI的日志分析,其余情况(方法调用链为空和死信队列)就会直接将错误日志的消息以邮件的形式发送
1.combinations-intelligent-analysis-starter
1.目录结构

2.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.sunxiansheng</groupId><artifactId>sunrays-combinations</artifactId><version>1.0.5</version></parent><version>1.0.5</version><artifactId>combinations-intelligent-analysis-starter</artifactId><dependencies><!-- common-rabbitmq-starter --><dependency><groupId>com.sunxiansheng</groupId><artifactId>common-rabbitmq-starter</artifactId><version>1.0.5</version></dependency><!-- common-openai-starter --><dependency><groupId>com.sunxiansheng</groupId><artifactId>common-openai-starter</artifactId><version>1.0.5</version></dependency><!-- javaparser-core --><dependency><groupId>com.github.javaparser</groupId><artifactId>javaparser-core</artifactId><version>3.25.4</version></dependency><!-- jackson-databind --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency><!-- common-mail-starter --><dependency><groupId>com.sunxiansheng</groupId><artifactId>common-mail-starter</artifactId><version>1.0.5</version></dependency><dependency><groupId>com.vladsch.flexmark</groupId><artifactId>flexmark-all</artifactId><version>0.62.2</version></dependency></dependencies></project>
3.自动配置
1.IntelligentAnalysisAutoConfiguration.java
package com.sunxiansheng.intelligent.analysis.config;import org.springframework.context.annotation.Configuration;/*** Description: 智能分析自动配置类** @Author sun* @Create 2025/1/1 19:27* @Version 1.0*/
@Configuration
public class IntelligentAnalysisAutoConfiguration {}
2.spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.sunxiansheng.intelligent.analysis.config.IntelligentAnalysisAutoConfiguration
2.combinations-intelligent-analysis-starter-demo
1.目录结构

2.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.sunxiansheng</groupId><artifactId>sunrays-demo</artifactId><version>1.0.5</version></parent><version>1.0.5</version><artifactId>combinations-intelligent-analysis-starter-demo</artifactId><dependencies><!-- combinations-intelligent-analysis-starter --><dependency><groupId>com.sunxiansheng</groupId><artifactId>combinations-intelligent-analysis-starter</artifactId><version>1.0.5</version></dependency><!-- common-log4j2-starter --><dependency><groupId>com.sunxiansheng</groupId><artifactId>common-log4j2-starter</artifactId><version>1.0.5</version></dependency></dependencies>
</project>
3.application.yml
spring:# 邮件配置mail:host: smtp.126.com # 邮箱服务商的SMTP服务器地址username: guest@126.com # 邮箱账户password: guest # 邮箱授权码或密码# RabbitMQ 配置rabbitmq:# 服务器地址host: guest# 用户名username: guest# 密码password: guest# 虚拟主机virtual-host: /# 端口port: 6783# 消费者配置listener:simple:acknowledge-mode: auto # 自动确认模式(消费者确认机制)retry:enabled: true # 开启重试机制max-attempts: 3 # 最大尝试次数initial-interval: 5000ms # 重试间隔时间(5s)multiplier: 2.0 # 重试时间间隔倍数stateless: true # false:有状态,true:无状态,如果是有状态的,每次重试都会发送到同一个队列
openai:api-key: guest
4.IntelligentAnalysisApplication.java 启动类
package com.sunxiansheng.intelligent.analysis;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** Description: 智能分析启动类** @Author sun* @Create 2025/1/1 19:29* @Version 1.0*/
@SpringBootApplication
public class IntelligentAnalysisApplication {public static void main(String[] args) {SpringApplication.run(IntelligentAnalysisApplication.class, args);}
}
5.工具类
1.MailUtil.java 发送邮件
package com.sunxiansheng.intelligent.analysis.utils;import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;
import java.io.UnsupportedEncodingException;/*** Description: 邮件工具类** @Author sun* @Create 2025/1/2 18:36* @Version 1.0*/
@Component
public class MailUtil {@Resourceprivate JavaMailSender mailSender;@Value("${spring.mail.username}")private String from;/*** 发送html邮件,没报错就是发送成功了** @param to 收件人* @param name 发件人名称* @param subject 邮件主题* @param htmlContent 邮件内容*/public void sendHtmlMessage(String to, String name, String subject, String htmlContent) throws UnsupportedEncodingException, MessagingException {MimeMessage message = mailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(message, true);// 创建邮件发送者地址helper.setFrom(new InternetAddress(MimeUtility.encodeText(name) + "<" + from + ">"));// 创建邮件发送者地址helper.setTo(new InternetAddress(MimeUtility.encodeText("接收方") + "<" + to + ">"));// 标题helper.setSubject(subject);// 第二个参数指定发送的是HTML格式helper.setText(htmlContent, true);mailSender.send(message);}
}
2.MethodCallChainUtil.java 根据堆栈信息从Gitee获取源码并提取每个方法的调用链
package com.sunxiansheng.intelligent.analysis.utils;import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseResult;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.MethodCallExpr;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class MethodCallChainUtil {/*** 从堆栈信息中提取每个方法的调用链** @param stackTrace 堆栈信息* @param giteeRepo Gitee 仓库地址* @param moduleName 模块名* @param branchName 分支名称* @param classPrefix 类路径前缀过滤器* @return 每个方法的调用链列表* @throws Exception 异常信息*/public static List<String> extractMethodCallChainsFromStackTrace(String stackTrace,String giteeRepo,String moduleName,String branchName,String classPrefix) throws Exception {List<String> callChains = new ArrayList<>();// 正则匹配堆栈中的类路径和方法名Pattern pattern = Pattern.compile("at ([\\w\\.]+)\\.([\\w]+)\\((\\w+\\.java):(\\d+)\\)");Matcher matcher = pattern.matcher(stackTrace);while (matcher.find()) {String classPath = matcher.group(1); // 类路径String methodName = matcher.group(2); // 方法名// 过滤掉不符合指定前缀的类if (!classPath.startsWith(classPrefix)) {continue;}// 从 Gitee 仓库获取类文件内容try {String classContent = readClassFileFromGitee(classPath, giteeRepo, moduleName, branchName);// 获取方法的调用链String methodCallChain = extractMethodCallChain(classContent, methodName);callChains.add("类: " + classPath + "\n" + methodCallChain);} catch (Exception e) {System.err.println("无法解析方法 " + methodName + " 于类: " + classPath);}}return callChains;}/*** 从 Gitee 仓库中读取类文件内容** @param classPath 类路径* @param giteeRepo Gitee 仓库地址* @param moduleName 模块名* @param branchName 分支名称* @return 类文件内容字符串* @throws Exception 如果类文件不存在或读取失败*/private static String readClassFileFromGitee(String classPath,String giteeRepo,String moduleName,String branchName) throws Exception {String filePath = "src/main/java/" + classPath.replace(".", "/") + ".java";String url = String.format("%s/raw/%s/%s/%s", giteeRepo, branchName, moduleName, filePath);HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();connection.setRequestMethod("GET");if (connection.getResponseCode() != 200) {throw new IllegalArgumentException("无法从 Gitee 获取类文件: " + url);}try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {return reader.lines().reduce((a, b) -> a + "\n" + b).orElse("");}}/*** 提取方法的调用链** @param classContent 类文件内容* @param methodName 方法名* @return 方法调用链* @throws Exception 如果解析失败或方法未找到*/private static String extractMethodCallChain(String classContent, String methodName) throws Exception {JavaParser javaParser = new JavaParser();ParseResult<CompilationUnit> parseResult = javaParser.parse(classContent);if (!parseResult.isSuccessful() || !parseResult.getResult().isPresent()) {throw new IllegalArgumentException("无法解析类文件内容");}CompilationUnit compilationUnit = parseResult.getResult().get();Optional<ClassOrInterfaceDeclaration> classDeclarationOpt = compilationUnit.findFirst(ClassOrInterfaceDeclaration.class);if (!classDeclarationOpt.isPresent()) {throw new IllegalArgumentException("未找到类定义");}ClassOrInterfaceDeclaration classDeclaration = classDeclarationOpt.get();// 使用队列递归查找调用链Queue<String> methodQueue = new LinkedList<>();Set<String> processedMethods = new HashSet<>();methodQueue.add(methodName);StringBuilder callChain = new StringBuilder();callChain.append("调用链:\n");// 开始递归提取方法内容及调用链while (!methodQueue.isEmpty()) {String currentMethodName = methodQueue.poll();if (processedMethods.contains(currentMethodName)) {continue; // 防止重复处理方法}processedMethods.add(currentMethodName);Optional<MethodDeclaration> methodOpt = classDeclaration.findAll(MethodDeclaration.class).stream().filter(method -> method.getNameAsString().equals(currentMethodName)).findFirst();if (!methodOpt.isPresent()) {callChain.append("未找到方法: ").append(currentMethodName).append("\n");continue;}MethodDeclaration method = methodOpt.get();callChain.append("方法: ").append(currentMethodName).append("\n").append(method).append("\n\n");// 查找调用此方法的其他方法for (MethodDeclaration callerMethod : classDeclaration.findAll(MethodDeclaration.class)) {if (!processedMethods.contains(callerMethod.getNameAsString())) {boolean callsTarget = callerMethod.findAll(MethodCallExpr.class).stream().anyMatch(call -> call.getNameAsString().equals(currentMethodName));if (callsTarget) {methodQueue.add(callerMethod.getNameAsString());callChain.append("方法 '").append(callerMethod.getNameAsString()).append("' 调用了方法 '").append(currentMethodName).append("':\n");callChain.append(callerMethod).append("\n\n");}}}}return callChain.toString();}
}
3.StringUtils.java 合并然后截取指定长度字符串
package com.sunxiansheng.intelligent.analysis.utils;import java.nio.charset.StandardCharsets;
import java.util.List;/*** Description: 字符串工具类** @Author sun* @Create 2025/1/2 15:26* @Version 1.0*/
public class StringUtils {/*** 截取字符串的前n个字符。* 如果字符串长度小于n,则返回原字符串。** @param input 原字符串* @param n 截取的字符数* @return 截取后的字符串*/public static String truncate(String input, int n) {if (input == null || n <= 0) {return "";}return input.length() > n ? input.substring(0, n) : input;}/*** 按字节数截取字符串(支持多字节字符)。* 如果字符串总字节数小于限制,直接返回原字符串。** @param input 原字符串* @param byteLimit 最大字节数* @return 截取后的字符串*/public static String truncateByBytes(String input, int byteLimit) {if (input == null || byteLimit <= 0) {return "";}byte[] bytes = input.getBytes(StandardCharsets.UTF_8);if (bytes.length <= byteLimit) {return input;}// 按字节截取字符串int endIndex = 0;int currentBytes = 0;for (int i = 0; i < input.length(); i++) {char c = input.charAt(i);// UTF-8编码:ASCII占1字节,其他占2或3字节currentBytes += (c <= 0x7F) ? 1 : (c <= 0x7FF ? 2 : 3);if (currentBytes > byteLimit) {break;}endIndex = i + 1;}return input.substring(0, endIndex);}/*** 截取字符串的前n个字符并在超长时添加省略号(...)。** @param input 原字符串* @param n 截取的字符数* @return 截取后的字符串(可能包含省略号)*/public static String truncateWithEllipsis(String input, int n) {if (input == null || n <= 0) {return "";}if (input.length() <= n) {return input;}return input.substring(0, n) + "...";}/*** 按字节截取字符串并添加省略号(...)。** @param input 原字符串* @param byteLimit 最大字节数* @return 截取后的字符串(可能包含省略号)*/public static String truncateByBytesWithEllipsis(String input, int byteLimit) {if (input == null || byteLimit <= 0) {return "";}String truncated = truncateByBytes(input, byteLimit - 3);return truncated.length() < input.length() ? truncated + "..." : truncated;}/*** 合并字符串列表,截取指定长度,并在超长时添加省略号。** @param stringList 字符串列表* @param maxLength 最大字符数* @return 截取后的字符串(可能包含省略号)*/public static String mergeAndTruncateWithEllipsis(List<String> stringList, int maxLength) {if (stringList == null || stringList.isEmpty() || maxLength <= 0) {return "";}// 合并字符串StringBuilder merged = new StringBuilder();for (String str : stringList) {if (str != null) {merged.append(str);}}// 截取字符串String result = merged.toString();if (result.length() > maxLength) {return result.substring(0, maxLength) + "...";}return result;}
}
6.ELKEntity.java ELK映射实体类
package com.sunxiansheng.intelligent.analysis.entity;import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;import java.io.Serializable;/*** Description: ELK实体类** @Author sun* @Create 2025/1/1 18:08* @Version 1.0*/
@Data
public class ELKEntity implements Serializable {private static final long serialVersionUID = 1L;private String traceId;private String thread;private String logger;private String throwable;private String module;private String level;private String timestamp;private String host;@JsonProperty("log_message")private String logMessage;
}
7.RabbitMQConfig.java
package com.sunxiansheng.intelligent.analysis.config;import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** Description: RabbitMQ配置类** @Author sun* @Create 2025/1/1 17:09* @Version 1.0*/
@Configuration
public class RabbitMQConfig {/*** 死信交换机** @return*/@Beanpublic DirectExchange dlxExchange() {return new DirectExchange("dlxExchange");}/*** 死信队列** @return*/@Beanpublic Queue dlxQueue() {return QueueBuilder.durable("dlxQueue").build();}/*** 死信队列绑定死信交换机** @return*/@Beanpublic Binding dlxBinding() {return BindingBuilder.bind(dlxQueue()).to(dlxExchange()).with("dlx.elk");}/*** 创建一个fanout类型的elk交换机** @return*/@Beanpublic FanoutExchange elkExchange() {return new FanoutExchange("elk.exchange");}/*** 创建一个elk队列,并设置死信交换机和死信路由键** @return*/@Beanpublic Queue elkQueue() {return QueueBuilder.durable("elkQueue").withArgument("x-dead-letter-exchange", "dlxExchange").withArgument("x-dead-letter-routing-key", "dlx.elk").lazy().build();}/*** 交换机和队列绑定*/@Beanpublic Binding binding() {return BindingBuilder.bind(elkQueue()).to(elkExchange());}
}
8.ElkListener.java 监听从Logstash中发送过来的日志消息
package com.sunxiansheng.intelligent.analysis.consumer;import com.sunxiansheng.intelligent.analysis.entity.ELKEntity;
import com.sunxiansheng.intelligent.analysis.utils.MailUtil;
import com.sunxiansheng.intelligent.analysis.utils.MethodCallChainUtil;
import com.sunxiansheng.intelligent.analysis.utils.StringUtils;
import com.sunxiansheng.openai.client.OpenAiClient;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.List;/*** Description: elk消息监听** @Author sun* @Create 2025/1/1 17:17* @Version 1.0*/
@Component
@Slf4j
public class ElkListener {@Resourceprivate OpenAiClient openAiClient;@Resourceprivate MailUtil mailUtil;private static final String to = "sunxiansehng@gmail.com";@RabbitListener(queues = "elkQueue")public void receive(ELKEntity message) throws Exception {String timestamp = message.getTimestamp();String logger = message.getLogger();String module = message.getModule();String throwable = message.getThrowable();String host = message.getHost();String logMessage = message.getLogMessage();analyze(throwable, module, timestamp, logger, host, logMessage);}public void analyze(String throwable, String module, String timestamp, String logger,String host, String logMessage) throws Exception {List<String> methodCallChains = MethodCallChainUtil.extractMethodCallChainsFromStackTrace(throwable,"https://gitee.com/qyxinhua_0/sunrays-framework",module,"master","com.sunxiansheng");// 如果方法调用链为空,则直接返回if (methodCallChains.isEmpty()) {sendMailWithDetails("线上报错(无AI分析)", "无", timestamp, logger, module, host, logMessage, throwable);log.info("方法调用链为空,无法分析问题");return;}String pattern = " 问题: 在这里总结一个问题标题\n" +" ----------------------------------------\n" +" 1. 问题产生原因:\n" +" 在这里写原因\n" +" ----------------------------------------\n" +" 2. 问题解决方式:\n" +" 在这里写解决方式\n" +" ----------------------------------------\n";String info = String.format("方法调用链:%s 异常信息:%s",StringUtils.mergeAndTruncateWithEllipsis(methodCallChains, 500),StringUtils.truncateWithEllipsis(throwable, 500));String question = String.format("我会给你我的方法调用链以及异常信息:\n%s\n" +"请帮我按照下面的格式去分析一下问题产生的原因和解决方式:\n%s",info,pattern);log.info("问题:{}", question);String aiAns = openAiClient.askAI("gpt-4o", question, false);log.info("AI回答:{}", aiAns);// 发送AI分析邮件sendMailWithDetails("线上报错(有AI分析)", aiAns, timestamp, logger, module, host, logMessage, throwable);}private void sendMailWithDetails(String subject, String analysisResult, String timestamp, String logger,String module, String host, String logMessage, String throwable) throws Exception {// 构建邮件的HTML格式内容String htmlContent = buildHtmlContent(subject, analysisResult, timestamp, logger, module, host, logMessage, throwable);// 发送邮件mailUtil.sendHtmlMessage(to, "SunRays-Framework", subject, htmlContent);}public String buildHtmlContent(String subject, String analysisResult, String timestamp, String logger,String module, String host, String logMessage, String throwable) {// 使用 Flexmark 将 analysisResult 转换为 HTMLParser parser = Parser.builder().build();HtmlRenderer renderer = HtmlRenderer.builder().build();String analysisHtml = renderer.render(parser.parse(analysisResult));// 构建 HTML 内容return "<html><body>" +"<h2>" + subject + "</h2>" +"<table border='1' cellpadding='10' cellspacing='0'>" +"<tr><td><strong>时间戳</strong></td><td>" + timestamp + "</td></tr>" +"<tr><td><strong>日志器</strong></td><td>" + logger + "</td></tr>" +"<tr><td><strong>模块</strong></td><td>" + module + "</td></tr>" +"<tr><td><strong>主机</strong></td><td>" + host + "</td></tr>" +"<tr><td><strong>日志信息</strong></td><td>" + logMessage + "</td></tr>" +"<tr><td><strong>异常信息</strong></td><td><pre>" + throwable + "</pre></td></tr>" +"</table>" +"<h3>AI分析结果:</h3>" +"<div>" + analysisHtml + "</div>" + // 渲染后的分析结果"</body></html>";}
}
9.DlxQueueListener.java 监听死信队列,确保消费者可靠性
package com.sunxiansheng.intelligent.analysis.consumer;import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sunxiansheng.intelligent.analysis.entity.ELKEntity;
import com.sunxiansheng.intelligent.analysis.utils.MailUtil;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;/*** Description: 死信队列监听** @Author sun* @Create 2025/1/2 12:39* @Version 1.0*/
@Component
@Slf4j
public class DlxQueueListener {@Resourceprivate MailUtil mailUtil;private static final String to = "sunxiansehng@gmail.com";/*** 死信队列消息消费方法** @param message 死信队列中的消息*/@RabbitListener(queues = "dlxQueue")public void receiveDlxMessage(Message message) {// 处理死信消息,通常是日志记录、报警或人工干预log.error("DlxQueueListener:接收到死信消息");// 获取消息体String messageBody = new String(message.getBody(), StandardCharsets.UTF_8);// 使用 Jackson 将消息体反序列化为 ELKEntity 对象ObjectMapper objectMapper = new ObjectMapper();// 忽略掉未知属性objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);try {ELKEntity elkEntity = objectMapper.readValue(messageBody, ELKEntity.class);String logger = elkEntity.getLogger();String throwable = elkEntity.getThrowable();String module = elkEntity.getModule();String timestamp = elkEntity.getTimestamp();String host = elkEntity.getHost();String logMessage = elkEntity.getLogMessage();sendMailWithDetails("线上报错(死信队列消息)", "无", timestamp, logger, module, host, logMessage, throwable);} catch (Exception e) {log.error("DlxQueueListene:反序列化消息失败", e);}}private void sendMailWithDetails(String subject, String analysisResult, String timestamp, String logger,String module, String host, String logMessage, String throwable) throws Exception {// 构建邮件的HTML格式内容String htmlContent = buildHtmlContent(subject, analysisResult, timestamp, logger, module, host, logMessage, throwable);// 发送邮件mailUtil.sendHtmlMessage(to, "SunRays-Framework", subject, htmlContent);}public String buildHtmlContent(String subject, String analysisResult, String timestamp, String logger,String module, String host, String logMessage, String throwable) {// 使用 Flexmark 将 analysisResult 转换为 HTMLParser parser = Parser.builder().build();HtmlRenderer renderer = HtmlRenderer.builder().build();String analysisHtml = renderer.render(parser.parse(analysisResult));// 构建 HTML 内容return "<html><body>" +"<h2>" + subject + "</h2>" +"<table border='1' cellpadding='10' cellspacing='0'>" +"<tr><td><strong>时间戳</strong></td><td>" + timestamp + "</td></tr>" +"<tr><td><strong>日志器</strong></td><td>" + logger + "</td></tr>" +"<tr><td><strong>模块</strong></td><td>" + module + "</td></tr>" +"<tr><td><strong>主机</strong></td><td>" + host + "</td></tr>" +"<tr><td><strong>日志信息</strong></td><td>" + logMessage + "</td></tr>" +"<tr><td><strong>异常信息</strong></td><td><pre>" + throwable + "</pre></td></tr>" +"</table>" +"<h3>AI分析结果:</h3>" +"<div>" + analysisHtml + "</div>" + // 渲染后的分析结果"</body></html>";}
}
10.结果展示
1.combinations-elk-starter-demo 直接抛出异常

2.combinations-intelligent-analysis-starter-demo 开始监听,一旦发生异常,就进行ai分析


3.AI分析的邮件


3.Logstash的配置以及系统执行流程
1.这个配置可以将消息发送到RabbitMQ
input {tcp {port => 9601codec => multiline {# 匹配日志行的开始(时间戳 + 分隔符 XYZ123DELIMITERXYZ123)pattern => "^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3} XYZ123DELIMITERXYZ123"negate => truewhat => "previous"auto_flush_interval => 5}}
}filter {dissect {mapping => {"message" => "%{timestamp} XYZ123DELIMITERXYZ123 [%{thread}] XYZ123DELIMITERXYZ123 %{level} XYZ123DELIMITERXYZ123 [PFTID:%{traceId}] XYZ123DELIMITERXYZ123 [Module:%{module}] XYZ123DELIMITERXYZ123 %{logger} XYZ123DELIMITERXYZ123 %{log_message} XYZ123DELIMITERXYZ123 %{throwable}"}remove_field => ["message"]}date {match => ["timestamp", "yyyy-MM-dd HH:mm:ss.SSS"]timezone => "Asia/Shanghai" # 根据您的实际时区进行设置target => "@timestamp"}}output {elasticsearch {hosts => ["http://guest:9200"] # 替换为您的 Elasticsearch 地址index => "java-logs-%{+YYYY.MM.dd}" # 按日期创建索引}# 将 level 为 ERROR 的日志发送到 RabbitMQif [level] == "ERROR" {rabbitmq {host => "guest"port => 6783user => "guest"password => "guest"vhost => "/"exchange => "elk.exchange"exchange_type => "fanout"message_properties => {"content_type" => "application/json""priority" => 1}}}# 调试用,输出到控制台stdout {codec => rubydebug}
}
2.AI智能日志分析系统执行流程
1.Logstash采集日志,当日志为ERROR的时候发送到RabbitMQ
2.RabbitMQ监听到日志进行处理
1.通过javaparser根据异常堆栈来解析出所有自己项目的groupId下的类路径和方法名
2.通过仓库名字+日志中的moudle名+类路径就可以从Gitee中获取这个类的代码
3.再使用javaparser去获取到这个方法的调用链,就是当前方法以及调用了当前方法的内容
4.将方法调用链和异常堆栈进行截取后交给AI智能分析日志,给出解决方案
5.为了解决OpenAI的接口调用速率限制,采用消费者指数退避重试机制加上死信队列的方式确保消息正常消费
6.考虑成本问题,只有当方法调用链不为空的时候才进行AI的日志分析,其余情况(方法调用链为空和死信队列)就会直接将错误日志的消息以邮件的形式发送
相关文章:
AI智能日志分析系统
文章目录 1.combinations-intelligent-analysis-starter1.目录结构2.pom.xml3.自动配置1.IntelligentAnalysisAutoConfiguration.java2.spring.factories 2.combinations-intelligent-analysis-starter-demo1.目录结构2.pom.xml3.application.yml4.IntelligentAnalysisApplicat…...
试用ChatGPT开发一个大语言模型聊天App
参考官方文档,安装android studio https://developer.android.com/studio/install?hlzh-cn 参考这个添加permission权限: https://blog.csdn.net/qingye_love/article/details/14452863 参考下面链接完成Android Studio 给项目添加 gradle 依赖 ht…...
Unity Epplus读取excel表并存入So文件举例
目录 此篇需要你有一定的阅读代码的能力,不然点开了也不知道在做什么 这是读表工具 So文件这么写 使用 此篇需要你有一定的阅读代码的能力,不然点开了也不知道在做什么 在此之前你需要知道epplus是干什么的,然后知道其基本api࿰…...
连接 OpenAI 模型:基础操作
在这一部分中,我们将介绍如何连接 OpenAI 模型,设置 API 密钥,并使用 Spring AI 的 ChatClient 与 OpenAI 模型进行简单的对话。Spring AI 为集成 OpenAI 模型提供了方便的工具,使得开发者能够更轻松地与 GPT 系列模型进行交互。 …...
[ Spring ] Spring Cloud Alibaba Message Stream Binder for RocketMQ 2025
文章目录 IntroduceProject StructureDeclare Plugins and ModulesApply Plugins and Add DependenciesSender PropertiesSender ApplicationSender ControllerReceiver PropertiesReceiver ApplicationReceiver Message HandlerCongratulationsAutomatically Send Message By …...
ubuntu 更新24LTS中断导致“系统出错且无法恢复,请联系系统管理员”
22LTS to 24LTS 更新过程中手jian把更新程序controlC导致的。 解决 目前企图完成更新来恢复,重启后有软件包冲突,sudo apt upgrade报冲突。无法进行。 将原来source.list重新 sudo dpkg --configure -a sudo apt install -f 这些都不管用。还是显示gno…...
力扣-链表-203 移除链表元素
思路1 处理头节点,然后遍历下一个节点,只有确保下一个节点不是要移除的节点时再跳到下一个节点 代码1 class Solution { public:ListNode* removeElements(ListNode* head, int val) {while(head ! nullptr && head->val val){head head…...
Unity中关于实现 管道水流+瀑布流动+大肠蠕动效果笔记
Unity中关于实现 管道水流瀑布流动大肠蠕动效果笔记 效果展示: 参考资料及链接: 1、如何在 Unity 中创建水效果 - 水弯曲教程 https://www.youtube.com/watch?v3CcWus6d_B8 关于补充个人技能中:顶点噪波影响网格着色器配合粒子实现水特效 …...
宏_wps_宏修改word中所有excel表格的格式_设置字体对齐格式_删除空行等
需求: 将word中所有excel表格的格式进行统一化,修改其中的数字类型为“宋体, 五号,右对齐, 不加粗,不倾斜”,其中的中文为“宋体, 五号, 不加粗,不倾斜” 数…...
Linux——网络(udp)
文章目录 目录 文章目录 前言 一、upd函数及接口介绍 1. 创建套接字 - socket 函数 2. 绑定地址和端口 - bind 函数 3. 发送数据 - sendto 函数 4. 接收数据 - recvfrom 函数 5. 关闭套接字 - close 函数 二、代码示例 1.服务端 2.客户端 总结 前言 Linux——网络基础…...
Oracle-Java JDBC 连接超时之后的认知纠正
背景 偶然读到熊老师的文章《老熊的三分地-JDBC中语句超时与事务》了解到:JAVA代码的最后正常断开数据库连接,在默认情况下,正常断开的数据库连接会自动提交没有提交的事务。 通过文章的测试JAVA程序,可以表明,JDB…...
自定义数据集使用框架的线性回归方法对其进行拟合
代码 import torch import numpy as np import torch.nn as nncriterion nn.MSELoss()data np.array([[-0.5, 7.7],[1.8, 98.5],[0.9, 57.8],[0.4, 39.2],[-1.4, -15.7],[-1.4, -37.3],[-1.8, -49.1],[1.5, 75.6],[0.4, 34.0],[0.8, 62.3]])x_data data[:, 0] y_data data…...
15天基础内容-5
day13 【String类、StringBuilder类】 主要内容 String类常用方法【重点】 String类案例【重点】 StringBuilder类【重点】 StringBuilder类常用方法【重点: append】 StringBuilder类案例【理解】 第一章String类 1.1 String类的判断方法 String类实现判断功能…...
82,【6】BUUCTF WEB .[CISCN2019 华东南赛区]Double Secret
进入靶场 提到了secret,那就访问 既然这样,那就传参看能不能报错 这个页面证明是有用的 传参长一点就会报错,传什么内容无所谓 所以网站是flask框架写的 有一个颜色深一点,点开看看 rc4加密url编码 import base64 from urllib…...
Android WebView 中网页被劫持的原因及解决方案
文章目录 一、原因分析二、解决方案一览三、解决方案代码案例3.1 使用 HTTPS3.2 验证 URL3.3 禁用 JavaScript3.4 使用安全的 WebView 设置3.5 监控网络请求3.6 使用安全的 DNS 四、案例深入分析4.1 问题4.2 分析 五、结论 在 Android 应用开发中,WebView 是一个常用…...
特朗普政府将开展新网络攻击
近日,特朗普政府已表态:减少物理战争,网络战将代替,以实现美国的全球优势。 特朗普也指示美国网络司令部可以在没有总统批准的情况下开展更广泛行动,尤其是应对一些突发事件,这其实成为了后续美国通过网络…...
快递代取项目Uniapp+若依后端管理
快递接单代取得uniappspringboot项目 实际效果图...
arcgis短整型变为长整型的处理方式
1.用QGIS的重构字段工具进行修改,亲测比arcgis的更改字段工具有用 2.更换低版本的arcgis10.2.2,亲测10.5和10.6都有这个毛病,虽然官方文档里面说的是10.6.1及以上 Arcgis10.2.2百度链接:https://pan.baidu.com/s/1HYTwgnBJsBug…...
06、Redis相关概念:缓存击穿、雪崩、穿透、预热、降级、一致性等
Redis相关概念:缓存击穿、雪崩、穿透、预热、降级、一致性等 Redis缓存雪崩、缓存击穿、缓存预热热点key、缓存降级、短链接、分布式锁秒杀、预减库存、 堆外缓存Redis架构设计、Redis动态刷新、Redis和DB双写一致性、过期删除策略、集群数据倾斜等一、缓存雪崩 缓…...
嵌入式基础 -- PCIe 控制器中断管理之MSI与MSI-X简介
PCIe 控制器中断管理技术文档 1. 背景 在现代计算机系统中,中断是设备与 CPU 通信的重要机制,PCIe 控制器提供了从传统线中断到基于消息的中断(MSI/MSI-X)的演进,以提升中断处理效率和可扩展性。x86 和 ARM 架构虽然…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
