操作日志的记录
因为我们不光要记录代码的运行,如(logback log4j),而且还应该记录用户的行为(Controller 请求日志),这叫做业务运行日志。
业务运行日志的作用
- 记录用户的行为 用于后续的分析
- 记录用户的所有的操作
日志实现的思路
1.我们需要记录哪些数据(谁在什么时间干了什么事情,结果如何)? 数据要存入到哪里(存储到数据库表,推送到指定日志存储服务,打印到日志文件等)?
2.在项目中什么位置记录(一般在Controller请求,进行around 环绕增强)
如何实现记录功能
实现方式:注解+AOP
Aop日志记录 具体代码实现
aop的使用流程,这里使用注解式aop来实现
具体步骤:
- 设置切入点
- 可以切在方法上
- 可以切在注解上
@Transactional 事务注解 注解加在类上 aop 切在注解上
- 写增强 日志记录增强
- 获取日志的相关信息用户的id ip地址, 时间, 操作的描述, 类型等信息
- 将日志对象 添加到指定服务(文件,数据库,系统)

但是,操作的描述如何获取呢?
使用自定义注解:
-
在 目标 方法上添加自定义注解 (@Logger) 如下
-
在增强中获取注解(@Logger)的属性获取
具体实现
依赖引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>1.21</version>
</dependency>
自定义注解
package com.wang.chao.micro.log.annotation;
import com.wang.chao.micro.log.enums.LogSeverity;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Logger {
/**
* 日志来源 操作模块
*
* @return
*/
String operSource() default "";
/**
* 日志级别
*
* @return
*/
LogSeverity severity() default LogSeverity.WARNING;
/**
* 日志操作名称
*
* @return
*/
String operName() default "";
/**
* 是否 持久化(persistence)
*
* @return
*/
boolean isPersistence() default false;
}
使用AOP统一处理日志
package com.wang.chao.micro.log.annotation.aspect;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wang.chao.micro.id.genertor.IdWorker;
import com.wang.chao.micro.log.annotation.Logger;
import com.wang.chao.micro.log.bean.ExceptionOperLog;
import com.wang.chao.micro.log.bean.OperationLog;
import com.wang.chao.micro.log.service.LoggerSupport;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
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.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
@Component
@Aspect
@Slf4j
public class LoggerAspect {
private static final ObjectMapper mapper = new ObjectMapper();
@Pointcut("@annotation(com.wang.chao.micro.log.annotation.Logger)")
public void logger() {
}
@Around("logger()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
Object ret = pjp.proceed();
OperationLog operLog = buildOperationLog(pjp, ret);
// log.info(writeValueAsString(operLog));
LoggerSupport.addAuditLog(operLog);
return ret;
}
@AfterThrowing(value = "logger()", throwing = "throwable")
public void doAfterThrowing(JoinPoint joinPoint, Throwable throwable) {
// 保存异常日志记录
OperationLog operLog = buildExceptionOperationLog(joinPoint, throwable);
LoggerSupport.addAuditLog(operLog);
}
private OperationLog buildExceptionOperationLog(JoinPoint joinPoint, Throwable t) {
ExceptionOperLog operLog = new ExceptionOperLog(buildOperationLog(joinPoint, null));
operLog.setExceptionName(t.getClass().getName()); // 异常名称
operLog.setExceptionMessage(stackTraceToString(t.getClass().getName(), t.getMessage(), t.getStackTrace())); // 异常信息
return operLog;
}
private OperationLog buildOperationLog(JoinPoint joinPoint, Object ret) {
OperationLog operLog = new OperationLog();
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
operLog.setTranceId(IdWorker.nextId());
operLog.setStartTime(System.currentTimeMillis());
operLog.setUrl(request.getRequestURL().toString());
operLog.setMethod(request.getMethod());
// 从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取切入点所在的方法
Method method = signature.getMethod();
// 获取操作日志注解信息
Logger loggerAnnotation = method.getAnnotation(Logger.class);
if (loggerAnnotation != null) {
operLog.setSource(loggerAnnotation.operSource());
operLog.setSeverity(loggerAnnotation.severity());
operLog.setOperName(loggerAnnotation.operName());
operLog.setPersistence(loggerAnnotation.isPersistence());
}
operLog.setRequestMethod(signature.getDeclaringTypeName() + "." + signature.getName());
operLog.setRemoteAddr(request.getRemoteAddr());
operLog.setArgs(writeValueAsString(request.getParameterMap()));
//获取请求头中的User-Agent
UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
operLog.setUserAgent(userAgent);
// pjp.proceed():当我们执行完切面代码之后,还有继续处理业务相关的代码。proceed()方法会继续执行业务代码,并且其返回值,就是业务处理完成之后的返回值。
operLog.setEndTime(System.currentTimeMillis());
operLog.setResult(ret);
return operLog;
}
private String writeValueAsString(Object object){
String json = null;
try {
json = mapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
log.error("object covert json exception,",e);
}
return json;
}
/**
* 转换异常信息为字符串
*
* @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;
}
}
另外日志处理采用了阻塞队列进行数据缓冲处理,日志对象具体消费使用多线程实现。
具体日志服务代码如下:
https://gitee.com/mystarry-sky/microservice-component/tree/master/log-genertor
https://gitee.com/mystarry-sky/microservice-component/
本文介绍了如何在SpringBoot中利用AOP和自定义注解实现操作日志管理,包括记录用户行为、日志实现思路、具体实现步骤、依赖引入以及自定义注解和AOP的使用,旨在提供一种业务运行日志的解决方案。
 实现操作日志管理&spm=1001.2101.3001.5002&articleId=109609432&d=1&t=3&u=249f38d6e1a042989df0b5bb14539d51)
1179

被折叠的 条评论
为什么被折叠?



