欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringBoot使用切面+注解实现对所有请求的日志记录到数据库中

程序员文章站 2022-06-08 23:24:26
...

业务场景:项目拆分微服务,恰巧鄙人负责几个基础的服务,各种对接其他服务。到后期运维,接各种锅:我调用你接口了,你那数据不对。我传递是这个参数,你那处理的有问题......。只好加上日志记录,业务场景就是:防止背锅!

 1.定义注解

package com.haier.hwork.qwrgqk.business.annotation;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLog {
    String value() default "";
    //1-增删改,2-查询,默认是1
    int type() default 1;
}

2.定义AOP

@Aspect
@Component
public class OperationLogAspect {

    @Autowired
    private TlOperationLogDao tlOperationLogDao;

  //扫描使用这个注解的方法
    @Pointcut("@annotation(com.haier.hwork.qwrgqk.business.annotation.OperationLog)")
    public void logPointCut() {}

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Date beginTime=new Date();
        String result=null;
        String status=null;
    //执行代码,都加try catch,即使出错也不能影响主进程
        try {
            Object obj = point.proceed();//放行请求的主进程并拿到返回值
            if(obj!=null) {
                result=JSONObject.toJSONString(obj);
            }
            status="success";
            return obj;
        } catch (Exception e) {
        //请求执行出错
            result=e.getMessage();
            status="error";
            throw e;
        } finally {
        //无论成功失败,都保存下日志
            saveLog(point, beginTime, new Date(), result, status);
        }
    }

  //保存日志
    private void saveLog(ProceedingJoinPoint joinPoint, Date beginTime, Date endTime, String result, String status) {
        TlOperationLog tlOperationLog=new TlOperationLog();
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            OperationLog annotation = method.getAnnotation(OperationLog.class);
            int type=1;//注解类型
            if (annotation != null) {
                //注解上的描述
                tlOperationLog.setOperateModule(annotation.value());
          //注解类型
                tlOperationLog.setOperateType(String.valueOf(annotation.type()));
                type=annotation.type();
            }

      //记录入参
            if(joinPoint.getArgs()!=null) {
                try {
                    tlOperationLog.setParam(JSONObject.toJSONString(joinPoint.getArgs()));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        //如果注解类型是1(增删改)或者是2(查询)报错,记录返回值,如果是查询正常就不记录,有可能返回值太多
            if(type==1 || "error".equals(status)) {
                tlOperationLog.setResult(result);
            }
            tlOperationLog.setStartTime(beginTime);//记录请求开始时间
            tlOperationLog.setEndTime(endTime);//记录请求结束时间
            tlOperationLog.setStatus(status);//记录执行状态

            HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
            if (request != null) {
                tlOperationLog.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));//记录请求头信息
                tlOperationLog.setUserCode(request.getHeader(Constant.HEADER_CURRENT_USER));//获取登录人,登录信息放在了header里
                if(request.getRequestURL()!=null) {//记录请求地址
                    tlOperationLog.setUrl(request.getRequestURL().toString());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    //启动一个线程,执行报错日志,防止影响主请求
        new Thread() {
            @Override
            public void run() {
                try {
            //保存数据到数据库
                    tlOperationLogDao.insertSelective(tlOperationLog);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

3.配置需要记录日志的方法

@PostMapping("/save")
@OperationLog("新增APP TOKEN管理")
public BaseResponse save() {
    return ResponseMsgUtil.success(tsSysAppTokenService.save());
}

凡是加上OperationLog注解的方法,都会记录日志,注解参数需要一个描述当前方法的字符串,会记录到数据库中。
另外还需要参数type,非必填,默认1,代表是增删改,type=2代表查询。
@OperationLog("新增APP TOKEN管理")
@OperationLog(value="查询管理客户接口", type=2)