Java记录操作日志

java自带的日志框架是java.util.logging(JUL),从JDK1.4(2002)开始捆绑在JDK中。可以使用JUL来记录操作日志。以下是使用JUL记录事务的示例:

// java.util.loggingjava.util.logging.Logger logger = java.util.logging.Logger.getLogger (this.getClass ().getName ());logger.info ("This is an info message");logger.severe ("This is an error message"); // == ERROR

操作日志和系统日志的区别

系统日志:统日志主要是为开发排查问题提供依据,一般打印在日志文件中;系统日志的可读性要求没那么高,日志中会包含代码的信息,比如在某个类的某一行打印了一个日志。
操作日志:主要是对某个对象进行新增操作或者修改操作后记录下这个新增或者修改,操作日志要求可读性比较强,因为它主要是给用户看的,比如订单的物流信息,用户需要知道在什么时间发生了什么事情。

实现操作日志共能一般有两种方法

  1. 第一种就是很传统的做法,就是在每个模块进行插入日志的操作,这种方法虽然实现巨鹿用户操作,但是很繁琐,基本是重复的工作。
  2. 第二种就是使用spring的AOP来实现记录用户操作,也是现在最流行的写法。它的优势在于这种记录用户操作的代码独立于其他业务逻辑代码,不仅实现了解耦,而且避免了冗余代码。

操作日志表

这里我就简单记录一下基本的信息。

需要的一些工具类

获取Ip地址

package com.kl.util;import org.springframework.web.context.request.RequestAttributes;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;import java.net.InetAddress;import java.net.UnknownHostException;/** * @author Wen先森 * @version 1.0 * @date 2023/2/28 10:25 */public class IpUtils {/** * 获取Ip地址 * @return */public static String getIpAddress() {HttpServletRequest request = null;try {RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();request = ((ServletRequestAttributes)requestAttributes).getRequest();} catch (Exception var2) {return null;}return getIpAddr(request);}/** * 获取Ip地址 * @param request * @return */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" : ip;}

获取浏览器和操作系统

<dependency><groupId>eu.bitwalker</groupId><artifactId>UserAgentUtils</artifactId><version>1.21</version></dependency>
UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));// 获取客户端操作系统String os = userAgent.getOperatingSystem().getName();// 获取客户端浏览器String browser = userAgent.getBrowser().getName();

第一种:传统方法

  1. 新增日志实体类、dao层 接口
  2. 这里我是将添加日志编写到工具类中
package com.kl.util;import com.kl.entity.IotMSUser;import com.kl.entity.OprLog;import com.kl.repo.OprLogRepo;import eu.bitwalker.useragentutils.UserAgent;import org.springframework.util.ObjectUtils;/** * @author Wen先森 * @version 1.0 * @date 2023/2/27 19:48 */public class LogUtils {/** * 添加操作日志 * @param content */public static void insertLog (String content) {UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));// 获取客户端操作系统String os = userAgent.getOperatingSystem().getName();// 获取客户端浏览器String browser = userAgent.getBrowser().getName();// 获取用户信息,可以从自己的项目中token中去取,这里就不再描写Integer userId=1;// 获取Ip地址String ipAddress = IpUtils.getIpAddress();OprLog oprLog = new OprLog();oprLog.setIp(ipAddress);oprLog.setUserAgent(browser);oprLog.setUserId(userId);oprLog.setContent(content);oprLog.setCreateTime(FormatUtil.formatDate());// 这里我才用的是JPA,大家也可以换成自己熟悉的框架去添加OprLogRepo oprLogRepo = SpringUtils.getBean(OprLogRepo.class);oprLogRepo.save(oprLog);}}
  1. 在controller层的某一个方法调用该工具类记录操作日志。

第二种:AOP实现记录用户操作

  1. 在pom.xml中添加AOP依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
  1. 自定义操作日志注解
package com.kl.interceptor.aspectj;import java.lang.annotation.*;/** * @author Wen先森 * @version 1.0 * @date 2023/3/6 16:03 * @description: 自定义操作日志注解 */@Target(ElementType.METHOD)//注解放置的目标位置即方法级别@Retention(RetentionPolicy.RUNTIME)//注解在哪个阶段执行@Documentedpublic @interface OprLog {/** * 操作内容 */public String content() default "";}
  1. 自定义操作日志切面类,该类是将操作日志保存到数据库
package com.kl.interceptor.aspectj;import com.kl.entity.IotMSUser;import com.kl.repo.OprLogRepo;import com.kl.util.*;import eu.bitwalker.useragentutils.UserAgent;import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.Signature;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.stereotype.Component;import org.springframework.util.ObjectUtils;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.sql.Timestamp;/** * @author Wen先森 * @date 2023/3/6 16:18 * @version 1.0 * @description: 操作日志切面处理类 */@Slf4j@Aspect@Componentpublic class OperationLogAspect {/** * 设置操作日志切入点 在注解的位置切入代码 */@Pointcut("@annotation(com.kl.interceptor.aspectj.OprLog)")public void oprLogPointCut() {}/** * 记录操作日志 * @param joinPoint 方法的执行点 * @param result方法返回值 * @throws Throwable */@AfterReturning(returning= "result", value = "oprLogPointCut()")public void saveOperLog(JoinPoint joinPoint, Object result){try{// 获得注解OprLog controllerLog = getAnnotationLog(joinPoint);if (controllerLog == null){return;}//将返回值转换成map集合Map<String, String> map = (Map<String, String>) result;// 返回值信息(根据需求决定是否记录)String msg = MapUtils.getString(map, "msg");UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));// 获取客户端操作系统String os = userAgent.getOperatingSystem().getName();// 获取客户端浏览器String browser = userAgent.getBrowser().getName();// 获取用户信息IotMSUser user = TokenUtil.getUserId();// 用户ID,"-1" 代表没有登陆系统用户Integer userId=-1;if (!ObjectUtils.isEmpty(user)){userId=user.getId();}// 获取Ip地址String ipAddress = IpUtils.getIpAddress();com.kl.entity.OprLog oprLog = new com.kl.entity.OprLog();oprLog.setIp(ipAddress);oprLog.setUserAgent(browser);oprLog.setUserId(userId);oprLog.setContent(controllerLog.content());oprLog.setCreateTime(FormatUtil.formatDate());OprLogRepo oprLogRepo = SpringUtils.getBean(OprLogRepo.class);oprLogRepo.save(oprLog);}catch (Exception e){// 记录本地异常日志log.error("==前置通知异常==");log.error("异常信息:{}", e.getMessage());}}/** * 是否存在注解,如果存在就获取 */private OprLog getAnnotationLog(JoinPoint joinPoint) throws Exception{Signature signature = joinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method method = methodSignature.getMethod();if (method != null){return method.getAnnotation(OprLog.class);}return null;}}
  1. 在controller层的某一个方法加入@OprLog注解
最终结果