From ad10addbefd8c11c2d1acde2c91ccd7af9f77769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E5=AE=87=E5=A5=87?= Date: Mon, 24 Nov 2025 17:48:18 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=97=A5=E5=BF=97/?= =?UTF-8?q?=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruoyi/framework/notice/fs/FsNotice.java | 35 ++---- .../web/exception/GlobalExceptionHandler.java | 117 +++++++++++++----- .../com/ruoyi/system/domain/SysOperLog.java | 5 +- 3 files changed, 99 insertions(+), 58 deletions(-) diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/notice/fs/FsNotice.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/notice/fs/FsNotice.java index 1f56db8d9..833ae154c 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/notice/fs/FsNotice.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/notice/fs/FsNotice.java @@ -7,9 +7,9 @@ import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson2.JSON; import com.ruoyi.common.exception.ServiceException; -import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.framework.notice.AbstractNotice; import com.ruoyi.framework.notice.fs.entity.*; +import com.ruoyi.system.domain.SysOperLog; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -58,39 +58,29 @@ public class FsNotice extends AbstractNotice { /** * 发送异常信息给监控群 * - * @param e + * @param optLog */ - public void sendException2MonitorChat(T e) { - sendException2MonitorChat(null, e); - } - - /** - * 发送异常信息给监控群 - * - * @param e - */ - public void sendException2MonitorChat(String uri, T e) { + public void sendException2MonitorChat(final SysOperLog optLog) { if (Boolean.TRUE.equals(monitorSwitch)) { - String username = SecurityUtils.getUsernameSafe(); ThreadUtil.execAsync(() -> { try { //内容 FeiShuMsg.ZhCn zhCn = new FeiShuMsg.ZhCn(); - List> contentFields = new ArrayList<>(6); + List> contentFields = new ArrayList<>(7); contentFields.add(Collections.singletonList(FeiShuTextField .createText(CharSequenceUtil.format("环境:{}", env)))); - if (uri != null) { - contentFields.add(Collections.singletonList(FeiShuTextField - .createText(CharSequenceUtil.format("地址:{}", uri)))); - } contentFields.add(Collections.singletonList(FeiShuTextField - .createText(CharSequenceUtil.format("用户:{}", username)))); + .createText(CharSequenceUtil.format("地址:{}", optLog.getOperUrl())))); contentFields.add(Collections.singletonList(FeiShuTextField - .createText(CharSequenceUtil.format("时间:{}", DateUtil.now())))); + .createText(CharSequenceUtil.format("入参:{}", StrUtil.truncateUtf8(optLog.getOperParam(), 512))))); contentFields.add(Collections.singletonList(FeiShuTextField - .createText(CharSequenceUtil.format("异常:{}", e.getClass().getName())))); + .createText(CharSequenceUtil.format("用户:{}", optLog.getOperName())))); contentFields.add(Collections.singletonList(FeiShuTextField - .createText(CharSequenceUtil.format("错误信息:{}", StrUtil.truncateUtf8(e.getMessage(), 512))))); + .createText(CharSequenceUtil.format("时间:{}", DateUtil.formatDateTime(optLog.getOperTime()))))); + contentFields.add(Collections.singletonList(FeiShuTextField + .createText(CharSequenceUtil.format("异常:{}", optLog.getTitle())))); + contentFields.add(Collections.singletonList(FeiShuTextField + .createText(CharSequenceUtil.format("错误信息:{}", StrUtil.truncateUtf8(optLog.getErrorMsg(), 512))))); zhCn.setContent(contentFields); //消息体 FeiShuMsg feiShuMsg = new FeiShuMsg(); @@ -106,7 +96,6 @@ public class FsNotice extends AbstractNotice { } } - /** * 发送消息 * diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java index cb9a190a5..a0b78fc03 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java @@ -1,14 +1,19 @@ package com.ruoyi.framework.web.exception; +import cn.hutool.core.map.MapUtil; +import cn.hutool.json.JSONUtil; import com.ruoyi.common.constant.HttpStatus; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.exception.DemoModeException; import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.html.EscapeUtil; +import com.ruoyi.common.utils.ip.IpUtils; import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.framework.notice.fs.FsNotice; +import com.ruoyi.system.domain.SysOperLog; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.access.AccessDeniedException; @@ -21,6 +26,9 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import javax.servlet.http.HttpServletRequest; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; /** * 全局异常处理器 @@ -38,9 +46,9 @@ public class GlobalExceptionHandler @ExceptionHandler(AccessDeniedException.class) public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) { - String requestURI = request.getRequestURI(); - log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage()); - sendExceptionMsg(requestURI, e); + SysOperLog optLog = createOptLog(e, request); + log.error("请求地址'{}',权限校验失败'{}'", optLog.getOperUrl(), e.getMessage()); + sendExceptionMsg(optLog); return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权"); } @@ -51,9 +59,9 @@ public class GlobalExceptionHandler public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, HttpServletRequest request) { - String requestURI = request.getRequestURI(); - log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); - sendExceptionMsg(requestURI, e); + SysOperLog optLog = createOptLog(e, request); + log.error("请求地址'{}',不支持'{}'请求", optLog.getOperUrl(), e.getMethod()); + sendExceptionMsg(optLog); return AjaxResult.error(e.getMessage()); } @@ -63,9 +71,9 @@ public class GlobalExceptionHandler @ExceptionHandler(ServiceException.class) public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) { - String requestURI = request.getRequestURI(); - log.error("请求地址'{}',发生业务异常'{}'", requestURI, e.getMessage()); - sendExceptionMsg(requestURI, e); + SysOperLog optLog = createOptLog(e, request); + log.error("请求地址'{}',发生业务异常'{}'", optLog.getOperUrl(), e.getMessage()); + sendExceptionMsg(optLog); Integer code = e.getCode(); return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); } @@ -76,9 +84,9 @@ public class GlobalExceptionHandler */ @ExceptionHandler(IllegalArgumentException.class) public AjaxResult illegalArgumentException(IllegalArgumentException e, HttpServletRequest request) { - String requestURI = request.getRequestURI(); - log.error("请求地址'{}',发生校验异常'{}'", requestURI, e.getMessage()); - sendExceptionMsg(requestURI, e); + SysOperLog optLog = createOptLog(e, request); + log.error("请求地址'{}',发生校验异常'{}'", optLog.getOperUrl(), e.getMessage()); + sendExceptionMsg(optLog); return AjaxResult.error(e.getMessage()); } @@ -88,9 +96,9 @@ public class GlobalExceptionHandler @ExceptionHandler(MissingPathVariableException.class) public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) { - String requestURI = request.getRequestURI(); - log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e); - sendExceptionMsg(requestURI, e); + SysOperLog optLog = createOptLog(e, request); + log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", optLog.getOperUrl(), e); + sendExceptionMsg(optLog); return AjaxResult.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName())); } @@ -100,14 +108,14 @@ public class GlobalExceptionHandler @ExceptionHandler(MethodArgumentTypeMismatchException.class) public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) { - String requestURI = request.getRequestURI(); + SysOperLog optLog = createOptLog(e, request); String value = Convert.toStr(e.getValue()); if (StringUtils.isNotEmpty(value)) { value = EscapeUtil.clean(value); } - log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e); - sendExceptionMsg(requestURI, e); + log.error("请求参数类型不匹配'{}',发生系统异常.", optLog.getOperUrl(), e); + sendExceptionMsg(optLog); return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), value)); } @@ -117,9 +125,9 @@ public class GlobalExceptionHandler @ExceptionHandler(RuntimeException.class) public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) { - String requestURI = request.getRequestURI(); - log.error("请求地址'{}',发生未知异常.", requestURI, e); - sendExceptionMsg(requestURI, e); + SysOperLog optLog = createOptLog(e, request); + log.error("请求地址'{}',发生未知异常.", optLog.getOperUrl(), e); + sendExceptionMsg(optLog); return AjaxResult.error(e.getMessage()); } @@ -129,9 +137,9 @@ public class GlobalExceptionHandler @ExceptionHandler(Exception.class) public AjaxResult handleException(Exception e, HttpServletRequest request) { - String requestURI = request.getRequestURI(); - log.error("请求地址'{}',发生系统异常.", requestURI, e); - sendExceptionMsg(requestURI, e); + SysOperLog optLog = createOptLog(e, request); + log.error("请求地址'{}',发生系统异常.", optLog.getOperUrl(), e); + sendExceptionMsg(optLog); return AjaxResult.error(e.getMessage()); } @@ -141,9 +149,10 @@ public class GlobalExceptionHandler @ExceptionHandler(BindException.class) public AjaxResult handleBindException(BindException e, HttpServletRequest request) { + SysOperLog optLog = createOptLog(e, request); log.error(e.getMessage(), e); String message = e.getAllErrors().get(0).getDefaultMessage(); - sendExceptionMsg(request.getRequestURI(), e); + sendExceptionMsg(optLog); return AjaxResult.error(message); } @@ -153,10 +162,10 @@ public class GlobalExceptionHandler @ExceptionHandler(MethodArgumentNotValidException.class) public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) { - String requestURI = request.getRequestURI(); + SysOperLog optLog = createOptLog(e, request); String message = e.getBindingResult().getFieldError().getDefaultMessage(); - log.error("请求地址'{}',发生校验异常: {}", requestURI, message); - sendExceptionMsg(requestURI, e); + log.error("请求地址'{}',发生校验异常: {}", optLog.getOperUrl(), message); + sendExceptionMsg(optLog); return AjaxResult.error(message); } @@ -172,11 +181,53 @@ public class GlobalExceptionHandler /** * 发送异常消息 * - * @param uri - * @param e - * @param + * @param optLog */ - private void sendExceptionMsg(String uri, T e) { - SpringUtils.getBean(FsNotice.class).sendException2MonitorChat(uri, e); + private void sendExceptionMsg(SysOperLog optLog) { + log.info("【异常请求日志】{}", optLog); + SpringUtils.getBean(FsNotice.class).sendException2MonitorChat(optLog); + } + + private SysOperLog createOptLog(T e, HttpServletRequest request) { + SysOperLog syslog = new SysOperLog(); + syslog.setTitle(e.getClass().getName()); + syslog.setRequestMethod(request.getMethod()); + syslog.setOperName(SecurityUtils.getUsernameSafe()); + syslog.setOperUrl(request.getRequestURI()); + syslog.setOperIp(IpUtils.getIpAddr(request)); + syslog.setOperParam(JSONUtil.toJsonStr(getParamsMap(request))); + syslog.setStatus(1); + syslog.setErrorMsg(e.getMessage()); + syslog.setOperTime(new Date()); + return syslog; + } + + /** + * 获取参数 + * + * @param request + * @return + */ + private Map getParamsMap(HttpServletRequest request) { + Map requestParams = request.getParameterMap(); + if (MapUtil.isEmpty(requestParams)) { + return MapUtil.empty(); + } + Map params = new HashMap<>(); + for (Map.Entry entry : requestParams.entrySet()) { + String name = entry.getKey(); + String[] values = entry.getValue(); + StringBuilder valueStrBuilder = new StringBuilder(); + if (values != null) { + for (int i = 0; i < values.length; i++) { + valueStrBuilder.append(values[i]); + if (i != values.length - 1) { + valueStrBuilder.append(","); + } + } + } + params.put(name, valueStrBuilder.toString()); + } + return params; } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java index 3d46f39c2..824b02da5 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java @@ -1,18 +1,19 @@ package com.ruoyi.system.domain; -import java.util.Date; import com.fasterxml.jackson.annotation.JsonFormat; import com.ruoyi.common.annotation.Excel; import com.ruoyi.common.annotation.Excel.ColumnType; import com.ruoyi.common.core.domain.BaseEntity; import lombok.ToString; +import java.util.Date; + /** * 操作日志记录表 oper_log * * @author ruoyi */ -@ToString(callSuper = false) +@ToString(callSuper = false, exclude = {"operId"}) public class SysOperLog extends BaseEntity { private static final long serialVersionUID = 1L; From 3c39ea161a5b405edeb32fa8b68ee74aa3ecfd2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E5=AE=87=E5=A5=87?= Date: Mon, 24 Nov 2025 17:55:05 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=E6=95=8F=E6=84=9F=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/web/exception/GlobalExceptionHandler.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java index a0b78fc03..dd54dfc56 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java @@ -1,6 +1,7 @@ package com.ruoyi.framework.web.exception; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ArrayUtil; import cn.hutool.json.JSONUtil; import com.ruoyi.common.constant.HttpStatus; import com.ruoyi.common.core.domain.AjaxResult; @@ -12,6 +13,7 @@ import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.html.EscapeUtil; import com.ruoyi.common.utils.ip.IpUtils; import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.framework.aspectj.LogAspect; import com.ruoyi.framework.notice.fs.FsNotice; import com.ruoyi.system.domain.SysOperLog; import org.slf4j.Logger; @@ -216,6 +218,10 @@ public class GlobalExceptionHandler Map params = new HashMap<>(); for (Map.Entry entry : requestParams.entrySet()) { String name = entry.getKey(); + if (ArrayUtil.contains(LogAspect.EXCLUDE_PROPERTIES, name)) { + //敏感信息不记入日志 + continue; + } String[] values = entry.getValue(); StringBuilder valueStrBuilder = new StringBuilder(); if (values != null) { From c638d33343d42e58541243cd2d72f3439361b02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E5=AE=87=E5=A5=87?= Date: Mon, 24 Nov 2025 18:51:56 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruoyi/framework/aspectj/LogAspect.java | 2 +- .../web/exception/GlobalExceptionHandler.java | 54 ++++++++++++------- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java index ef6b37174..cdb8092c4 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java @@ -116,7 +116,7 @@ public class LogAspect // 保存数据库 // AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); // 输出到日志文件 - log.info("【接口请求日志】{}", operLog); + log.info("【接口日志】{}", operLog); } catch (Exception exp) diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java index dd54dfc56..296beb155 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java @@ -2,6 +2,7 @@ package com.ruoyi.framework.web.exception; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ArrayUtil; +import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.ruoyi.common.constant.HttpStatus; import com.ruoyi.common.core.domain.AjaxResult; @@ -11,6 +12,7 @@ import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.html.EscapeUtil; +import com.ruoyi.common.utils.http.HttpHelper; import com.ruoyi.common.utils.ip.IpUtils; import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.framework.aspectj.LogAspect; @@ -186,7 +188,7 @@ public class GlobalExceptionHandler * @param optLog */ private void sendExceptionMsg(SysOperLog optLog) { - log.info("【异常请求日志】{}", optLog); + log.info("【异常日志】{}", optLog); SpringUtils.getBean(FsNotice.class).sendException2MonitorChat(optLog); } @@ -210,29 +212,41 @@ public class GlobalExceptionHandler * @param request * @return */ - private Map getParamsMap(HttpServletRequest request) { + private Map getParamsMap(HttpServletRequest request) { + Map params = new HashMap<>(); Map requestParams = request.getParameterMap(); - if (MapUtil.isEmpty(requestParams)) { - return MapUtil.empty(); - } - Map params = new HashMap<>(); - for (Map.Entry entry : requestParams.entrySet()) { - String name = entry.getKey(); - if (ArrayUtil.contains(LogAspect.EXCLUDE_PROPERTIES, name)) { - //敏感信息不记入日志 - continue; - } - String[] values = entry.getValue(); - StringBuilder valueStrBuilder = new StringBuilder(); - if (values != null) { - for (int i = 0; i < values.length; i++) { - valueStrBuilder.append(values[i]); - if (i != values.length - 1) { - valueStrBuilder.append(","); + if (MapUtil.isNotEmpty(requestParams)) { + for (Map.Entry entry : requestParams.entrySet()) { + String name = entry.getKey(); + if (ArrayUtil.contains(LogAspect.EXCLUDE_PROPERTIES, name)) { + //敏感信息不记入日志 + continue; + } + String[] values = entry.getValue(); + StringBuilder valueStrBuilder = new StringBuilder(); + if (values != null) { + for (int i = 0; i < values.length; i++) { + valueStrBuilder.append(values[i]); + if (i != values.length - 1) { + valueStrBuilder.append(","); + } } } + params.put(name, valueStrBuilder.toString()); + } + } + String requestBodyStr = HttpHelper.getBodyString(request); + if (JSONUtil.isTypeJSONObject(requestBodyStr)) { + JSONObject requestBody = JSONUtil.parseObj(requestBodyStr); + for (Map.Entry entry : requestBody.entrySet()) { + String name = entry.getKey(); + if (ArrayUtil.contains(LogAspect.EXCLUDE_PROPERTIES, name)) { + //敏感信息不记入日志 + continue; + } + Object value = entry.getValue(); + params.put(name, value); } - params.put(name, valueStrBuilder.toString()); } return params; }