上篇博文可以通过注解 标注在方法上实现aop,来对某个方法进行加强
呢么 我们该怎么通过aop 来实现日志的记录呢
大致是这样的思路
aop--->记录请求响应信息---> 利用线程池进行insert 操作保存日志信息
@Before("dataProcess()") // 之前操作
@AfterReturning(returning = "ret", pointcut = "dataProcess()") // 之后操作
@AfterThrowing(value = "dataProcess()", throwing = "throwable") // 异常信息
@After("dataProcess()") 首先我们得知道基于切点者4个的顺序
没有异常的时候:Before--->切点-->after--->AfterReturning
有异常的时候:Before--->切点-->after--->AfterThrowing
@Data
public class LogBean {
/**
* 浏览器信息
*/
private String browserInfo;
/**
* 请求URL
*/
private String requestURL;
/**
* 请求类型(get、post、put、delete等)
*/
private String httpMethod;
/**
* 客户端请求IP
*/
private String requestIP;
/**
* 请求参数
*/
private String requestParam;
/**
* 操作的类和方法
*/
private String operateClassMethod;
/**
* 耗时
*/
private long consumeTime;
/***
* 响应结果
*/
private String responseResult;
/**
* 操作描述
*/
private String operateDesc;
/**
* 异常信息
*/
private String exceptionMsg;
/**
* 操作时间
*/
private String operateTime;
}
// 我们封装一个javabean 可以重写他的tostring 方式 让他的tosting 编程日志格式 类似于下面的
然后我们在声明一个 注解 默认是持久化的 第一个参数就是你的比如说 保存用户
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebLog {
// 操作描述
String value() default "";
// true 表示持久化日志,false表示不持久化
boolean persistent() default true;
}
spring的aop 记录 请求响应信息
@Aspect
@Component
public class WebLogAspect {
private static final Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
private ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>();
private ThreadLocal<LogBean> webLogBeanThreadLocal = new ThreadLocal<>();
private ThreadLocal<Boolean> isPersistentThreadLocal = new ThreadLocal<>();
/**
* 日志处理类
*/
@Autowired
private WebLogHandler webLogHandler;
@Pointcut(value = "@annotation(com.util.annotation.WebLog)")
public void dataProcess() {
}
@Before("dataProcess()") // 之前操作
public void doBefore(JoinPoint joinPoint) throws Exception {
logger.info("-------------我处理之前-------------");
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
LogBean logBean = new LogBean();
logBean.setBrowserInfo(request.getHeader("User-Agent"));
logBean.setRequestURL(request.getRequestURL().toString());
String reqmethod = request.getMethod();
logBean.setHttpMethod(reqmethod);
logBean.setRequestIP(request.getRemoteAddr());
// get请求
// 存放请求数据
/*get请求存储数据*/
if ("GET DELETE".contains(reqmethod)) {
HashMap<Object, Object> map = new HashMap<>();
Map<String, String[]> parameterMap = request.getParameterMap();
if (!ObjectUtils.isEmpty(parameterMap)) {
// 获取请求数据
for (String key : parameterMap.keySet()) {
String[] parmValue = parameterMap.get(key);
String finalValue = !ObjectUtils.isEmpty(parmValue) ? Arrays.stream(parmValue).collect(Collectors.joining(",")) : "";
map.put(key, finalValue);
}
}
logBean.setRequestParam(map.toString());
} else {
/*Post请求存储数据*/
InputStream in = request.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuffer sb = new StringBuffer();
String str = "";
while ((str = reader.readLine()) != null) {
sb.append(str);
}
logBean.setRequestParam(sb.toString());
}
logBean.setOperateClassMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
// 当前方法名
Method method = methodSignature.getMethod();
WebLog webLog = method.getAnnotation(WebLog.class);
/*反射获取此时这个方法的描述*/
logBean.setOperateDesc(webLog.value());
if (logger.isInfoEnabled()) {
logger.info("请求参数:[" + logBean.toString() + "]");
}
startTimeThreadLocal.set(System.currentTimeMillis());// 记录时间 消耗时间
isPersistentThreadLocal.set(webLog.persistent()); // 记录是否持久化
webLogBeanThreadLocal.set(logBean);// 记录这个bean
}
@AfterReturning(returning = "ret", pointcut = "dataProcess()") // 之后操作
public void doAfterReturning(Object ret) throws Throwable {
if (webLogBeanThreadLocal.get() != null) {
LogBean logBean = webLogBeanThreadLocal.get();
if (startTimeThreadLocal.get() != null) {
long startTime = startTimeThreadLocal.get();
/*记录时间*/
logBean.setConsumeTime(System.currentTimeMillis() - startTime);
}
if (ObjectUtils.isEmpty(ret)) {
ret = "";
}
logBean.setResponseResult(ret.toString());
String nowTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
logBean.setOperateTime(nowTime);
if (logger.isInfoEnabled()) {
// logger.info("请求处理结束,参数:["+JSON.toJSONString(logBean)+"]");
}
Boolean persistentFlag = null;
if (isPersistentThreadLocal.get() != null) {
persistentFlag = isPersistentThreadLocal.get();
}
webLogHandler.processLog(logBean, persistentFlag);
}
startTimeThreadLocal.remove();
webLogBeanThreadLocal.remove();
isPersistentThreadLocal.remove();
}
@After("dataProcess()")
public void after(JoinPoint jp) throws IOException {
logger.info("-------------我处理完毕了-------------");
}
//后置异常通知
@AfterThrowing(value = "dataProcess()", throwing = "throwable") // 异常信息
public void throwException(JoinPoint jp, Throwable throwable) {
logger.error("业务处理发生异常",throwable);
if (webLogBeanThreadLocal.get() != null) {
LogBean logBean = webLogBeanThreadLocal.get();
if (startTimeThreadLocal.get()!=null) {
long startTime = startTimeThreadLocal.get();
logBean.setConsumeTime(System.currentTimeMillis() - startTime);
}
logBean.setExceptionMsg(throwable.getMessage());
String nowTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
logBean.setOperateTime(nowTime);
boolean persistentFlag=false;
if (isPersistentThreadLocal.get()!= null) {
persistentFlag=isPersistentThreadLocal.get();
}
webLogHandler.processLog(logBean, persistentFlag);
}
startTimeThreadLocal.remove();
webLogBeanThreadLocal.remove();
isPersistentThreadLocal.remove();
}
}
webLogHandler.processLog(logBean, persistentFlag);
核心就在于这里 记录下来的日志信息我们需要利用异步 就得利用多线程来进行操作
public interface WebLogHandler<T> {
/**
* 处理日志
* @param t 日志对象
* @param isPersistent 是否需要持久化 true表示需要持久化、false表示不需要
* @throws LogPersistenceException
*/
void processLog(T t,boolean isPersistent) throws RuntimeException;
/**
* 持久化日志
* @param t 持久化日志对象到数据库
* @throws LogPersistenceException
*/
void persistenceLog(T t) throws RuntimeException;
@Service
public class DefaultWebLogHandler implements WebLogHandler<LogBean> {
@Autowired
ThreadPoolTaskExecutor threadPoolTaskExecutor; // 线程池
@Autowired
OperationLogService operationLogService;// 日志操作类 这个类用来做日志的insert操作
@Override
public void processLog(LogBean logBean, boolean isPersistent) throws RuntimeException {
if (isPersistent) {
this.persistenceLog(logBean);
}
}
@Override
public void persistenceLog(LogBean logBean) throws RuntimeException {
this.threadPoolTaskExecutor.execute(
new OperationLogThread(operationLogService,logBean));
}
线程池 config 配置 可以利用@value读取文件
@Configuration
public class LogConfiguration {
private int corePoolSize=5;
private int maxPoolSize=10;
@Bean
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor=new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(corePoolSize);
threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize);
threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
return threadPoolTaskExecutor;
}
}
线程池 操作线程 对日志进行insert
public class OperationLogThread implements Runnable {
private OperationLogService operationLogService;
private LogBean logBean;
public OperationLogThread(OperationLogService operationLogService, LogBean logBean) {
this.operationLogService=operationLogService;
this.logBean=logBean;
}
@Override
public void run() {
operationLogService.insertLog( logBean);
}
}
public interface OperationLogService {
void insertLog(LogBean logBean);
}
此时应该连接db 做insert 操作 我此时也就吧日志打印出来了
@Service
public class OperationLogServiceImpl implements OperationLogService {
@Override
public void insertLog(LogBean logBean) {
System.out.println(logBean.toString());
}
}
最后可以把这个commonLog打成jar
<dependency>
<artifactId>commonLog</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</dependency>
@GetMapping
@WebLog(value="用户新增")
public String getResult() {
System.out.println("访问了");
return "abc";
}