Commit 70af2bc78c1dfbf516f6a8589597b251aac8466a

Authored by YunaiV
1 parent fe94e459

1. 增加 API 访问日志、异常日志表

2. 调整过滤器的顺序
ruoyi-common/src/main/resources/logback.xml deleted
1 -<?xml version="1.0" encoding="UTF-8"?>  
2 -<configuration>  
3 - <!-- 日志存放路径 -->  
4 - <property name="log.path" value="/home/ruoyi/logs" />  
5 - <!-- 日志输出格式 -->  
6 - <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />  
7 -  
8 - <!-- 控制台输出 -->  
9 - <appender name="console" class="ch.qos.logback.core.ConsoleAppender">  
10 - <encoder>  
11 - <pattern>${log.pattern}</pattern>  
12 - </encoder>  
13 - </appender>  
14 -  
15 - <!-- 系统日志输出 -->  
16 - <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">  
17 - <file>${log.path}/sys-info.log</file>  
18 - <!-- 循环政策:基于时间创建日志文件 -->  
19 - <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
20 - <!-- 日志文件名格式 -->  
21 - <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>  
22 - <!-- 日志最大的历史 60天 -->  
23 - <maxHistory>60</maxHistory>  
24 - </rollingPolicy>  
25 - <encoder>  
26 - <pattern>${log.pattern}</pattern>  
27 - </encoder>  
28 - <filter class="ch.qos.logback.classic.filter.LevelFilter">  
29 - <!-- 过滤的级别 -->  
30 - <level>INFO</level>  
31 - <!-- 匹配时的操作:接收(记录) -->  
32 - <onMatch>ACCEPT</onMatch>  
33 - <!-- 不匹配时的操作:拒绝(不记录) -->  
34 - <onMismatch>DENY</onMismatch>  
35 - </filter>  
36 - </appender>  
37 -  
38 - <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">  
39 - <file>${log.path}/sys-error.log</file>  
40 - <!-- 循环政策:基于时间创建日志文件 -->  
41 - <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
42 - <!-- 日志文件名格式 -->  
43 - <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>  
44 - <!-- 日志最大的历史 60天 -->  
45 - <maxHistory>60</maxHistory>  
46 - </rollingPolicy>  
47 - <encoder>  
48 - <pattern>${log.pattern}</pattern>  
49 - </encoder>  
50 - <filter class="ch.qos.logback.classic.filter.LevelFilter">  
51 - <!-- 过滤的级别 -->  
52 - <level>ERROR</level>  
53 - <!-- 匹配时的操作:接收(记录) -->  
54 - <onMatch>ACCEPT</onMatch>  
55 - <!-- 不匹配时的操作:拒绝(不记录) -->  
56 - <onMismatch>DENY</onMismatch>  
57 - </filter>  
58 - </appender>  
59 -  
60 - <!-- 用户访问日志输出 -->  
61 - <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">  
62 - <file>${log.path}/sys-user.log</file>  
63 - <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
64 - <!-- 按天回滚 daily -->  
65 - <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>  
66 - <!-- 日志最大的历史 60天 -->  
67 - <maxHistory>60</maxHistory>  
68 - </rollingPolicy>  
69 - <encoder>  
70 - <pattern>${log.pattern}</pattern>  
71 - </encoder>  
72 - </appender>  
73 -  
74 - <!-- 系统模块日志级别控制 -->  
75 - <logger name="com.ruoyi" level="info" />  
76 - <!-- Spring日志级别控制 -->  
77 - <logger name="org.springframework" level="warn" />  
78 -  
79 - <root level="info">  
80 - <appender-ref ref="console" />  
81 - </root>  
82 -  
83 - <!--系统操作日志-->  
84 - <root level="info">  
85 - <appender-ref ref="file_info" />  
86 - <appender-ref ref="file_error" />  
87 - </root>  
88 -  
89 - <!--系统用户操作日志-->  
90 - <logger name="sys-user" level="info">  
91 - <appender-ref ref="sys-user"/>  
92 - </logger>  
93 -</configuration>  
94 \ No newline at end of file 0 \ No newline at end of file
src/main/java/cn/iocoder/dashboard/common/enums/UserTypeEnum.java 0 → 100644
  1 +package cn.iocoder.dashboard.common.enums;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * 全局用户类型枚举
  8 + */
  9 +@AllArgsConstructor
  10 +@Getter
  11 +public enum UserTypeEnum {
  12 +
  13 + MEMBER(1, "会员"), // 面向 c 端,普通用户
  14 + ADMIN(2, "管理员"); // 面向 b 端,管理后台
  15 +
  16 + /**
  17 + * 类型
  18 + */
  19 + private final Integer value;
  20 + /**
  21 + * 类型名
  22 + */
  23 + private final String name;
  24 +
  25 +}
