AOP异步日志记录
2022年12月10日
准备所需的日志表
CREATE TABLE `sys_oper_log` (
`oper_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '日志主键',
`title` varchar(50) COLLATE utf8mb4_bin DEFAULT '' COMMENT '模块标题',
`business_type` int(2) DEFAULT '0' COMMENT '业务类型(0其它 1新增 2修改 3删除)',
`method` varchar(200) COLLATE utf8mb4_bin DEFAULT '' COMMENT '方法名称',
`request_method` varchar(10) COLLATE utf8mb4_bin DEFAULT '' COMMENT '请求方式',
`operator_type` int(1) DEFAULT '0' COMMENT '操作类别(0其它 1后台用户 2手机端用户)',
`oper_name` varchar(50) COLLATE utf8mb4_bin DEFAULT '' COMMENT '操作人员',
`dept_name` varchar(50) COLLATE utf8mb4_bin DEFAULT '' COMMENT '部门名称',
`oper_url` varchar(255) COLLATE utf8mb4_bin DEFAULT '' COMMENT '请求URL',
`oper_ip` varchar(128) COLLATE utf8mb4_bin DEFAULT '' COMMENT '主机地址',
`oper_location` varchar(255) COLLATE utf8mb4_bin DEFAULT '' COMMENT '操作地点',
`oper_param` varchar(2000) COLLATE utf8mb4_bin DEFAULT '' COMMENT '请求参数',
`json_result` varchar(2000) COLLATE utf8mb4_bin DEFAULT '' COMMENT '返回参数',
`status` int(1) DEFAULT '0' COMMENT '操作状态(0正常 1异常)',
`error_msg` varchar(2000) COLLATE utf8mb4_bin DEFAULT '' COMMENT '错误消息',
`oper_time` datetime DEFAULT NULL COMMENT '操作时间',
PRIMARY KEY (`oper_id`)
) ENGINE=InnoDB AUTO_INCREMENT=106 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='操作日志记录';
pom依赖
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
实体
@Data
public class Student {
private String id;
private String name;
private Integer age;
}
/**
* <p>
* 操作日志记录
* </p>
*
* @author lc
* @since 2022-12-09
*/
@TableName("sys_oper_log")
public class SysOperLog implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 日志主键
*/
@TableId(value = "oper_id", type = IdType.AUTO)
private Long operId;
/**
* 模块标题
*/
private String title;
/**
* 业务类型(0其它 1新增 2修改 3删除)
*/
private Integer businessType;
/**
* 方法名称
*/
private String method;
/**
* 请求方式
*/
private String requestMethod;
/**
* 操作类别(0其它 1后台用户 2手机端用户)
*/
private Integer operatorType;
/**
* 操作人员
*/
private String operName;
/**
* 部门名称
*/
private String deptName;
/**
* 请求URL
*/
private String operUrl;
/**
* 主机地址
*/
private String operIp;
/**
* 操作地点
*/
private String operLocation;
/**
* 请求参数
*/
private String operParam;
/**
* 返回参数
*/
private String jsonResult;
/**
* 操作状态(0正常 1异常)
*/
private Integer status;
/**
* 错误消息
*/
private String errorMsg;
/**
* 操作时间
*/
private LocalDateTime operTime;
public Long getOperId() {
return operId;
}
public void setOperId(Long operId) {
this.operId = operId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Integer getBusinessType() {
return businessType;
}
public void setBusinessType(Integer businessType) {
this.businessType = businessType;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getRequestMethod() {
return requestMethod;
}
public void setRequestMethod(String requestMethod) {
this.requestMethod = requestMethod;
}
public Integer getOperatorType() {
return operatorType;
}
public void setOperatorType(Integer operatorType) {
this.operatorType = operatorType;
}
public String getOperName() {
return operName;
}
public void setOperName(String operName) {
this.operName = operName;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public String getOperUrl() {
return operUrl;
}
public void setOperUrl(String operUrl) {
this.operUrl = operUrl;
}
public String getOperIp() {
return operIp;
}
public void setOperIp(String operIp) {
this.operIp = operIp;
}
public String getOperLocation() {
return operLocation;
}
public void setOperLocation(String operLocation) {
this.operLocation = operLocation;
}
public String getOperParam() {
return operParam;
}
public void setOperParam(String operParam) {
this.operParam = operParam;
}
public String getJsonResult() {
return jsonResult;
}
public void setJsonResult(String jsonResult) {
this.jsonResult = jsonResult;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public LocalDateTime getOperTime() {
return operTime;
}
public void setOperTime(LocalDateTime operTime) {
this.operTime = operTime;
}
@Override
public String toString() {
return "SysOperLog{" +
"operId=" + operId +
", title=" + title +
", businessType=" + businessType +
", method=" + method +
", requestMethod=" + requestMethod +
", operatorType=" + operatorType +
", operName=" + operName +
", deptName=" + deptName +
", operUrl=" + operUrl +
", operIp=" + operIp +
", operLocation=" + operLocation +
", operParam=" + operParam +
", jsonResult=" + jsonResult +
", status=" + status +
", errorMsg=" + errorMsg +
", operTime=" + operTime +
"}";
}
}
自定义日志注解
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/**
* 模块
*/
String title() default "";
/**
* 功能
*/
BusinessType businessType() default BusinessType.OTHER;
/**
* 操作人类别
*/
OperatorType operatorType() default OperatorType.MANAGE;
/**
* 是否保存请求的参数
*/
boolean isSaveRequestData() default true;
/**
* 是否保存响应的参数
*/
boolean isSaveResponseData() default true;
}
Spring实例获取工具类
@Component
public class SpringUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringUtils.applicationContext=applicationContext;
}
/**
* 获取类型为requiredType的对象
*
* @param clz
* @return
* @throws org.springframework.beans.BeansException
*/
public static <T> T getBean(Class<T> clz) throws BeansException {
T result = (T) applicationContext.getBean(clz);
return result;
}
/**
* 获取对象
*
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws org.springframework.beans.BeansException
*/
public static <T> T getBean(String name) throws BeansException {
return (T) applicationContext.getBean(name);
}
}
IP地址工具类
/**
* 获取IP方法
*
* @author tienchin
*/
public class IpUtils {
/**
* 获取客户端IP
*
* @param request 请求对象
* @return IP地址
*/
public static String getIpAddr(HttpServletRequest request) {
if (request == null) {
return "unknown";
}
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("X-Forwarded-For");
}
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("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip);
}
/**
* 检查是否为内部IP地址
*
* @param ip IP地址
* @return 结果
*/
public static boolean internalIp(String ip) {
byte[] addr = textToNumericFormatV4(ip);
return internalIp(addr) || "127.0.0.1".equals(ip);
}
/**
* 检查是否为内部IP地址
*
* @param addr byte地址
* @return 结果
*/
private static boolean internalIp(byte[] addr) {
if (addr==null || addr.length < 2) {
return true;
}
final byte b0 = addr[0];
final byte b1 = addr[1];
// 10.x.x.x/8
final byte SECTION_1 = 0x0A;
// 172.16.x.x/12
final byte SECTION_2 = (byte) 0xAC;
final byte SECTION_3 = (byte) 0x10;
final byte SECTION_4 = (byte) 0x1F;
// 192.168.x.x/16
final byte SECTION_5 = (byte) 0xC0;
final byte SECTION_6 = (byte) 0xA8;
switch (b0) {
case SECTION_1:
return true;
case SECTION_2:
if (b1 >= SECTION_3 && b1 <= SECTION_4) {
return true;
}
case SECTION_5:
switch (b1) {
case SECTION_6:
return true;
}
default:
return false;
}
}
/**
* 将IPv4地址转换成字节
*
* @param text IPv4地址
* @return byte 字节
*/
public static byte[] textToNumericFormatV4(String text) {
if (text.length() == 0) {
return null;
}
byte[] bytes = new byte[4];
String[] elements = text.split("\\.", -1);
try {
long l;
int i;
switch (elements.length) {
case 1:
l = Long.parseLong(elements[0]);
if ((l < 0L) || (l > 4294967295L)) {
return null;
}
bytes[0] = (byte) (int) (l >> 24 & 0xFF);
bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 2:
l = Integer.parseInt(elements[0]);
if ((l < 0L) || (l > 255L)) {
return null;
}
bytes[0] = (byte) (int) (l & 0xFF);
l = Integer.parseInt(elements[1]);
if ((l < 0L) || (l > 16777215L)) {
return null;
}
bytes[1] = (byte) (int) (l >> 16 & 0xFF);
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 3:
for (i = 0; i < 2; ++i) {
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L)) {
return null;
}
bytes[i] = (byte) (int) (l & 0xFF);
}
l = Integer.parseInt(elements[2]);
if ((l < 0L) || (l > 65535L)) {
return null;
}
bytes[2] = (byte) (int) (l >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 4:
for (i = 0; i < 4; ++i) {
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L)) {
return null;
}
bytes[i] = (byte) (int) (l & 0xFF);
}
break;
default:
return null;
}
} catch (NumberFormatException e) {
return null;
}
return bytes;
}
/**
* 获取IP地址
*
* @return 本地IP地址
*/
public static String getHostIp() {
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
}
return "127.0.0.1";
}
/**
* 获取主机名
*
* @return 本地主机名
*/
public static String getHostName() {
try {
return InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
}
return "未知";
}
/**
* 从多级反向代理中获得第一个非unknown IP地址
*
* @param ip 获得的IP地址
* @return 第一个非unknown IP地址
*/
public static String getMultistageReverseProxyIp(String ip) {
// 多级反向代理检测
if (ip != null && ip.indexOf(",") > 0) {
final String[] ips = ip.trim().split(",");
for (String subIp : ips) {
if (false == isUnknown(subIp)) {
ip = subIp;
break;
}
}
}
return ip;
}
/**
* 检测给定字符串是否为未知,多用于检测HTTP请求相关
*
* @param checkString 被检测的字符串
* @return 是否未知
*/
public static boolean isUnknown(String checkString) {
return StringUtils.isEmpty(checkString) || "unknown".equalsIgnoreCase(checkString);
}
}
线程池配置
@Configuration
public class ThreadPoolConfig {
private static final Logger logger = LoggerFactory.getLogger(ThreadPoolConfig.class);
// 核心线程池大小
private int corePoolSize = 50;
// 最大可创建的线程数
private int maxPoolSize = 200;
// 队列最大长度
private int queueCapacity = 1000;
// 线程池维护线程所允许的空闲时间
private int keepAliveSeconds = 300;
// 创建ScheduledExecutorService对象
@Bean("scheduledExecutorService")
protected ScheduledExecutorService scheduledExecutorService() {
return new ScheduledThreadPoolExecutor(corePoolSize,
// commons-lang3 包下的对线程名称的重命名
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").build(),
// 拒绝策略为 主线程执行,重写执行的异常方法
new ThreadPoolExecutor.CallerRunsPolicy()) {
//必须重写ScheduledThreadPoolExecutor中的afterExecute方法,否则无法打印日志
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future<?>) {
try {
Future<?> future = (Future<?>) r;
if (future.isDone()) {
future.get();
}
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
if (t != null) {
logger.error(t.getMessage(), t);
}
}
};
}
}
异步任务配置
public class AsyncManager {
private static final Logger logger = LoggerFactory.getLogger(AsyncManager.class);
/**
* 操作延迟10毫秒
*/
private final int OPERATE_DELAY_TIME = 10;
/**
* 异步操作任务调度线程池
*/
private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
/**
* 单例模式
*/
private AsyncManager() {
}
private static AsyncManager me = new AsyncManager();
public static AsyncManager me() {
return me;
}
/**
* 执行任务
*
* @param task 任务
*/
public void execute(TimerTask task) {
executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
}
/**
* 停止任务线程池
*/
public void shutdown() {
shutdownAndAwaitTermination(executor);
}
private void shutdownAndAwaitTermination(ExecutorService pool) {
if (pool != null && !pool.isShutdown()) {
pool.shutdown();
try {
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
pool.shutdownNow();
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
logger.info("Pool did not terminate");
}
}
} catch (InterruptedException ie) {
pool.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
}
@Component
public class ShutdownManager {
private static final Logger logger = LoggerFactory.getLogger("sys-user");
@PreDestroy
public void destroy() {
shutdownAsyncManager();
}
/**
* 停止异步执行任务
*/
private void shutdownAsyncManager() {
try {
logger.info("====关闭后台任务任务线程池====");
AsyncManager.me().shutdown();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
任务工厂配置
public class AsyncFactory {
private static final Logger logger = LoggerFactory.getLogger(AsyncFactory.class);
/**
* 操作日志记录
*
* @param operLog 操作日志信息
* @return 任务task
*/
public static TimerTask recordOper(final SysOperLog operLog) {
return new TimerTask() {
@Override
public void run() {
SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog);
}
};
}
}
常量
/**
* 操作状态
*
* @author tienchin
*/
public enum BusinessStatus {
/**
* 成功
*/
SUCCESS,
/**
* 失败
*/
FAIL,
}
/**
* 业务操作类型
*
* @author tienchin
*/
public enum BusinessType {
/**
* 其它
*/
OTHER,
/**
* 新增
*/
INSERT,
/**
* 修改
*/
UPDATE,
/**
* 删除
*/
DELETE,
/**
* 授权
*/
GRANT,
/**
* 导出
*/
EXPORT,
/**
* 导入
*/
IMPORT,
/**
* 强退
*/
FORCE,
/**
* 生成代码
*/
GENCODE,
/**
* 清空数据
*/
CLEAN,
}
/**
* 操作人类别
*
* @author tienchin
*/
public enum OperatorType {
/**
* 其它
*/
OTHER,
/**
* 后台用户
*/
MANAGE,
/**
* 手机端用户
*/
MOBILE
}
核心切面配置
@Aspect
@Component
public class LogAspect {
private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
/**
* 返回通知 正常执行完毕后的通知
*
* @param joinPoint 切点
* @param log 自定义日志注解
* @param object 切点执行返回值
*/
@AfterReturning(value = "@annotation(log)", returning = "object")
public void doAfterReturning(JoinPoint joinPoint, Log log, Object object) {
handleLog(joinPoint, log, null, object);
}
/**
* 异常通知 切点方法执行异常
*
* @param joinPoint 切点
* @param log 自定义日志注解
* @param e 切点方法执行异常
*/
@AfterThrowing(value = "@annotation(log)", throwing = "e")
public void doThrowingReturning(JoinPoint joinPoint, Log log, Exception e) {
handleLog(joinPoint, log, e, null);
}
/**
* 日志处理
*
* @param joinPoint 切点
* @param log 自定义日志注解
* @param e 异常对象
* @param result 切点执行返回值
*/
private void handleLog(JoinPoint joinPoint, Log log, Exception e, Object result) {
try {
//这里可以获取用户的登录信息
String userName = "管理员";
//获取当前请求对象
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//日志基本信息
SysOperLog sysOperLog = new SysOperLog();
sysOperLog.setStatus(BusinessStatus.SUCCESS.ordinal());
sysOperLog.setOperIp(IpUtils.getIpAddr(request));
sysOperLog.setOperUrl(request.getRequestURI());
if (userName != null) {
sysOperLog.setOperName(userName);
}
//异常的日志
if (e != null) {
sysOperLog.setStatus(BusinessStatus.FAIL.ordinal());
sysOperLog.setErrorMsg(e.getMessage().substring(0, 2000));
}
//请求资源方法相关的设置
//例如:public java.lang.Object org.lc.custom_log.controller.StudentController.deleteStudent(java.lang.Integer)
String methodName = joinPoint.getSignature().toLongString();
sysOperLog.setMethod(methodName);
sysOperLog.setRequestMethod(request.getMethod());
//处理注解上的参数
handlerLogDescription(joinPoint, log, sysOperLog, result, request);
//异步操作数据库添加日志
AsyncManager.me().execute(AsyncFactory.recordOper(sysOperLog));
} catch (Exception exception) {
// 记录本地异常日志
logger.error("==前置通知异常==");
logger.error("异常信息:{}", exception.getMessage());
exception.printStackTrace();
}
}
/**
* 处理注解上的信息
*
* @param joinPoint 切点
* @param log 注解对象
* @param sysOperLog 操作日志
* @param result 切点返回结果
* @param request 请求对象
*/
private void handlerLogDescription(JoinPoint joinPoint, Log log, SysOperLog sysOperLog, Object result, HttpServletRequest request) {
sysOperLog.setBusinessType(log.businessType().ordinal());
sysOperLog.setTitle(log.title());
sysOperLog.setOperatorType(log.operatorType().ordinal());
//是否需要保存请求数据
if (log.isSaveRequestData()) {
//获取请求参数信息
saveRequestData(joinPoint, sysOperLog, request);
}
//是否需要保存响应数据
if (log.isSaveResponseData()) {
//直接保存响应信息
Object o = JSON.toJSON(result);
if (o != null) {
sysOperLog.setJsonResult(substring(o.toString(), 0, 2000));
}
}
}
/**
* 请求参数处理
*
* @param joinPoint 切点
* @param sysOperLog 日志对象
* @param request
*/
private void saveRequestData(JoinPoint joinPoint, SysOperLog sysOperLog, HttpServletRequest request) {
String requestMethod = sysOperLog.getRequestMethod();
//如果为 PUT 请求或者 POST 请求
if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
//直接获取方法上的所有参数
Object[] args = joinPoint.getArgs();
String argsResult = argsArrayToString(args);
sysOperLog.setOperParam(argsResult.substring(0, 2000));
} else {
//则为其他DELETE、GET 等,则从URL获取路径模版参数
// 例如:http://localhost:8080/student/delete/{id} 只获取id的键和值
Map<?, ?> attribute = (Map<?, ?>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
if (attribute != null) {
sysOperLog.setOperParam(substring(attribute.toString(), 0, 2000));
}
}
}
public static String substring(final String str, int start, int end) {
if (str == null) {
return "";
}
if (end < 0) {
end = str.length() + end;
}
if (start < 0) {
start = str.length() + start;
}
if (end > str.length()) {
end = str.length();
}
if (start > end) {
return "";
}
if (start < 0) {
start = 0;
}
if (end < 0) {
end = 0;
}
return str.substring(start, end);
}
/**
* 参数拼装
*/
private String argsArrayToString(Object[] paramsArray) {
StringBuilder params = new StringBuilder();
if (paramsArray != null && paramsArray.length > 0) {
for (Object o : paramsArray) {
if (o != null && !isFilterObject(o)) {
try {
Object jsonObj = JSON.toJSON(o);
params.append(jsonObj.toString()).append(" ");
} catch (Exception e) {
}
}
}
}
return params.toString().trim();
}
/**
* 判断是否需要过滤的对象。
* 若为文件类型,或者其他对象类型,则不拼装参数
* @param o 对象信息。
* @return 如果是需要过滤的对象,则返回true;否则返回false。
*/
@SuppressWarnings("rawtypes")
public boolean isFilterObject(final Object o) {
Class<?> clazz = o.getClass();
//是否为数组类型
if (clazz.isArray()) {
//获取数组的类型,并且判断该类型是否为MultipartFile的接口或者父类
//例如: System.out.println(Number.class.isAssignableFrom(Integer.class)); true
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
} else if (Collection.class.isAssignableFrom(clazz)) {
// 若为集合类型
Collection collection = (Collection) o;
//遍历集合中的元素
for (Object value : collection) {
return value instanceof MultipartFile;
}
} else if (Map.class.isAssignableFrom(clazz)) {
// 若为map类型
Map map = (Map) o;
// 遍历map中的元素
for (Object value : map.entrySet()) {
Map.Entry entry = (Map.Entry) value;
return entry.getValue() instanceof MultipartFile;
}
}
// 其他的返回
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|| o instanceof BindingResult;
}
}
日志Service层
/**
* <p>
* 操作日志记录 服务类
* </p>
*
* @author lc
* @since 2022-12-09
*/
public interface ISysOperLogService extends IService<SysOperLog> {
void insertOperlog(SysOperLog operLog);
}
/**
* <p>
* 操作日志记录 服务实现类
* </p>
*
* @author lc
* @since 2022-12-09
*/
@Service
public class SysOperLogServiceImpl extends ServiceImpl<SysOperLogMapper, SysOperLog> implements ISysOperLogService {
@Autowired
SysOperLogMapper sysOperLogMapper;
@Override
public void insertOperlog(SysOperLog operLog) {
sysOperLogMapper.insertOperlog(operLog);
}
}
日志mapper层
/**
* <p>
* 操作日志记录 Mapper 接口
* </p>
*
* @author lc
* @since 2022-12-09
*/
public interface SysOperLogMapper extends BaseMapper<SysOperLog> {
void insertOperlog(SysOperLog operLog);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.lc.custom_log.mapper.SysOperLogMapper">
<insert id="insertOperlog" parameterType="SysOperLog">
insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time)
values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operLocation}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, sysdate())
</insert>
</mapper>
控制器测试
@RestController
@RequestMapping("/student")
public class StudentController {
@Log(title = "学生信息管理", businessType = BusinessType.INSERT)
@PostMapping("/add")
public Object addStudent(@RequestBody Student student) {
System.out.println(student.toString());
Map<String, Object> result = new HashMap<>();
result.put("status", 200);
result.put("message", "添加学生成功");
return result;
}
@Log(title = "学生信息管理", businessType = BusinessType.UPDATE)
@PutMapping("/update")
public Object updateStudent(@RequestBody Student student) {
System.out.println(student.toString());
Map<String, Object> result = new HashMap<>();
result.put("status", 200);
result.put("message", "修改学生成功");
return result;
}
@Log(title = "学生信息管理", businessType = BusinessType.DELETE)
@DeleteMapping("/delete/{id}")
public Object deleteStudent(@PathVariable("id") Integer id) {
System.out.println(id);
Map<String, Object> result = new HashMap<>();
result.put("status", 200);
result.put("message", "删除学生成功");
return result;
}
}
启动程序
@MapperScan(basePackages = "org.lc.custom_log.mapper")
@SpringBootApplication
public class CustomLogApplication {
public static void main(String[] args) {
SpringApplication.run(CustomLogApplication.class, args);
}
}
yml配置
# 应用名称
spring.application.name=custom_log
# 应用服务 WEB 访问端口
server.port=8080
# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据源名称
spring.datasource.name=custom_log_datasource
# 数据库连接地址
spring.datasource.url=jdbc:mysql://ip:3306/tienchin?serverTimezone=Asia/Shanghai&useSSL=false
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=xxx
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.type-aliases-package=org.lc.custom_log.entity