1、依赖
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.11</version>
</dependency>
<!-- AOP切面 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、自定义切入点枚举类
OperaLog.java
import java.lang.annotation.*;
/**
* 自定义操作日志注解
*/
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented
public @interface OperaLog {
String operaModule() default ""; // 操作模块
String operaType() default ""; // 操作类型
String operaDesc() default ""; // 操作说明
}
3、日志记录表、错误日志表
OperationLog.java
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import java.io.Serializable;
import java.util.Date;
import com.dj.core.bean.EnumModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
* 请求日志表
* </p>
*
* @author jack
* @since 2020-12-24
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_operation_log")
public class OperationLog extends Model<OperationLog> {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "oper_id", type = IdType.AUTO)
private Long operId;
/**
* 功能模块
*/
private String operModul;
/**
* 操作类型
*/
private Integer operType;
/**
* 操作描述
*/
private String operDesc;
/**
* 请求参数
*/
private String operRequParam;
/**
* 返回参数
*/
private String operRespParam;
/**
* 操作人ID
*/
private Integer operUserId;
/**
* 操作人姓名
*/
private String operUserName;
/**
* 操作方法
*/
private String operMethod;
/**
* 请求URI
*/
private String operUri;
/**
* 请求IP
*/
private String operIp;
/**
* 版本
*/
private String operVer;
/**
* 操作时间
*/
private Date operCreateTime;
@Override
protected Serializable pkVal() {
return this.operId;
}
/**
* 操作类型(1-系统登录 2-内容管理)
*/
public enum OperType implements EnumModel {
login(1, "系统登录"), content(2, "内容管理");
private Integer value; // 值
private String text; // 文本
private OperType(Integer value, String text) {
this.value = value;
this.text = text;
}
public Integer getValue() {
return value;
}
public String getText() {
return text;
}
}
}
ExceptionLog.java
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
* 请求错误日志表
* </p>
*
* @author jack
* @since 2020-12-24
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_exception_log")
public class ExceptionLog extends Model<ExceptionLog> {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "exc_id", type = IdType.AUTO)
private Long excId;
/**
* 请求参数
*/
private String excRequParam;
/**
* 异常名称
*/
private String excName;
/**
* 异常信息
*/
private String excMessage;
/**
* 操作人ID
*/
private Integer operUserId;
/**
* 操作人姓名
*/
private String operUserName;
/**
* 操作方法
*/
private String operMethod;
/**
* 请求URI
*/
private String operUri;
/**
* 请求IP
*/
private String operIp;
/**
* 版本
*/
private String operVer;
/**
* 操作时间
*/
private Date operCreateTime;
@Override
protected Serializable pkVal() {
return this.excId;
}
}
枚举类:EnumModel .java
/**
* @Description: 枚举模型
* @author Dreamji
* @date 2017年11月10日 下午8:13:16
*/
public interface EnumModel {
// 获取值
public Integer getValue();
// 获取文本
public String getText();
}
4、定义操作日志切面
备注:operationLogService、exceptionLogService 实现类在此省略。
OperaLogAspect.java
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @program: bike-lease
* @description: AOP日志
* @author: Jack.Fang
* @date:2020-12-22 1104
**/
@Aspect
@Component
@Slf4j
public class OperaLogAspect {
/**
* 操作版本号
* <p>
* 项目启动时从命令行传入,例如:java -jar xxx.war --version=201902
* </p>
*/
@Value("${version}")
private String operVer;
@Autowired
private OperationLogService operationLogService;
@Autowired
private ExceptionLogService exceptionLogService;
public UserContext getUserContext() {
return UserContext.getCurrentContext();
}
/**
* 设置操作日志切入点 记录操作日志 在注解的位置切入代码
*/
@Pointcut("@annotation(com.dj.yier.aop.OperaLog) || execution(* com.dj.admin.controller.AdminLoginController.doLogin(..)))")
public void operLogPoinCut() {
}
/**
* 设置操作异常切入点记录异常日志 扫描所有controller包下操作
*/
@Pointcut("execution(public * com.dj.*.controller.*.*(..))")
public void operExceptionLogPoinCut() {
}
/**
* 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
*
* @param joinPoint 切入点
* @param keys 返回结果
*/
@AfterReturning(value = "operLogPoinCut()", returning = "keys")
public void saveOperLog(JoinPoint joinPoint, Object keys) {
try {
// 获取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
// 从获取RequestAttributes中获取HttpServletRequest的信息
HttpServletRequest request = (HttpServletRequest) requestAttributes
.resolveReference(RequestAttributes.REFERENCE_REQUEST);
MyLogRecord operlog = new MyLogRecord();
// 从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取切入点所在的方法
Method method = signature.getMethod();
// 获取操作
LogRecord opLog = method.getAnnotation(LogRecord.class);
if (opLog != null) {
String operModul = opLog.operaModule();
String operType = opLog.operaType();
String operDesc = opLog.operaDesc();
operlog.setOperationModel(operModul); // 操作模块
operlog.setOperationType(operType); // 操作类型:2-内容管理
operlog.setOperationDesc(operDesc); // 操作描述
}
// 请求的参数
Map<String, String> rtnMap = converMap(request.getParameterMap());
// 获取方法参数
Object[] args = joinPoint.getArgs();
Parameter[] parameters = method.getParameters();
Map<String, Object> parameterValues = new HashMap<>();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < parameters.length; i++) {
parameterValues.put(parameters[i].getName(), args[i]);
builder.append( args[i]);
builder.append(",");
}
String params = "";
if (CollectionUtil.isNotEmpty(rtnMap)){
// 将参数所在的数组转换成json
params = JSON.toJSONString(rtnMap);
}else {
params = builder.toString();
}
// 获取请求的类名
String className = joinPoint.getTarget().getClass().getName();
// 获取请求的方法名
String methodName = method.getName();
operlog.setOperationReqParam(params); // 请求参数
operlog.setOperationMethod(className + "." + methodName); // 请求方法
if (ObjectUtil.isNotEmpty(keys)){
R result = (R)keys;
if(0 == result.getCode()){
operlog.setOperationResult("成功");
operlog.setOperationRespParam(JSON.toJSONString(result.getData()));
}else{
operlog.setOperationResult("失败");
operlog.setFileReason(result.getMsg());
}
}
//String respParam = JSON.toJSONString(keys);
// R result = JSON.parseObject(respParam, R.class);
// if ()
// if(respParam.length()>1000){//防止过长
// respParam = respParam.substring(0,1000);
// }
// operlog.setOperRespParam(respParam); // 返回结果
Integer userId = UserUtil.getUserId(request);
// Integer userId = 1;
MyUser myUser = myUserMapper.selectById(userId);
operlog.setUserId(userId.toString());
operlog.setUserName(myUser.getUsername()); // 请求用户ID
operlog.setNickName(myUser.getNickname()); // 请求用户名称
operlog.setOperationIp(getIpAddress(request)); // 请求IP
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
operlog.setOperationTime(format.format(new Date())); // 创建时间
// operlog.setOperVer(operVer); // 操作版本
operationLogService.save(operlog);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 异常返回通知,用于拦截异常日志信息 连接点抛出异常后执行
*
* @param joinPoint 切入点
* @param e 异常信息
*/
@AfterThrowing(pointcut = "operExceptionLogPoinCut()", throwing = "e")
public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
// 获取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
// 从获取RequestAttributes中获取HttpServletRequest的信息
HttpServletRequest request = (HttpServletRequest) requestAttributes
.resolveReference(RequestAttributes.REFERENCE_REQUEST);
ExceptionLog excepLog = new ExceptionLog();
try {
// 从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取切入点所在的方法
Method method = signature.getMethod();
// 获取请求的类名
String className = joinPoint.getTarget().getClass().getName();
// 获取请求的方法名
String methodName = method.getName();
methodName = className + "." + methodName;
// 请求的参数
Map<String, String> rtnMap = converMap(request.getParameterMap());
// 将参数所在的数组转换成json
String params = JSON.toJSONString(rtnMap);
UserContext userContext = getUserContext();
excepLog.setExcRequParam(params); // 请求参数
excepLog.setOperMethod(methodName); // 请求方法名
excepLog.setExcName(e.getClass().getName()); // 异常名称
excepLog.setExcMessage(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace())); // 异常信息
excepLog.setOperUserId(userContext.getUserId()); // 操作员ID
excepLog.setOperUserName(userContext.getUserName()); // 操作员名称
excepLog.setOperUri(request.getRequestURI()); // 操作URI
excepLog.setOperIp(getIpAddress(request)); // 操作员IP
excepLog.setOperVer(operVer); // 操作版本号
excepLog.setOperCreateTime(new Date()); // 发生异常时间
exceptionLogService.save(excepLog);
} catch (Exception e2) {
e2.printStackTrace();
}
}
/**
* 转换request 请求参数
*
* @param paramMap request获取的参数数组
*/
public Map<String, String> converMap(Map<String, String[]> paramMap) {
Map<String, String> rtnMap = new HashMap<String, String>();
for (String key : paramMap.keySet()) {
rtnMap.put(key, paramMap.get(key)[0]);
}
return rtnMap;
}
/**
* 转换异常信息为字符串
*
* @param exceptionName 异常名称
* @param exceptionMessage 异常信息
* @param elements 堆栈信息
*/
public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
StringBuffer strbuff = new StringBuffer();
for (StackTraceElement stet : elements) {
strbuff.append(stet + "\n");
}
String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
return message;
}
public static String getIpAddress(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
log.debug("[操作日志][getIpAddress] 获取到的ip为:{}",ip);
if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
// 获取第一个非unknown的IP地址
String[] ipList = ip.split(",");
for (int i = 0; i < ipList.length; i++) {
String strIp = ipList[i].trim();
if (!"unknown".equalsIgnoreCase(strIp)) {
ip = strIp;
break;
}
}
} else {
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;
}
}
5、在需要记录日志的Controller方法上打上@OperaLog注解
@OperaLog(operaModule="审批管理",operaType="查询",operaDesc="查询审批列表")
@RequestMapping("/index")
public ModelAndView index(HttpServletRequest request, HttpServletResponse response, MakeOrder info) {
Page<MakeOrder> page = new Page<>(request);
page = makeOrderService.getMakeOrderPage(page, info);
return mav("admin/approval/index").addObject(Page.PAGE_KEY, page).addObject("info", info);
}
-----------------------------------
java 操作记录功能实现 java记录用户操作日志