diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysOperLog.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysOperLog.java index d815cb03..f2c96126 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysOperLog.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysOperLog.java @@ -252,4 +252,8 @@ public class SysOperLog extends BaseEntity { this.costTime = costTime; } + + public String toLogString(){ + return ""; + } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/log/AccessAddressUtils.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/log/AccessAddressUtils.java new file mode 100644 index 00000000..fa438e08 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/log/AccessAddressUtils.java @@ -0,0 +1,45 @@ +package com.ruoyi.system.log; + +import javax.servlet.http.HttpServletRequest; + +/** + * @Auther : qzy + * @Date: 2024/1/24 16:55 + **/ +public class AccessAddressUtils { + + + /** + * 获取用户真实IP地址,不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址, + * 参考文章: http://developer.51cto.com/art/201111/305181.htm + * + * 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值,究竟哪个才是真正的用户端的真实IP呢? + * 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。 + * + * 如:X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130, + * 192.168.1.100 + * + * 用户真实IP为: 192.168.1.110 + * @param request + * @return + */ + public static String getIpAddress(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + return ip; + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/log/LoggerUtils.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/log/LoggerUtils.java new file mode 100644 index 00000000..aafb2999 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/log/LoggerUtils.java @@ -0,0 +1,54 @@ +package com.ruoyi.system.log; + +import javax.servlet.http.HttpServletRequest; +import java.util.Objects; + +/** + * 日志记录所有请求 工具类 + * @Auther : qzy + * @Date: 2024/1/24 16:55 + **/ +public final class LoggerUtils { + + public static final String LOGGER_RETURN = "LOGGER_RETURN"; + + private LoggerUtils() {} + + /** + * 获取客户端ip地址 + * @param request + * @return + */ + public static String getCliectIp(HttpServletRequest request) + { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || Objects.equals(ip.trim(), "") || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || Objects.equals(ip.trim(), "") || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || Objects.equals(ip.trim(), "") || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + + // 多个路由时,取第一个非unknown的ip + final String[] arr = ip.split(","); + for (final String str : arr) { + if (!"unknown".equalsIgnoreCase(str)) { + ip = str; + break; + } + } + return ip; + } + + /** + * 判断是否为ajax请求 + * @param request + * @return + */ + public static String getRequestType(HttpServletRequest request) { + return request.getHeader("X-Requested-With"); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/log/aop/WebRequestLogAspect.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/log/aop/WebRequestLogAspect.java new file mode 100644 index 00000000..78a86045 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/log/aop/WebRequestLogAspect.java @@ -0,0 +1,142 @@ +package com.ruoyi.system.log.aop; + + +import com.alibaba.fastjson2.JSON; +import com.ruoyi.system.log.AccessAddressUtils; +import com.ruoyi.system.log.LoggerUtils; +import com.ruoyi.system.log.dto.LoggerEntity; +import org.apache.commons.lang3.RandomUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author : qzy + * @since : 2024/1/24 16:36 + * <p style='color:yellow'> + * 通用请求日志打印处理类,方便联调/测试,生产环境视情况可选择是否保留 + * + * </p> + **/ +@Aspect +@Component +public class WebRequestLogAspect { + + private static final Logger LOGGER = LoggerFactory.getLogger(WebRequestLogAspect.class); + + private final ThreadLocal<LoggerEntity> threadLocal = new ThreadLocal<>(); + + private final ThreadLocal<Long> startTimeLocal = new ThreadLocal<>(); + + + @Pointcut("within(com.ruoyi.*.controller.*)") + public void webRequestLog() { + } + + @Before("webRequestLog()") + public void doBefore(JoinPoint joinPoint) { + try { + // 接收到请求,记录请求内容 + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + if (requestAttributes == null) { + // 防止拦截的不是接口层,获取不到请求数据的情况 + threadLocal.remove(); + return; + } + ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes; + HttpServletRequest request = attributes.getRequest(); + //处理特殊请求,直接返回 +// if (request.getRequestURI().contains("admin/sysLog")) { +// threadLocal.remove(); +// return; +// } + + String method = request.getMethod(); + String params; + String post_code = "POST"; + if (post_code.equals(method)) { + Object[] paramsArray = joinPoint.getArgs(); + params = argsArrayToString(paramsArray); + } else { + //获取请求参数信息 + params = JSON.toJSONString(request.getParameterMap()); + } + + LoggerEntity optLog = new LoggerEntity(); + //设置请求方法 + optLog.setId(RandomUtils.nextLong()); + optLog.setMethodCode(request.getMethod()); + //设置请求类型(json|普通请求) + optLog.setTypeCode(LoggerUtils.getRequestType(request)); + //设置请求参数内容json字符串 + optLog.setParamData(params); + //设置请求地址 + optLog.setUri(request.getRequestURI()); + //设置sessionId + optLog.setSessionId(request.getSession().getId()); + //设置调用的java类 + optLog.setJavaBean(joinPoint.getSignature().getDeclaringTypeName()); + //设置调用的java方法 + optLog.setJavaMethod(joinPoint.getSignature().getName()); +// optLog.setParamData(params); + //设置调用方ip + optLog.setClientIp(AccessAddressUtils.getIpAddress(request)); + threadLocal.set(optLog); + startTimeLocal.set(System.currentTimeMillis()); + LOGGER.info(optLog.toLogStr()); + } catch (Exception e) { + LOGGER.error("***操作请求日志记录失败doBefore()***", e); + } + } + + @AfterReturning(returning = "result", pointcut = "webRequestLog()") + public void doAfterReturning(Object result) { + // 处理完请求,返回内容 + if (threadLocal.get() == null) { + return; + } + LoggerEntity optLog = threadLocal.get(); + Long startTime = startTimeLocal.get(); + optLog.setReturnData(JSON.toJSONString(result)); + optLog.setTimeConsuming((int) (System.currentTimeMillis() - startTime)); + LOGGER.info(optLog.toLogStr()); + threadLocal.remove(); + startTimeLocal.remove(); + } + + /** + * 请求参数拼装 + * @return String + */ + private String argsArrayToString(Object[] paramsArray) { + StringBuilder params = new StringBuilder(); + if (paramsArray != null) { + for (Object o : paramsArray) { + if (o instanceof MultipartFile || o instanceof MultipartFile[]) { + // 当导入文件时,不打印输出二进制 + params.append("上传文件 "); + } else { + try { + Object jsonObj = JSON.toJSON(o); + params.append(jsonObj.toString()).append(" "); + } catch (Exception e) { + LOGGER.debug("参数json解析异常,{}", e.getMessage()); + } + } + } + } + return params.toString().trim(); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/log/dto/LoggerEntity.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/log/dto/LoggerEntity.java new file mode 100644 index 00000000..ff7f432a --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/log/dto/LoggerEntity.java @@ -0,0 +1,216 @@ +package com.ruoyi.system.log.dto; + + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + * 日志记录所有请求 实体类 + * @Auther : qzy + * @Date: 2024/1/24 16:55 + **/ +public class LoggerEntity implements Serializable { + /** + * 编号 + */ + private Long id; + /** + * 客户端请求ip + */ + private String clientIp; + /** + * 客户端请求路径 + */ + private String uri; + /** + * 终端请求方式,普通请求,ajax请求 + */ + private String type; + /** + * 请求方式method,post,get等 + */ + private String method; + /** + * 请求参数内容,json + */ + private String paramData; + /** + * 请求接口唯一session标识 + */ + private String sessionId; + /** + * 请求时间 + */ + private Timestamp time; + /** + * 接口返回时间 + */ + private String returnTime; + /** + * 接口返回数据json + */ + private String returnData; + /** + * 请求时httpStatusCode代码,如:200,400,404等 + */ + private String httpStatusCode; + /** + * 请求耗时秒单位 + */ + private int timeConsuming; + private String javaBean; + private String javaMethod; + private String operaAdmin; + private String operaSource; + private String typeCode; + private String methodCode; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getClientIp() { + return clientIp; + } + + public void setClientIp(String clientIp) { + this.clientIp = clientIp; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public String getParamData() { + return paramData; + } + + public void setParamData(String paramData) { + this.paramData = paramData; + } + + public String getSessionId() { + return sessionId; + } + + public void setSessionId(String sessionId) { + this.sessionId = sessionId; + } + + public Timestamp getTime() { + return time; + } + + public void setTime(Timestamp time) { + this.time = time; + } + + public String getReturnTime() { + return returnTime; + } + + public void setReturnTime(String returnTime) { + this.returnTime = returnTime; + } + + public String getReturnData() { + return returnData; + } + + public void setReturnData(String returnData) { + this.returnData = returnData; + } + + public String getHttpStatusCode() { + return httpStatusCode; + } + + public void setHttpStatusCode(String httpStatusCode) { + this.httpStatusCode = httpStatusCode; + } + + public int getTimeConsuming() { + return timeConsuming; + } + + public void setTimeConsuming(int timeConsuming) { + this.timeConsuming = timeConsuming; + } + + public void setJavaBean(String javaBean) { + this.javaBean = javaBean; + } + + public String getJavaBean() { + return javaBean; + } + + public void setJavaMethod(String javaMethod) { + this.javaMethod = javaMethod; + } + + public String getJavaMethod() { + return javaMethod; + } + + public void setOperaAdmin(String operaAdmin) { + + this.operaAdmin = operaAdmin; + } + + public String getOperaAdmin() { + return operaAdmin; + } + + public void setOperaSource(String operaSource) { + this.operaSource = operaSource; + } + + public String getOperaSource() { + return operaSource; + } + + public void setTypeCode(String typeCode) { + this.typeCode = typeCode; + } + + public String getTypeCode() { + return typeCode; + } + + public void setMethodCode(String methodCode) { + this.methodCode = methodCode; + } + + public String getMethodCode() { + return methodCode; + } + + public String toLogStr(){ + return String.format("\n==>请求IP:{%s},\n==>请求路径:{%s} - {%s},\n==>方法名:{%s},\n==>入参:{%s},\n==>出参:{%s}", this.clientIp, this.methodCode, this.uri, this.javaMethod, this.paramData,this.returnData); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/log/package-info.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/log/package-info.java new file mode 100644 index 00000000..f7cf81b6 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/log/package-info.java @@ -0,0 +1,4 @@ +/** + * 统一请求日志处理工具 + */ +package com.ruoyi.system.log;