src/main/java/cn/iocoder/dashboard/framework/logger/apilog/config/ApiLogConfiguration.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.logger.apilog.config;
  2 +
  3 +import cn.iocoder.dashboard.framework.logger.apilog.core.filter.ApiAccessLogFilter;
  4 +import cn.iocoder.dashboard.framework.web.config.WebProperties;
  5 +import cn.iocoder.dashboard.framework.web.core.enums.FilterOrderEnum;
  6 +import org.springframework.boot.web.servlet.FilterRegistrationBean;
  7 +import org.springframework.context.annotation.Bean;
  8 +import org.springframework.context.annotation.Configuration;
  9 +
  10 +import javax.servlet.Filter;
  11 +
  12 +@Configuration
  13 +public class ApiLogConfiguration {
  14 +
  15 + /**
  16 + * 创建 ApiAccessLogFilter Bean,记录 API 请求日志
  17 + */
  18 + @Bean
  19 + public FilterRegistrationBean<ApiAccessLogFilter> apiAccessLogFilter(WebProperties webProperties) {
  20 + ApiAccessLogFilter filter = new ApiAccessLogFilter(webProperties);
  21 + return createFilterBean(filter, FilterOrderEnum.API_ACCESS_LOG_FILTER);
  22 + }
  23 +
  24 + private static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {
  25 + FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter);
  26 + bean.setOrder(order);
  27 + return bean;
  28 + }
  29 +
  30 +}
src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/filter/ApiAccessLogFilter.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.logger.apilog.core.filter;
  2 +
  3 +import cn.hutool.extra.servlet.ServletUtil;
  4 +import cn.iocoder.dashboard.framework.logger.apilog.core.service.dto.ApiAccessLogCreateDTO;
  5 +import cn.iocoder.dashboard.framework.web.config.WebProperties;
  6 +import cn.iocoder.dashboard.util.servlet.ServletUtils;
  7 +import lombok.AllArgsConstructor;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.springframework.web.filter.OncePerRequestFilter;
  10 +
  11 +import javax.servlet.FilterChain;
  12 +import javax.servlet.ServletException;
  13 +import javax.servlet.http.HttpServletRequest;
  14 +import javax.servlet.http.HttpServletResponse;
  15 +import java.io.IOException;
  16 +import java.util.Date;
  17 +import java.util.Map;
  18 +
  19 +/**
  20 + * API 访问日志 Filter
  21 + *
  22 + * @author 芋道源码
  23 + */
  24 +@AllArgsConstructor
  25 +@Slf4j
  26 +public class ApiAccessLogFilter extends OncePerRequestFilter {
  27 +
  28 + private final WebProperties webProperties;
  29 +
  30 + @Override
  31 + protected boolean shouldNotFilter(HttpServletRequest request) {
  32 + return !request.getRequestURI().startsWith(webProperties.getApiPrefix());
  33 + }
  34 +
  35 + @Override
  36 + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  37 + throws ServletException, IOException {
  38 + // 获得开始时间
  39 + Date startTime = new Date();
  40 + // 提前获得参数,避免 XssFilter 过滤处理
  41 + Map<String, String> queryString = ServletUtil.getParamMap(request);
  42 + String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtil.getBody(request) : null;
  43 +
  44 + try {
  45 + // 继续过滤器
  46 + filterChain.doFilter(request, response);
  47 + // 正常执行,记录日志
  48 + createApiAccessLog(request, startTime, queryString, requestBody, null);
  49 + } catch (Exception ex) {
  50 + // 异常执行,记录日志
  51 + createApiAccessLog(request, startTime, queryString, requestBody, ex);
  52 + throw ex;
  53 + }
  54 + }
  55 +
  56 + private void createApiAccessLog(HttpServletRequest request, Date startTime,
  57 + Map<String, String> queryString, String requestBody, Exception ex) {
  58 + try {
  59 + ApiAccessLogCreateDTO createDTO = this.buildApiAccessLogDTO(request, startTime, queryString, requestBody, ex);
  60 +
  61 + } catch (Exception e) {
  62 + log.error("[createApiAccessLog][url({}) 发生异常]", request.getRequestURI(), ex);
  63 + }
  64 + }
  65 +
  66 + private ApiAccessLogCreateDTO buildApiAccessLogDTO(HttpServletRequest request, Date startTime,
  67 + Map<String, String> queryString, String requestBody, Exception ex) {
  68 + return null;
  69 + }
  70 +
  71 +}
