SpringBoot项目,项目中早已整合了一套Spring AOP切面相关技术实现,该有的流程都有,常规操作就是给方法加上自定义注解就可以在被调用时,根据当前请求头获取到用户信息(token)、请求IP地址等信息,然后分两种情况写入数据库日志表:
但是现在有个新的需求,如题,当我尝试将该现有的日志注解写在主线程中的“某个异步方法”时,切面日志类在写入数据库之前有个“获取请求头”信息的的操作理所当然的报错了。
这两天我没少查阅资料,为了存请求头信息,考虑过redis、MQTT、MySQL在记录一条请求头的记录等等,都被我推翻了。目前我采用写死的方案:
然后我将当前的日志切面类(LogAspect)抽离成父类(BaseLogAspect),再定义一个异常日志切面类(LogAsyncAspect),分别继承自父类(BaseLogAspect),其中LogAspect留空不写,完整继承父类;LogAsyncAspect则是部分继承,部分重写父类方法,这里我重写了获取用户请求头信息的那个方法,我不再通过String userStr = request.getHeader(AuthConstant.USER_TOKEN_HEADER);
获取,而是改成用我上面预设的用户信息,请求ip用localhost;(以上还在撸代码,执行结果暂时未知是否符合当前预期)
/**
* @apiNote 日志异步切面<br>
* 部分继承 BaseLogAspect 抽象类,部分重写。
* @since 2024年3月29日
* @author TabKey9
* @version 0.0.1.240329
*/
@Slf4j
@Aspect
@Component
public class LogAsyncAspect extends BaseLogAspect {
@Override
protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
try {
// 虚拟一个异步线程专用账号信息
UserDto userDto = new UserDto();
userDto.setId(-1L);
userDto.setUsername("异步操作专员");
// MySQL 日志记录表实体对象
SysOperLogEntity operLog = new SysOperLogEntity();
// 操作状态
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
// 请求的地址 【获取不到】
operLog.setOperIp("localhost");
// 请求url 【获取不到】
operLog.setOperUrl("/**");
// 操作人员
operLog.setOperName(userDto.getUsername());
if (e != null) {
operLog.setStatus(BusinessStatus.FAIL.ordinal());
operLog.setErrorMsg(e.getMessage());
}
// 设置方法名称
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
operLog.setMethod(className + "." + methodName + "()");
// 设置请求方式
operLog.setRequestMethod("Async");
// 处理设置注解上的参数 【调用父类方法】
getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
// 设置消耗时间 【TIME_THREADLOCAL在父类用protected修饰符定义】
operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
operLog.setOperTime(new Date());
// 保存数据库 【sysOperLogService在父类用protected修饰符定义】
sysOperLogService.save(operLog);
} catch (Exception exp) {
// 记录本地异常日志
log.error("异常信息:{}", exp.getMessage());
exp.printStackTrace();
} finally {
//【TIME_THREADLOCAL在父类用protected修饰符定义】
TIME_THREADLOCAL.remove();
}
}
}
请问:我的做法合理吗?或者有哪里考虑不周吗?以及还有其它解决方案嘛?
当我了解越深入越觉得超纲了,目前我技术不够扎实,硬要把这需求实现起来不仅麻烦,还有一堆可能存在却还是捉摸不透的隐患。改用最传统的方式实现了,try/catch,调异步方法的时候,传入请求头信息,在catch的时候自己写(写一条错误日志到MySQL),或者在finally的时候,虽然简单粗bao,但自我感觉安全可控。
@HW,
ThreadLocal
在同一线程下还有这样的妙用,感谢分享,但是因为切面那一块知识点整不明白,玩不转,这个需求我得简化实现了