文章目录
- 描述
- 一、持久化部分
-
- 表:
- [1.1 controller](#1.1 controller)
- [1.2 service](#1.2 service)
- [1.3 service impl](#1.3 service impl)
- [1.4 mapper](#1.4 mapper)
- 1.5实体entity
- 二.核心部分
-
- 1.1注解
- [1.2 saveAo](#1.2 saveAo)
- [13 enum](#13 enum)
- [1.4 aspect](#1.4 aspect)
- [1.5 util](#1.5 util)
- 三.服务配置
描述
由于加全局日志,WebLog注解没有起到作用
一、持久化部分
表:
sql
-- 1.提货点
drop table if exists `base_operation_log`;
CREATE TABLE `base_operation_log`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
`request_ip` varchar(50) DEFAULT '' COMMENT '操作ID',
`type` int DEFAULT NULL COMMENT '日志类型 1.操作类型 2.异常异常',
`staff_name` varchar(50) DEFAULT '' COMMENT '操作人',
`description` varchar(512) DEFAULT '' COMMENT '描述',
`biz_id` bigint DEFAULT NULL COMMENT '业务ID',
`class_path` varchar(255) DEFAULT '' COMMENT '类路径',
`action_method` varchar(50) DEFAULT NULL COMMENT '请求方法',
`request_uri` varchar(255) DEFAULT NULL COMMENT '请求地址',
`http_method` varchar(10) DEFAULT NULL COMMENT '请求类型:GET:GET请求;POST:POST请求;PUT:PUT请求;DELETE:DELETE请求;PATCH:PATCH请求;TRACE:TRACE请求;HEAD:HEAD请求;OPTIONS:OPTIONS请求',
`params` text COMMENT '参数',
`result` text COMMENT '返回值',
`ex_detail` text COMMENT '异常描述',
`start_time` datetime DEFAULT NULL COMMENT '开始时间',
`finish_time` datetime DEFAULT NULL COMMENT '结束时间',
`consuming_time` bigint DEFAULT NULL COMMENT '消耗时间',
`ua` varchar(500) DEFAULT NULL COMMENT '浏览器',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`create_user_id` bigint DEFAULT '0' COMMENT '创建人',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`update_user_id` bigint DEFAULT '0' COMMENT '更新人',
`create_org_id` bigint DEFAULT '0' COMMENT '创建组织ID',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='操作日志';
1.1 controller
java
package com.ulinkle.base.controller;
import com.ulinkle.base.api.BaseOperationLogService;
import com.ulinkle.common.core.entity.BaseOperationLogSaveAo;
import com.ulinkle.common.core.utils.BeanPlusUtil;
import com.ulinkle.domain.base.entity.BaseOperationLog;
import io.swagger.annotations.Api;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 王
* @description
* @createDate
*/
@RestController
@RequestMapping("/operateLog")
@Api(tags = "操作日志")
@AllArgsConstructor
public class BaseOperateLogController {
private final BaseOperationLogService baseOperationLogService;
@PostMapping("add")
public void add(@RequestBody BaseOperationLogSaveAo saveAo) {
baseOperationLogService.save(BeanPlusUtil.toBean(saveAo, BaseOperationLog.class));
}
}
1.2 service
java
import com.baomidou.mybatisplus.extension.service.IService;
import com.ulinkle.domain.base.entity.BaseOperationLog;
/**
* @author DELL
* @description 针对表【base_operation_log(操作日志)】的数据库操作Service
* @createDate 2024-10-17 14:53:59
*/
public interface BaseOperationLogService extends IService<BaseOperationLog> {
}
1.3 service impl
java
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ulinkle.base.api.BaseOperationLogService;
import com.ulinkle.base.mapper.BaseOperationLogMapper;
import com.ulinkle.domain.base.entity.BaseOperationLog;
import org.springframework.stereotype.Service;
/**
* @author DELL
* @description 针对表【base_operation_log(操作日志)】的数据库操作Service实现
* @createDate 2024-10-17 14:53:59
*/
@Service
public class BaseOperationLogServiceImpl extends ServiceImpl<BaseOperationLogMapper, BaseOperationLog>
implements BaseOperationLogService{
}
1.4 mapper
java
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ulinkle.domain.base.entity.BaseOperationLog;
/**
* @author DELL
* @description 针对表【base_operation_log(操作日志)】的数据库操作Mapper
* @createDate 2024-10-17 14:53:59
* @Entity com.ulinkle.domain.base.entity.BaseOperationLog
*/
public interface BaseOperationLogMapper extends BaseMapper<BaseOperationLog> {
}
BaseOperationLogMapper.xml
xml
<?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="com.ulinkle.base.mapper.BaseOperationLogMapper">
<resultMap id="BaseResultMap" type="com.ulinkle.domain.base.entity.BaseOperationLog">
<id property="id" column="id" jdbcType="BIGINT"/>
<result property="requestIp" column="request_ip" jdbcType="VARCHAR"/>
<result property="type" column="type" jdbcType="INTEGER"/>
<result property="staffName" column="staff_name" jdbcType="VARCHAR"/>
<result property="description" column="description" jdbcType="VARCHAR"/>
<result property="bizId" column="biz_id" jdbcType="BIGINT"/>
<result property="classPath" column="class_path" jdbcType="VARCHAR"/>
<result property="actionMethod" column="action_method" jdbcType="VARCHAR"/>
<result property="requestUri" column="request_uri" jdbcType="VARCHAR"/>
<result property="httpMethod" column="http_method" jdbcType="VARCHAR"/>
<result property="params" column="params" jdbcType="VARCHAR"/>
<result property="result" column="result" jdbcType="VARCHAR"/>
<result property="exDetail" column="ex_detail" jdbcType="VARCHAR"/>
<result property="startTime" column="start_time" jdbcType="TIMESTAMP"/>
<result property="finishTime" column="finish_time" jdbcType="TIMESTAMP"/>
<result property="consumingTime" column="consuming_time" jdbcType="BIGINT"/>
<result property="ua" column="ua" jdbcType="VARCHAR"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="createUserId" column="create_user_id" jdbcType="BIGINT"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
<result property="updateUserId" column="update_user_id" jdbcType="BIGINT"/>
<result property="createOrgId" column="create_org_id" jdbcType="BIGINT"/>
</resultMap>
<sql id="Base_Column_List">
id,request_ip,type,
staff_name,description,biz_id,
class_path,action_method,request_uri,
http_method,params,result,
ex_detail,start_time,finish_time,
consuming_time,ua,create_time,
create_user_id,update_time,update_user_id,
create_org_id
</sql>
</mapper>
1.5实体entity
java
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ulinkle.common.core.entity.LogTypeEnum;
import com.ulinkle.common.core.entity.SuperEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 操作日志
* @TableName base_operation_log
*/
@EqualsAndHashCode(callSuper = true)
@TableName(value ="base_operation_log")
@Data
public class BaseOperationLog extends SuperEntity<Long> implements Serializable {
/**
* 操作ID
*/
@TableField(value = "request_ip")
private String requestIp;
/**
* 日志类型 1.操作类型 2.异常异常
*/
@TableField(value = "type")
private LogTypeEnum type;
/**
* 操作人
*/
@TableField(value = "staff_name")
private String staffName;
/**
* 描述
*/
@TableField(value = "description")
private String description;
/**
* 业务ID
*/
@TableField(value = "biz_id")
private Long bizId;
/**
* 类路径
*/
@TableField(value = "class_path")
private String classPath;
/**
* 请求方法
*/
@TableField(value = "action_method")
private String actionMethod;
/**
* 请求地址
*/
@TableField(value = "request_uri")
private String requestUri;
/**
* 请求类型:GET:GET请求;POST:POST请求;PUT:PUT请求;DELETE:DELETE请求;PATCH:PATCH请求;TRACE:TRACE请求;HEAD:HEAD请求;OPTIONS:OPTIONS请求
*/
@TableField(value = "http_method")
private String httpMethod;
/**
* 参数
*/
@TableField(value = "params")
private String params;
/**
* 返回值
*/
@TableField(value = "result")
private String result;
/**
* 异常描述
*/
@TableField(value = "ex_detail")
private String exDetail;
/**
* 开始时间
*/
@TableField(value = "start_time")
private LocalDateTime startTime;
/**
* 结束时间
*/
@TableField(value = "finish_time")
private LocalDateTime finishTime;
/**
* 消耗时间
*/
@TableField(value = "consuming_time")
private Long consumingTime;
/**
* 浏览器
*/
@TableField(value = "ua")
private String ua;
@TableField(value = "create_org_id",fill = FieldFill.INSERT)
private Integer createOrgId;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
二.核心部分
1.1注解
java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 操作日志注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD})
public @interface WebLog {
/**
* 是否启用 操作日志
*
* @return 是否启用
*/
boolean enabled() default true;
/**
* @return {String}
*/
String value() default "";
/** 模块 */
String modular() default "";
/**
* 是否拼接Controller类上@ApiOperation注解的描述值
*
* @return 是否拼接Controller类上的描述值
*/
boolean controllerApiValue() default true;
/**
* 是否记录方法的入参
*
* @return 是否记录方法的入参
*/
boolean request() default true;
/**
* 若设置了 request = false、requestByError = true,则方法报错时,依然记录请求的入参
*
* @return 当 request = false时, 方法报错记录请求参数
*/
boolean requestByError() default true;
/**
* 是否记录返回值
*
* @return 是否记录返回值
*/
boolean response() default true;
}
1.2 saveAo
java
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 操作日志
* @TableName base_operation_log
*/
@Data
public class BaseOperationLogSaveAo implements Serializable {
/**
* 操作ID
*/
@ApiModelProperty("操作ID")
@Length(max= 50,message="编码长度不能超过50")
private String requestIp;
/**
* 日志类型 1.操作类型 2.异常异常
*/
@ApiModelProperty("日志类型 1.操作类型 2.异常异常")
private LogTypeEnum type;
/**
* 操作人
*/
@ApiModelProperty("操作人")
@Length(max= 50,message="编码长度不能超过50")
private String staffName;
/**
* 描述
*/
@ApiModelProperty("描述")
@Length(max= 255,message="编码长度不能超过255")
private String description;
/**
* 业务ID
*/
@ApiModelProperty("业务ID")
private Long bizId;
/**
* 类路径
*/
@ApiModelProperty("类路径")
@Length(max= 255,message="编码长度不能超过255")
private String classPath;
/**
* 请求方法
*/
@ApiModelProperty("请求方法")
@Length(max= 50,message="编码长度不能超过50")
private String actionMethod;
/**
* 请求地址
*/
@ApiModelProperty("请求地址")
@Length(max= 255,message="编码长度不能超过255")
private String requestUri;
/**
* 请求类型:GET:GET请求;POST:POST请求;PUT:PUT请求;DELETE:DELETE请求;PATCH:PATCH请求;TRACE:TRACE请求;HEAD:HEAD请求;OPTIONS:OPTIONS请求
*/
@ApiModelProperty("请求类型:GET:GET请求;POST:POST请求;PUT:PUT请求;DELETE:DELETE请求;PATCH:PATCH请求;TRACE:TRACE请求;HEAD:HEAD请求;OPTIONS:OPTIONS请求")
@Length(max= 10,message="编码长度不能超过10")
private String httpMethod;
/**
* 参数
*/
@ApiModelProperty("参数")
private String params;
/**
* 返回值
*/
@ApiModelProperty("返回值")
private String result;
/**
* 异常描述
*/
@ApiModelProperty("异常描述")
private String exDetail;
/**
* 开始时间
*/
@ApiModelProperty("开始时间")
private LocalDateTime startTime;
/**
* 结束时间
*/
@ApiModelProperty("结束时间")
private LocalDateTime finishTime;
/**
* 消耗时间
*/
@ApiModelProperty("消耗时间")
private Long consumingTime;
/**
* 浏览器
*/
@ApiModelProperty("浏览器")
@Length(max= 500,message="编码长度不能超过500")
private String ua;
private Long createUserId;
}
13 enum
java
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
import com.ulinkle.common.core.interfaces.BaseEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;
/**
* 日志类型:1-操作;2-异常
*/
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "日志类型", description = "1-操作;2-异常")
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum LogTypeEnum implements BaseEnum<Integer> {
/**
*
*/
OPT(1, "操作"),
EX(2, "生产工单"),
;
@EnumValue
@JsonValue
@ApiModelProperty(value = "code", allowableValues = "1,2,3", example = "1")
private Integer code;
@ApiModelProperty(value = "描述")
private String desc;
@Override
public String getExtra() {
return BaseEnum.super.getExtra();
}
@Override
public boolean eq(Integer val) {
return BaseEnum.super.eq(val);
}
@Override
@JsonProperty
public Integer getValue() {
return BaseEnum.super.getValue();
}
public Long longValue() {
return code.longValue();
}
@JsonCreator
public static LogTypeEnum of(Integer val) {
return Stream.of(values()).filter(item -> Objects.equals(item.getValue(), val)).findAny().orElse(null);
}
@Override
public Set<String> getDescSet() {
LogTypeEnum[] values = LogTypeEnum.values();
Set<String> set = new HashSet<>();
for (LogTypeEnum value : values) {
set.add(value.getDesc());
}
return set;
}
}
1.4 aspect
1.4.1
java
import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.ulinkle.common.core.annotation.WebLog;
import com.ulinkle.common.core.entity.BaseOperationLogSaveAo;
import com.ulinkle.common.core.entity.LogTypeEnum;
import com.ulinkle.common.core.utils.*;
import com.ulinkle.common.core.web.page.DataResponse;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.lang.NonNull;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Consumer;
/**
* 操作日志使用spring event异步入库
*
* @author zuihou
* @date 2019-07-01 15:15
*/
@Slf4j
@Aspect
public class LogAspect {
public static final int MAX_LENGTH = 65535;
private static final ThreadLocal<BaseOperationLogSaveAo> THREAD_LOCAL = new ThreadLocal<>();
private static final String FORM_DATA_CONTENT_TYPE = "multipart/form-data";
@Pointcut("execution(public * com.ulinkle.*.controller.*.*(..)) || @annotation(com.ulinkle.common.core.annotation.WebLog)")
public void logAspect() {
}
/**
* 返回通知
*
* @param ret 返回值
* @param joinPoint 端点
*/
@AfterReturning(returning = "ret", pointcut = "logAspect()")
public void doAfterReturning(JoinPoint joinPoint, Object ret) {
tryCatch(p -> {
// WebLog log = getWebLog(joinPoint);
// if (log == null) return;
DataResponse<?> r = Convert.convert(DataResponse.class, ret);
BaseOperationLogSaveAo logDTO = get();
if (r == null) {
logDTO.setType(LogTypeEnum.OPT);
logDTO.setResult(getText(String.valueOf(ret == null ? StrPool.EMPTY : ret)));
} else {
if (r.isSuccess()) {
logDTO.setType(LogTypeEnum.OPT);
} else {
logDTO.setType(LogTypeEnum.EX);
logDTO.setExDetail(r.getMsg());
}
logDTO.setResult(getText(r.toString()));
}
publishEvent(logDTO);
});
}
/**
* 异常通知
*
* @param joinPoint 端点
* @param e 异常
*/
@AfterThrowing(pointcut = "logAspect()", throwing = "e")
public void doAfterThrowable(JoinPoint joinPoint, Throwable e) {
tryCatch((aaa) -> {
// WebLog log = getWebLog(joinPoint);
// if (log == null) return;
BaseOperationLogSaveAo optLogDTO = get();
optLogDTO.setType(LogTypeEnum.EX);
// 遇到错误时,请求参数若为空,则记录
if (StrUtil.isEmpty(optLogDTO.getParams())) {
Object[] args = joinPoint.getArgs();
HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
String strArgs = getArgs(args, request);
optLogDTO.setParams(getText(strArgs));
}
// 异常对象
optLogDTO.setExDetail(ExceptionUtil.stacktraceToString(e, MAX_LENGTH));
publishEvent(optLogDTO);
});
}
private WebLog getWebLog(JoinPoint joinPoint) {
WebLog log = LogUtil.getTargetAnnotation(joinPoint);
if (check(joinPoint, log)) {
return null;
}
return log;
}
/**
* 执行方法之前
*
* @param joinPoint 端点
*/
@Before(value = "logAspect()")
public void doBefore(JoinPoint joinPoint) {
tryCatch(val -> {
// WebLog log = getWebLog(joinPoint);
// if (log == null) return;
BaseOperationLogSaveAo optLogDTO = buildBaseOperationLogSaveAo(joinPoint, null);
THREAD_LOCAL.set(optLogDTO);
});
}
@NonNull
private BaseOperationLogSaveAo buildBaseOperationLogSaveAo(JoinPoint joinPoint, WebLog log) {
// 开始时间
BaseOperationLogSaveAo optLogDTO = get();
optLogDTO.setCreateUserId(SecurityUtils.getLoginId().longValue());
optLogDTO.setStaffName(SecurityUtils.getLoginName());
setDescription(joinPoint, log, optLogDTO);
// 类名
optLogDTO.setClassPath(joinPoint.getTarget().getClass().getName());
//获取执行的方法名
optLogDTO.setActionMethod(joinPoint.getSignature().getName());
HttpServletRequest request = setParams(joinPoint, log, optLogDTO);
optLogDTO.setRequestIp(getClientIPByHeader(request));
optLogDTO.setRequestUri(URLUtil.getPath(request.getRequestURI()));
optLogDTO.setHttpMethod(request.getMethod());
optLogDTO.setUa(StrUtil.sub(request.getHeader("user-agent"), 0, 500));
optLogDTO.setStartTime(LocalDateTime.now());
return optLogDTO;
}
public static String getClientIPByHeader(HttpServletRequest request, String... headerNames) {
String ip;
for (String header : headerNames) {
ip = request.getHeader(header);
if (!NetUtil.isUnknown(ip)) {
return NetUtil.getMultistageReverseProxyIp(ip);
}
}
ip = request.getRemoteAddr();
return NetUtil.getMultistageReverseProxyIp(ip);
}
@NonNull
private HttpServletRequest setParams(JoinPoint joinPoint, WebLog log, BaseOperationLogSaveAo optLogDTO) {
// 参数
Object[] args = joinPoint.getArgs();
HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes(), "只能在Spring Web环境使用@WebLog记录日志")).getRequest();
// if (log.request()) {
String strArgs = getArgs(args, request);
optLogDTO.setParams(getText(strArgs));
// }
return request;
}
private void setDescription(JoinPoint joinPoint, WebLog log, BaseOperationLogSaveAo optLogDTO) {
String controllerDescription = "";
Api api = joinPoint.getTarget().getClass().getAnnotation(Api.class);
if (api != null) {
String[] tags = api.tags();
if (ArrayUtil.isNotEmpty(tags)) {
controllerDescription = tags[0];
}
}
String controllerMethodDescription = LogUtil.getApiOperationValue(joinPoint);
// String controllerMethodDescription = LogUtil.getDescribe(log);
// if (StringUtils.isEmpty(controllerMethodDescription)) {
// controllerMethodDescription = LogUtil.getApiOperationValue(joinPoint);
// }
// if (log.controllerApiValue()) {
// optLogDTO.setDescription(controllerDescription + "-" + controllerMethodDescription);
// } else {
// optLogDTO.setDescription(controllerMethodDescription);
// }
optLogDTO.setDescription(controllerDescription + "-" + controllerMethodDescription);
}
private BaseOperationLogSaveAo get() {
BaseOperationLogSaveAo log = THREAD_LOCAL.get();
if (log == null) {
return new BaseOperationLogSaveAo();
}
return log;
}
private void tryCatch(Consumer<String> consumer) {
try {
consumer.accept("");
} catch (Exception e) {
log.warn("记录操作日志异常", e);
THREAD_LOCAL.remove();
}
}
private void publishEvent(BaseOperationLogSaveAo log) {
log.setFinishTime(LocalDateTime.now());
log.setConsumingTime(log.getStartTime().until(log.getFinishTime(), ChronoUnit.MILLIS));
MySpringUtils.publishEvent(new LogEvent(log));
THREAD_LOCAL.remove();
}
/**
* 监测是否需要记录日志
*
* @param joinPoint 端点
* @param log 操作日志
* @return true 表示不需要记录日志
*/
private boolean check(JoinPoint joinPoint, WebLog log) {
if (log == null || !log.enabled()) {
return true;
}
// 读取目标类上的注解
WebLog targetClass = joinPoint.getTarget().getClass().getAnnotation(WebLog.class);
// 加上 log == null 会导致父类上的方法永远需要记录日志
return targetClass != null && targetClass.enabled();
}
/**
* 截取指定长度的字符串
*
* @param val 参数
* @return 截取文本
*/
private String getText(String val) {
return StrUtil.sub(val, 0, 65535);
}
private String getArgs(Object[] args, HttpServletRequest request) {
String strArgs = StrPool.EMPTY;
Object[] params = Arrays.stream(args).filter(item -> !(item instanceof ServletRequest || item instanceof ServletResponse)).toArray();
try {
if (!request.getContentType().contains(FORM_DATA_CONTENT_TYPE)) {
strArgs = JsonUtil.toJson(params);
}
} catch (Exception e) {
try {
strArgs = Arrays.toString(params);
} catch (Exception ex) {
log.warn("解析参数异常", ex);
}
}
return strArgs;
}
}
1.4.2
java
import com.ulinkle.common.core.entity.BaseOperationLogSaveAo;
import org.springframework.context.ApplicationEvent;
/**
* @author 王
* @description
* @createDate
*/
public class LogEvent extends ApplicationEvent {
public LogEvent(BaseOperationLogSaveAo logSaveAo) {
super(logSaveAo);
}
}
1.4.3
java
import com.ulinkle.common.core.entity.BaseOperationLogSaveAo;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import java.util.function.Consumer;
/**
* @author 王
* @description
* @createDate
*/
@Slf4j
@AllArgsConstructor
public class LogListener {
private final Consumer<BaseOperationLogSaveAo> consumer;
@Async
@Order
@EventListener(LogEvent.class)
public void saveSysLog(LogEvent event) {
BaseOperationLogSaveAo sysLog = (BaseOperationLogSaveAo) event.getSource();
consumer.accept(sysLog);
}
}
1.5 util
java
import com.ulinkle.common.core.annotation.WebLog;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import java.lang.reflect.Method;
/**
* 日志工具类
*
* @author zuihou
* @date 2019-04-28 11:30
*/
@Slf4j
public final class LogUtil {
private LogUtil() {
}
/***
* 获取操作信息
*/
public static String getDescribe(JoinPoint point) {
WebLog annotation = getTargetAnnotation(point);
if (annotation == null) {
return StrPool.EMPTY;
}
return annotation.value();
}
public static String getDescribe(WebLog annotation) {
if (annotation == null) {
return StrPool.EMPTY;
}
return annotation.value();
}
/**
* 优先从子类获取 @WebLog:
* 1,若子类重写了该方法,有标记就记录日志,没标记就忽略日志
* 2,若子类没有重写该方法,就从父类获取,父类有标记就记录日志,没标记就忽略日志
*/
public static WebLog getTargetAnnotation(JoinPoint point) {
try {
WebLog annotation = null;
if (point.getSignature() instanceof MethodSignature) {
Method method = ((MethodSignature) point.getSignature()).getMethod();
if (method != null) {
annotation = method.getAnnotation(WebLog.class);
}
}
return annotation;
} catch (Exception e) {
log.warn("获取 {}.{} 的 @WebLog 注解失败", point.getSignature().getDeclaringTypeName(), point.getSignature().getName(), e);
return null;
}
}
public static String getApiOperationValue(JoinPoint point) {
try {
if (point.getSignature() instanceof MethodSignature) {
Method method = ((MethodSignature) point.getSignature()).getMethod();
if (method != null) {
ApiOperation annotation = method.getAnnotation(ApiOperation.class);
if (annotation == null) {
return "";
}
return annotation.value();
}
}
return "";
} catch (Exception e) {
log.warn("获取 {}.{} 的 @ApiOperation 注解失败", point.getSignature().getDeclaringTypeName(), point.getSignature().getName(), e);
return null;
}
}
}
1.5.2
java
import lombok.Getter;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
import java.util.Map;
/**
* Spring工具类
*/
public final class MySpringUtils {
@Getter
private static ApplicationContext applicationContext;
private static ApplicationContext parentApplicationContext;
private MySpringUtils() {
}
/**
* 单例Holder模式: 优点:将懒加载和线程安全完美结合的一种方式(无锁)。(推荐)
*
* @return 实实例
*/
public static MySpringUtils getInstance() {
return SpringUtilsHolder.INSTANCE;
}
public static void setApplicationContext(ApplicationContext ctx) {
Assert.notNull(ctx, "SpringUtil injection ApplicationContext is null");
applicationContext = ctx;
parentApplicationContext = ctx.getParent();
}
public static Object getBean(String name) {
Assert.hasText(name, "SpringUtil name is null or empty");
try {
return applicationContext.getBean(name);
} catch (Exception e) {
return parentApplicationContext.getBean(name);
}
}
public static <T> T getBean(String name, Class<T> type) {
Assert.hasText(name, "SpringUtil name is null or empty");
Assert.notNull(type, "SpringUtil type is null");
try {
return applicationContext.getBean(name, type);
} catch (Exception e) {
return parentApplicationContext.getBean(name, type);
}
}
public static <T> T getBean(Class<T> type) {
Assert.notNull(type, "SpringUtil type is null");
try {
return applicationContext.getBean(type);
} catch (Exception e) {
return parentApplicationContext.getBean(type);
}
}
public static <T> Map<String, T> getBeansOfType(Class<T> type) {
Assert.notNull(type, "SpringUtil type is null");
try {
return applicationContext.getBeansOfType(type);
} catch (Exception e) {
return parentApplicationContext.getBeansOfType(type);
}
}
public static ApplicationContext publishEvent(Object event) {
applicationContext.publishEvent(event);
return applicationContext;
}
/**
* <p>
* 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
* 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
*/
private static class SpringUtilsHolder {
/**
* 静态初始化器,由JVM来保证线程安全
*/
private static final MySpringUtils INSTANCE = new MySpringUtils();
}
}
三.服务配置
需要记录日志的服务增加以下配置
java
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ulinkle.common.api.base.BaseOperateLogFeignClient;
import com.ulinkle.common.core.log.LogAspect;
import com.ulinkle.common.core.log.LogListener;
import com.ulinkle.common.core.utils.MySpringUtils;
import com.ulinkle.pos.api.ItemLogService;
import com.ulinkle.pos.event.ItemLogListener;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.util.List;
/***
*@description
*@author wangShu
*@version 1.0.0
*@create 2023/12/6 11:16
**/
@Configuration
public class WebConfiguration extends WebMvcConfigurationSupport {
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.stream().filter(c -> c instanceof MappingJackson2HttpMessageConverter)
.map(c ->(MappingJackson2HttpMessageConverter)c)
.forEach(c->{
ObjectMapper mapper = c.getObjectMapper();
// 为mapper注册一个带有SerializerModifier的Factory,此modifier主要做的事情为:当序列化类型为array,list、set时,当值为空时,序列化成[]
mapper.setSerializerFactory(mapper.getSerializerFactory().withSerializerModifier(new MyBeanSerializerModifier()));
c.setObjectMapper(mapper);
});
}
@Bean
public ItemLogListener itemLogListener(ItemLogService itemLogService) {
return new ItemLogListener(itemLogService::saveBatch);
}
@Bean
public LogListener sysLogListener(BaseOperateLogFeignClient logApi) {
return new LogListener(logApi::add);
}
@Bean
public LogAspect getLogAspect() {
return new LogAspect();
}
/**
* Spring 工具类
*
* @param applicationContext 上下文
*/
@Bean
public MySpringUtils getMySpringUtils(ApplicationContext applicationContext) {
MySpringUtils instance = MySpringUtils.getInstance();
MySpringUtils.setApplicationContext(applicationContext);
return instance;
}
}