src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/service/dto/ApiAccessLogCreateDTO.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.logger.apilog.core.service.dto;
  2 +
  3 +import javax.validation.constraints.NotNull;
  4 +import java.util.Date;
  5 +
  6 +/**
  7 + * API 访问日志创建 DTO
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +public class ApiAccessLogCreateDTO {
  12 +
  13 + /**
  14 + * 链路追踪编号
  15 + */
  16 + private String traceId;
  17 + /**
  18 + * 用户编号
  19 + */
  20 + private Integer userId;
  21 + /**
  22 + * 用户类型
  23 + */
  24 + private Integer userType;
  25 + /**
  26 + * 应用名
  27 + */
  28 + @NotNull(message = "应用名不能为空")
  29 + private String applicationName;
  30 +
  31 + /**
  32 + * 请求方法名
  33 + */
  34 + @NotNull(message = "http 请求方法不能为空")
  35 + private String requestMethod;
  36 + /**
  37 + * 访问地址
  38 + */
  39 + @NotNull(message = "访问地址不能为空")
  40 + private String requestUrl;
  41 + /**
  42 + * 请求参数
  43 + */
  44 + @NotNull(message = "请求参数不能为空")
  45 + private String requestParams;
  46 + /**
  47 + * 用户 IP
  48 + */
  49 + @NotNull(message = "ip 不能为空")
  50 + private String userIp;
  51 + /**
  52 + * 浏览器 UA
  53 + */
  54 + @NotNull(message = "User-Agent 不能为空")
  55 + private String userAgent;
  56 +
  57 + /**
  58 + * 开始请求时间
  59 + */
  60 + @NotNull(message = "开始请求时间不能为空")
  61 + private Date startTime;
  62 + /**
  63 + * 结束请求时间
  64 + */
  65 + @NotNull(message = "结束请求时间不能为空")
  66 + private Date endTime;
  67 + /**
  68 + * 执行时长,单位:毫秒
  69 + */
  70 + @NotNull(message = "执行时长不能为空")
  71 + private Integer duration;
  72 + /**
  73 + * 结果码
  74 + */
  75 + @NotNull(message = "错误码不能为空")
  76 + private Integer resultCode;
  77 + /**
  78 + * 结果提示
  79 + */
  80 + private String resultMsg;
  81 +
  82 +}
src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/service/package-info.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.logger.apilog.core.service;
src/main/java/cn/iocoder/dashboard/framework/security/core/handler/LogoutSuccessHandlerImpl.java
@@ -37,6 +37,5 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { @@ -37,6 +37,5 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
37 } 37 }
38 // 返回成功 38 // 返回成功
39 ServletUtils.writeJSON(response, null); 39 ServletUtils.writeJSON(response, null);
40 -// ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.OK.value(), "退出成功")));  
41 } 40 }
42 } 41 }
src/main/java/cn/iocoder/dashboard/framework/web/config/WebConfiguration.java
1 package cn.iocoder.dashboard.framework.web.config; 1 package cn.iocoder.dashboard.framework.web.config;
2 2
  3 +import cn.iocoder.dashboard.framework.web.core.enums.FilterOrderEnum;
3 import cn.iocoder.dashboard.framework.web.core.filter.RequestBodyCacheFilter; 4 import cn.iocoder.dashboard.framework.web.core.filter.RequestBodyCacheFilter;
4 import cn.iocoder.dashboard.framework.web.core.filter.XssFilter; 5 import cn.iocoder.dashboard.framework.web.core.filter.XssFilter;
5 import org.springframework.boot.context.properties.EnableConfigurationProperties; 6 import org.springframework.boot.context.properties.EnableConfigurationProperties;
  7 +import org.springframework.boot.web.servlet.FilterRegistrationBean;
