当前位置: 首页 > news >正文

05_项目集成飞书预警

05_项目集成飞书预警

一、推送信息格式:

在这里插入图片描述

二、切面类及请求上下文信息类

请求上下文信息类:

/*** @desc: 请求上下文信息类* @author: sqnugy* @date: 2025/5/8**/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiContext {private String methodName;private String className;private String argsJson;
}

请求上下文信息保存类(通过 ThreadLocal 保存)

/*** @desc: ThreadLocal 存储请求上下文信息(ApiContext)* @author: sqnugy* @date: 2025/5/8**/
public class ApiContextHolder {private static final ThreadLocal<ApiContext> contextHolder = new ThreadLocal<>();public static void set(ApiContext context) {contextHolder.set(context);}public static ApiContext get() {return contextHolder.get();}public static void clear() {contextHolder.remove();}
}

ApiOperation 注解切面类:

import cn.jcs.boot.video.review.util.JsonUtils;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.slf4j.MDC;import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;/*** @desc: ApiOperation 注解的切面类,通过 ApiOperaton 注解进行织入切面,并将获取到的请求的类名、方法名、入参 保存到上下文中* @author: sqnugy* @date: 2025/5/8**/
@Aspect
@Component
@Slf4j
public class ApiOperationAspect {/** 以 @ApiOperation 注解为切点,凡是添加 @ApiOperation 的方法,都会执行环绕中的代码 */@Pointcut("@annotation(io.swagger.annotations.ApiOperation)")public void apiOperation() {}/*** 环绕* @param joinPoint* @return* @throws Throwable*/@Around("apiOperation()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {try {long startTime = System.currentTimeMillis();MDC.put("traceId", UUID.randomUUID().toString());String className = joinPoint.getTarget().getClass().getSimpleName();String methodName = joinPoint.getSignature().getName();Object[] args = joinPoint.getArgs();String argsJsonStr = Arrays.stream(args).map(toJsonStr()).collect(Collectors.joining(", "));String description = getApiOperationDescription(joinPoint);log.info("====== 请求开始: [{}], 入参: {}, 请求类: {}, 请求方法: {} =================================== ",description, argsJsonStr, className, methodName);// 保存上下文信息ApiContext apiContext = new ApiContext();apiContext.setClassName(className);apiContext.setMethodName(methodName);apiContext.setArgsJson(argsJsonStr);//将上下文信息保存到 ThreadLocal 内ApiContextHolder.set(apiContext);Object result = joinPoint.proceed();long executionTime = System.currentTimeMillis() - startTime;String resultJson = JsonUtils.toJsonString(result);log.info("====== 请求结束: [{}], 耗时: {}ms, 出参: {} =================================== ",description, executionTime, resultJson);return result;} finally {MDC.clear();}}/*** 获取注解的描述信息* @param joinPoint* @return*/private String getApiOperationDescription(ProceedingJoinPoint joinPoint) {// 1. 从 ProceedingJoinPoint 获取 MethodSignatureMethodSignature signature = (MethodSignature) joinPoint.getSignature();// 2. 使用 MethodSignature 获取当前被注解的 MethodMethod method = signature.getMethod();// 3. 从 Method 中提取 LogExecution 注解ApiOperation apiOperatiog = method.getAnnotation(ApiOperation.class);// 4. 从 LogExecution 注解中获取 description 属性return apiOperatiog.value();}/*** 转 JSON 字符串* @return*/private Function<Object, String> toJsonStr() {return arg -> JsonUtils.toJsonString(arg);}}

三、全局异常处理类

import cn.hutool.Hutool;
import cn.hutool.json.JSONUtil;
import cn.jcs.boot.video.review.common.CommonResult;
import cn.jcs.boot.video.review.common.aspect.ApiContext;
import cn.jcs.boot.video.review.common.aspect.ApiContextHolder;
import cn.jcs.boot.video.review.util.FeishuUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.HandlerMethod;import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Parameter;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author sqnugy* @version 1.0* @desc 全局的异常处理类* @date 2025/5/7 19:49*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {/*** 业务异常处理** @param e 捕捉的异常* @return*/@ExceptionHandler(BusinessException.class)public CommonResult<?> handlerMallServiceException(BusinessException e) {log.error("业务异常", e);IErrorCode errorCode = e.getErrorCode();if (errorCode != null) {return CommonResult.failed(errorCode);}e.printStackTrace();try {notifyFeishu(e, "业务异常");} catch (Exception e1) {e1.printStackTrace();}return CommonResult.failed(e.getMessage());}/*** 表单校验** @param e 参数校验异常* @return*/@ExceptionHandler(MethodArgumentNotValidException.class)public CommonResult<?> handlerValidException(MethodArgumentNotValidException e) {log.error("表单校验异常", e);BindingResult bindingResult = e.getBindingResult();List<ObjectError> allErrors = bindingResult.getAllErrors();Map<String, String> message = new HashMap<>();for (ObjectError error : allErrors) {FieldError fe = (FieldError) error;message.put(fe.getField(), error.getDefaultMessage());}FeishuUtil.send(e.getMessage());try {notifyFeishu(e, "参数校验异常");} catch (Exception e1) {e1.printStackTrace();}return CommonResult.validateFailed("表单数据错误", message);}/*** 全局异常处理** @param e* @param handlerMethod* @return*/@ExceptionHandler(Exception.class)public CommonResult<?> handlerException(Exception e, HandlerMethod handlerMethod) {log.error("报错方法名字 [{}]", handlerMethod.getMethod().getName(), e);try {notifyFeishu(e, handlerMethod);} catch (Exception e1) {e1.printStackTrace();}return CommonResult.failed("系统异常,请稍后重试");}private String getStackTraceAsString(Throwable throwable) {StringWriter sw = new StringWriter();PrintWriter pw = new PrintWriter(sw);throwable.printStackTrace(pw);return sw.toString();}private void notifyFeishu(Exception e, String title) {try {ApiContext context = ApiContextHolder.get();String methodName = context != null ? context.getMethodName() : "未知方法";String className = context != null ? context.getClassName() : "未知类";String argsJson = context != null ? context.getArgsJson() : "无入参";String stackTrace = getStackTraceAsString(e);String message = String.format("🚨 %s 异常告警\n" +"📍 类名:%s\n" +"🔧 方法:%s\n" +"📥 入参:%s\n" +"🧵 异常信息:%s\n" +"📄 堆栈信息:\n%s",title,className,methodName,argsJson,e.getMessage(),stackTrace);FeishuUtil.send(message);} catch (Exception innerEx) {innerEx.printStackTrace();} finally {ApiContextHolder.clear();}}private void notifyFeishu(Exception e, HandlerMethod handlerMethod) {try {ApiContext context = ApiContextHolder.get();String params = context != null ? context.getArgsJson() : "获取失败";String stackTrace = getStackTraceAsString(e);String message = String.format("❗ 全局异常 异常告警\n📌 方法:%s.%s\n📥 入参:%s\n🧵 异常堆栈:\n%s",context != null ? context.getClassName() : "未知类",handlerMethod.getMethod().getName(),params,stackTrace);FeishuUtil.send(message);} catch (Exception innerEx) {innerEx.printStackTrace();} finally {ApiContextHolder.clear(); // 避免内存泄漏}}}

四、飞书通知工具类

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;import java.util.HashMap;
import java.util.Map;/*** @author sqnugy* @Date 2025/5/7* @description:*/
public class FeishuUtil {private final static String URL = "https://open.feishu.cn/open-apis/bot/v2/hook/e6eb4bab-5exxx"; #机器人链接public static void send(String feishuMsg){Map<String, Object> params = new HashMap<>();params.put("msg_type", "text");Map<String, Object> data = new HashMap<>();data.put("text", feishuMsg);params.put("content", data);String body = JSON.toJSONString(params);String result = HttpUtil.post(URL, body);}}
d(String feishuMsg){Map<String, Object> params = new HashMap<>();params.put("msg_type", "text");Map<String, Object> data = new HashMap<>();data.put("text", feishuMsg);params.put("content", data);String body = JSON.toJSONString(params);String result = HttpUtil.post(URL, body);}}

相关文章:

  • 2025 EAU UTUC指南学习笔记②:分期分级全梳理,科研的靶点可能藏在分层逻辑中
  • 数据结构(四)——栈的应用—数制转换
  • Vue3中emits和emit
  • App Store支付新政重构跨境电商生态:eBay卖家的突围之道
  • ABP vNext + gRPC 实现服务间高速通信
  • 【嵌入式面试高频知识点】-wifi相关
  • [硬件电路-18]:MCU - LPC1765FBD100是恩智浦(NXP)半导体推出的一款基于ARM Cortex-M3内核的高性能32位微控制器
  • Python3 上下文管理器:优雅管理资源的艺术
  • Java复习笔记-基础
  • Python cv2特征检测与描述:从理论到实战
  • Python量化交易Backtrader技术指标的实现
  • 【嵌入式开发-CAN】
  • ProfiNet与CANopen:新能源时代的“语言翻译官”
  • MySQL事务隔离机制与并发控制策略
  • Java详解LeetCode 热题 100(13):LeetCode 53:最大子数组和(Maximum Subarray)详解
  • maven 依赖冲突异常分析
  • Java基础
  • matlab稳定求解高精度二维对流扩散方程
  • 线代第二章矩阵第五、六、七节矩阵的转置、方阵的行列式、方阵的伴随矩阵
  • 初始图形学(8)
  • 欧洲承诺投资6亿欧元吸引外国科学家
  • 视频丨习近平同普京会谈:共同弘扬正确二战史观,维护联合国权威和地位
  • 丰田汽车:美国关税或导致4、5月损失1800亿日元,新财年净利润下滑三成
  • 深入贯彻中央八项规定精神学习教育中央第一指导组指导督导河北省见面会召开
  • 万里云端遇见上博--搭乘“上博号”主题飞机体验记
  • 网民反映“潜水时遭遇服务质量不佳”,三亚开展核查调查