切面记录日志

3 篇文章 0 订阅
1 篇文章 0 订阅

切面记录日志

	在项目中会出现各种异常,有服务器异常,数据库异常,用户操作异常等各种
问题,在项目运维的时候通常需要根据日志来查问题。有些问题是周期性的,比
如OOM等。使用切面来记录日志不会对代码有侵入性,可以统一管理,方便维护。

导入依赖

<!--切面-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!--hutool工具-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>4.5.6</version>
        </dependency>

设计日志对象

@Data
public class SysLog {
    /**
     * 主键
     */
    private Integer id;

    /**
     * 操作IP
     */
    private String ip;

    /**
     * 操作地点
     */
    private String location;

    /**
     * 操作类型 1 操作记录 2异常记录
     */
    private Integer type;

    /**
     * 操作人ID
     */
    private String userName;

    /**
     * 操作描述
     */
    private String description;

    /**
     * 请求方法
     */
    private String actionMethod;

    /**
     * 请求url
     */
    private String actionUrl;

    /**
     * 请求参数
     */
    private String params;

    /**
     * 操作系统
     */
    private String os;

    /**
     * 浏览器
     */
    private String browser;

    /**
     * 类路径
     */
    private String classPath;

    /**
     * 请求方法
     */
    private String requestMethod;

    /**
     * 开始时间
     */
    private LocalDateTime startTime;

    /**
     * 完成时间
     */
    private LocalDateTime finishTime;

    /**
     * 消耗时间
     */
    private Long consumingTime;

    /**
     * 异常详情信息 堆栈信息
     */
    private String exDetail;

    /**
     * 异常描述 e.getMessage
     */
    private String exDesc;
}

定义切面

@Slf4j
@Component
@Aspect
public class SysLogAspect {

    private ThreadLocal<SysLog> sysLogThreadLocal = new ThreadLocal<>();

    /**
     * 事件发布是由ApplicationContext对象管控
     * 发布事件只需要注入ApplicationContext
     */
    @Autowired
    private ApplicationContext applicationContext;


    /**
     * 定义切入点拦截规则
     */
    @Pointcut("@annotation(com.it.springbootdemo.logger.annotation.SysOperaLog)")
    public void sysLogAspect(){}


    /**
     * 前置拦截
     * @param joinPoint
     */
    @Before(value = "sysLogAspect()")
    public void recordLog(JoinPoint joinPoint) throws Exception {
        SysLog sysLog = new SysLog();
        //将当前实体保存到threadLocal
        sysLogThreadLocal.set(sysLog);
        // 开始时间
        long beginTime = Instant.now().toEpochMilli();
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();


        //访问目标方法的参数 可动态改变参数值
        Object[] args = joinPoint.getArgs();
        //获取执行的方法名
        sysLog.setActionMethod(joinPoint.getSignature().getName());
        // 类名
        sysLog.setClassPath(joinPoint.getTarget().getClass().getName());
        sysLog.setActionMethod(joinPoint.getSignature().getName());
        sysLog.setFinishTime(LocalDateTime.now());
        // 参数
        sysLog.setParams(Arrays.toString(args));
        sysLog.setDescription(LogUtil.getControllerMethodDescription(joinPoint));
        long endTime = Instant.now().toEpochMilli();
        sysLog.setConsumingTime(endTime - beginTime);
    }

    /**
     * 返回通知
     * @param ret
     */
    @AfterReturning(returning = "ret", pointcut = "sysLogAspect()")
    public void doAfterReturning(Object ret){
        //获得当前线程的日志对象
        SysLog sysLog = sysLogThreadLocal.get();
        //处理完请求,返回内容
        R r = Convert.convert(R.class,ret);
        if  (r.getCode() == 200){
            //正常返回
            sysLog.setType(1);
        } else {
            sysLog.setType(2);
            sysLog.setExDetail(r.getMsg());
        }
        //发布事件
        applicationContext.publishEvent(new SysLogEvent(sysLog));
        //移除当前Log实体
        sysLogThreadLocal.remove();
    }

    /**
     * 异常通知
     * @param e
     */
    @AfterThrowing(pointcut = "sysLogAspect()",throwing = "e")
    public void doAfterThrowable(Throwable e){
        SysLog sysLog = sysLogThreadLocal.get();
        //异常
        sysLog.setType(2);
        //异常对象
        sysLog.setExDetail(LogUtil.getStackTrace(e));
        //异常信息
        sysLog.setExDesc(e.getMessage());
        //发布事件
        applicationContext.publishEvent(new SysLogEvent(sysLog));
        //移除当前log实体
        sysLogThreadLocal.remove();
    }

}

异步发布事件监听

public class SysLogEvent extends ApplicationEvent {

    public SysLogEvent(Object source) {
        super(source);
    }
}

@Slf4j
@Component
public class SysLogListener {

    @Async
    @Order
    @EventListener(SysLogEvent.class)
    public void saveSysLog(SysLogEvent enet){
        SysLog sysLog = (SysLog)enet.getSource();
        //保存日志
        System.out.println(sysLog.toString());
    }

}

使用日志注解

@Slf4j
@RestController
public class TestController {

    @Autowired
    OrganizationServiceImp organizationImp;

    @SysOperaLog(descrption = "方法测试")
    @RequestMapping(value = "test")
    public R test(){
        organizationImp.testJdbc();
        int a = 1/0;
        return R.ok();
    }

}

总结:

设计日志对象
定义注解对象
定义切面拦截注解所使用的地方,使用前置拦截拦截到对应方法,在方法中使用
ThreadLocal对象添加日志对象,并设置日志对象属性,使用后置通知与异常通
知来设置日志对象的异常信息等属性,并做异步操作
定义监听事件,日志对象异步发布后开始执行

注意

使用切面拦截的时候有关ThreadLocal和spring的事件发布可以去查阅相关资料
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值