6 import org.springframework.context.annotation.Bean; 8 import org.springframework.context.annotation.Bean;
7 import org.springframework.context.annotation.Configuration; 9 import org.springframework.context.annotation.Configuration;
8 -import org.springframework.core.annotation.Order;  
9 import org.springframework.util.PathMatcher; 10 import org.springframework.util.PathMatcher;
10 import org.springframework.web.bind.annotation.RestController; 11 import org.springframework.web.bind.annotation.RestController;
11 import org.springframework.web.cors.CorsConfiguration; 12 import org.springframework.web.cors.CorsConfiguration;
@@ -15,10 +16,8 @@ import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; @@ -15,10 +16,8 @@ import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
15 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 16 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
16 17
17 import javax.annotation.Resource; 18 import javax.annotation.Resource;
  19 +import javax.servlet.Filter;
18 20
19 -/**  
20 - * Web 配置类  
21 - */  
22 @Configuration 21 @Configuration
23 @EnableConfigurationProperties({WebProperties.class, XssProperties.class}) 22 @EnableConfigurationProperties({WebProperties.class, XssProperties.class})
24 public class WebConfiguration implements WebMvcConfigurer { 23 public class WebConfiguration implements WebMvcConfigurer {
@@ -39,8 +38,7 @@ public class WebConfiguration implements WebMvcConfigurer { @@ -39,8 +38,7 @@ public class WebConfiguration implements WebMvcConfigurer {
39 * 创建 CorsFilter Bean,解决跨域问题 38 * 创建 CorsFilter Bean,解决跨域问题
40 */ 39 */
41 @Bean 40 @Bean
42 - @Order(Integer.MIN_VALUE)  
43 - public CorsFilter corsFilter() { 41 + public FilterRegistrationBean<CorsFilter> corsFilterBean() {
44 // 创建 CorsConfiguration 对象 42 // 创建 CorsConfiguration 对象
45 CorsConfiguration config = new CorsConfiguration(); 43 CorsConfiguration config = new CorsConfiguration();
46 config.setAllowCredentials(true); 44 config.setAllowCredentials(true);
@@ -50,25 +48,29 @@ public class WebConfiguration implements WebMvcConfigurer { @@ -50,25 +48,29 @@ public class WebConfiguration implements WebMvcConfigurer {
50 // 创建 UrlBasedCorsConfigurationSource 对象 48 // 创建 UrlBasedCorsConfigurationSource 对象
51 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 49 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
52 source.registerCorsConfiguration("/**", config); // 对接口配置跨域设置 50 source.registerCorsConfiguration("/**", config); // 对接口配置跨域设置
53 - return new CorsFilter(source); 51 + return createFilterBean(new CorsFilter(source), FilterOrderEnum.CORS_FILTER);
54 } 52 }
55 53
56 /** 54 /**
57 * 创建 RequestBodyCacheFilter Bean,可重复读取请求内容 55 * 创建 RequestBodyCacheFilter Bean,可重复读取请求内容
58 */ 56 */
59 @Bean 57 @Bean
60 - @Order(Integer.MIN_VALUE)  
61 - public RequestBodyCacheFilter requestBodyCacheFilter() {  
62 - return new RequestBodyCacheFilter(); 58 + public FilterRegistrationBean<RequestBodyCacheFilter> requestBodyCacheFilter() {
  59 + return createFilterBean(new RequestBodyCacheFilter(), FilterOrderEnum.REQUEST_BODY_CACHE_FILTER);
63 } 60 }
64 61
65 /** 62 /**
66 * 创建 XssFilter Bean,解决 Xss 安全问题 63 * 创建 XssFilter Bean,解决 Xss 安全问题
67 */ 64 */
68 @Bean 65 @Bean
69 - @Order(Integer.MIN_VALUE + 1000) // 需要保证在 RequestBodyCacheFilter 后面  
70 - public XssFilter xssFilter(XssProperties properties, PathMatcher pathMatcher) {  
71 - return new XssFilter(properties, pathMatcher); 66 + public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties, PathMatcher pathMatcher) {
  67 + return createFilterBean(new XssFilter(properties, pathMatcher), FilterOrderEnum.XSS_FILTER);
  68 + }
  69 +
  70 + private static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {
  71 + FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter);
  72 + bean.setOrder(order);
  73 + return bean;
72 } 74 }
73 75
74 } 76 }
src/main/java/cn/iocoder/dashboard/framework/web/core/enums/FilterOrderEnum.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.web.core.enums;
  2 +
  3 +/**
  4 + * 过滤器顺序的枚举类,保证过滤器按照符合我们的预期
  5 + *
  6 + * @author 芋道源码
  7 + */
  8 +public interface FilterOrderEnum {
  9 +
  10 + int CORS_FILTER = Integer.MIN_VALUE;
  11 +
  12 + int REQUEST_BODY_CACHE_FILTER = Integer.MIN_VALUE + 500;
  13 +
  14 + // OrderedRequestContextFilter 默认为 -105,用于国际化上下文等等
  15 +
  16 + int API_ACCESS_LOG_FILTER = -104; // 需要保证在 RequestBodyCacheFilter 后面
  17 +
  18 + int XSS_FILTER = -103; // 需要保证在 RequestBodyCacheFilter 后面
  19 +
  20 + // Spring Security Filter 默认为 -100,可见 SecurityProperties 配置属性类
  21 +
  22 +}
src/main/java/cn/iocoder/dashboard/framework/web/core/filter/RequestBodyCacheFilter.java
1 package cn.iocoder.dashboard.framework.web.core.filter; 1 package cn.iocoder.dashboard.framework.web.core.filter;
2 2
3 -import cn.hutool.core.util.StrUtil;  
4 -import org.springframework.http.MediaType; 3 +import cn.iocoder.dashboard.util.servlet.ServletUtils;
5 import org.springframework.web.filter.OncePerRequestFilter; 4 import org.springframework.web.filter.OncePerRequestFilter;
6 import org.springframework.web.util.ContentCachingRequestWrapper; 5 import org.springframework.web.util.ContentCachingRequestWrapper;
7 6
@@ -29,7 +28,7 @@ public class RequestBodyCacheFilter extends OncePerRequestFilter { @@ -29,7 +28,7 @@ public class RequestBodyCacheFilter extends OncePerRequestFilter {
29 @Override 28 @Override
30 protected boolean shouldNotFilter(HttpServletRequest request) { 29 protected boolean shouldNotFilter(HttpServletRequest request) {
31 // 只处理 json 请求内容 30 // 只处理 json 请求内容
32 - return !StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE); 31 + return !ServletUtils.isJsonRequest(request);
33 } 32 }
34 33
35 } 34 }
src/main/java/cn/iocoder/dashboard/framework/web/core/filter/XssRequestWrapper.java
@@ -6,6 +6,7 @@ import cn.hutool.core.util.ArrayUtil; @@ -6,6 +6,7 @@ import cn.hutool.core.util.ArrayUtil;
6 import cn.hutool.core.util.ReflectUtil; 6 import cn.hutool.core.util.ReflectUtil;
7 import cn.hutool.core.util.StrUtil; 7 import cn.hutool.core.util.StrUtil;
8 import cn.hutool.http.HTMLFilter; 8 import cn.hutool.http.HTMLFilter;
  9 +import cn.iocoder.dashboard.util.servlet.ServletUtils;
9 import org.springframework.http.MediaType; 10 import org.springframework.http.MediaType;
10 11
11 import javax.servlet.ReadListener; 12 import javax.servlet.ReadListener;
@@ -56,7 +57,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper { @@ -56,7 +57,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
56 @Override 57 @Override
57 public ServletInputStream getInputStream() throws IOException { 58 public ServletInputStream getInputStream() throws IOException {
58 // 如果非 json 请求,不进行 Xss 处理 59 // 如果非 json 请求,不进行 Xss 处理
59 - if (!StrUtil.startWithIgnoreCase(super.getContentType(), MediaType.APPLICATION_JSON_VALUE)) { 60 + if (!ServletUtils.isJsonRequest(this)) {
60 return super.getInputStream(); 61 return super.getInputStream();
61 } 62 }
62 63
src/main/java/cn/iocoder/dashboard/modules/infra/dal/dataobject/job/InfJobLogDO.java
@@ -9,7 +9,7 @@ import lombok.*; @@ -9,7 +9,7 @@ import lombok.*;
9 import java.util.Date; 9 import java.util.Date;
10 10
11 /** 11 /**
12 - * 定时任务的执行日志 sys_job_log 12 + * 定时任务的执行日志
13 * 13 *
14 * @author 芋道源码 14 * @author 芋道源码
15 */ 15 */
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/logger/SysApiAccessLogDO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.dal.dataobject.logger;
  2 +
  3 +import cn.iocoder.dashboard.common.enums.UserTypeEnum;
  4 +import cn.iocoder.dashboard.common.pojo.CommonResult;
  5 +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
  6 +import com.baomidou.mybatisplus.annotation.TableId;
  7 +import com.baomidou.mybatisplus.annotation.TableName;
  8 +import lombok.*;
  9 +
  10 +import java.util.Date;
  11 +
  12 +/**
  13 + * API 访问日志
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@TableName("sys_api_access_log")
  18 +@Data
  19 +@EqualsAndHashCode(callSuper = true)
  20 +@ToString(callSuper = true)
  21 +@Builder
  22 +@NoArgsConstructor
  23 +@AllArgsConstructor
  24 +public class SysApiAccessLogDO extends BaseDO {
  25 +
  26 + /**
  27 + * 编号
  28 + */
  29 + @TableId
  30 + private Integer id;
  31 + /**
  32 + * 链路追踪编号
  33 + *
  34 + * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
  35 + */
  36 + private String traceId;
  37 + /**
  38 + * 用户编号
  39 + */
  40 + private Integer userId;
  41 + /**
  42 + * 用户类型
  43 + *
  44 + * 枚举 {@link UserTypeEnum}
  45 + */
  46 + private Integer userType;
  47 + /**
  48 + * 应用名
  49 + *
  50 + * 目前读取 `spring.application.name` 配置项
  51 + */
  52 + private String applicationName;
  53 +
  54 + /**
  55 + * 请求方法名
  56 + */
  57 + private String requestMethod;
  58 + /**
  59 + * 访问地址
  60 + */
  61 + private String requestUrl;
  62 + /**
  63 + * 请求参数
  64 + *
  65 + * query: Query String
  66 + * body: Quest Body
  67 + */
  68 + private String requestParams;
  69 + /**
  70 + * 用户 IP
  71 + */
  72 + private String userIp;
  73 + /**
  74 + * 浏览器 UA
  75 + */
  76 + private String userAgent;
  77 +
  78 + /**
  79 + * 开始请求时间
  80 + */
  81 + private Date startTime;
  82 + /**
  83 + * 结束请求时间
  84 + */
  85 + private Date endTime;
  86 + /**
  87 + * 执行时长,单位:毫秒
  88 + */
  89 + private Integer duration;
  90 + /**
  91 + * 结果码
  92 + *
  93 + * 目前使用的 {@link CommonResult#getCode()} 属性
  94 + */
  95 + private Integer resultCode;
  96 + /**
  97 + * 结果提示
  98 + *
  99 + * 目前使用的 {@link CommonResult#getMsg()} 属性
  100 + */
  101 + private String resultMsg;
  102 +
  103 +}
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/logger/SysApiErrorLogDO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.dal.dataobject.logger;
  2 +
  3 +public class SysApiErrorLogDO {
  4 +}
src/main/java/cn/iocoder/dashboard/util/servlet/ServletUtils.java
1 package cn.iocoder.dashboard.util.servlet; 1 package cn.iocoder.dashboard.util.servlet;
2 2
3 import cn.hutool.core.io.IoUtil; 3 import cn.hutool.core.io.IoUtil;
  4 +import cn.hutool.core.util.StrUtil;
4 import cn.hutool.extra.servlet.ServletUtil; 5 import cn.hutool.extra.servlet.ServletUtil;
5 import cn.iocoder.dashboard.util.json.JsonUtils; 6 import cn.iocoder.dashboard.util.json.JsonUtils;
6 import org.springframework.http.MediaType; 7 import org.springframework.http.MediaType;
@@ -8,6 +9,7 @@ import org.springframework.web.context.request.RequestAttributes; @@ -8,6 +9,7 @@ import org.springframework.web.context.request.RequestAttributes;
8 import org.springframework.web.context.request.RequestContextHolder; 9 import org.springframework.web.context.request.RequestContextHolder;
9 import org.springframework.web.context.request.ServletRequestAttributes; 10 import org.springframework.web.context.request.ServletRequestAttributes;
10 11
  12 +import javax.servlet.ServletRequest;
11 import javax.servlet.http.HttpServletRequest; 13 import javax.servlet.http.HttpServletRequest;
12 import javax.servlet.http.HttpServletResponse; 14 import javax.servlet.http.HttpServletResponse;
13 import java.io.IOException; 15 import java.io.IOException;
@@ -86,4 +88,8 @@ public class ServletUtils { @@ -86,4 +88,8 @@ public class ServletUtils {
86 return ServletUtil.getClientIP(request); 88 return ServletUtil.getClientIP(request);
87 } 89 }
88 90
  91 + public static boolean isJsonRequest(ServletRequest request) {
  92 + return StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE);
  93 + }
  94 +
89 } 95 }