基于spring aop和MongoDB的日志储存管理

3.基于spring aop和MongoDB的日志储存管理

封装的日志实体:

@Data
public class LogInfo {

    private String requestMethod;
    private String requestIp;
    private String requestUrl;
    private Map<String,String[]> parameters;
    private Object[] args;
    private Date gmtCreate;
    private String requestTime;
    private Object returnValue;
    private String errorMsg;
}

service层

@Service
public class LogInfoServiceImpl implements LogInfoService {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 插入
     * @param logInfo
     */
    @Override
    public void insert(LogInfo logInfo) {
        //插入
        mongoTemplate.save(logInfo);
        //可以使用save或者insert完成插入
        //mongoTemplate.insert(logInfo,"collection_name");
    }

    @Override
    public void update(String requestIp,LogInfo logInfo) {
        //通过where来设置update依据的属性,然后在来选择update依据属性的依据值(类似jpa)
        //  类似:gt:大于,gte:大于等于
        //      Query query1 = new Query(Criteria.where("requestIp").gt(requestIp));
        Query query = new Query(Criteria.where("requestIp").is(requestIp));
        //更新采用update对象
        //  类似sql中的set,(属性,值)
        Update update = new Update();
        update.set("gmtCreate",logInfo.getGmtCreate());
        //更新一条数据
        mongoTemplate.updateFirst(query,update,LogInfo.class);
        //更新多条数据
        mongoTemplate.updateMulti(query,update,LogInfo.class);
        //默认返回旧对象,然后更新新对象
        mongoTemplate.findAndModify(query,update,LogInfo.class);
    }

    @Override
    public void delete(String requestIp) {
        //删除和更新类似
        //通过where来设置update依据的属性,然后在来选择update依据属性的依据值(类似jpa)
        //  类似:gt:大于,gte:大于等于
        //      Query query1 = new Query(Criteria.where("requestIp").gt(requestIp));
        Query query = new Query(Criteria.where("requestIp").is(requestIp));
        //调用remove删除
        mongoTemplate.remove(query,LogInfo.class);
        //带返回值的删除
        List<LogInfo> list = mongoTemplate.findAllAndRemove(query,LogInfo.class);
    }

    /**
     * 查询全部日志
     * @return
     */
    @Override
    public List<LogInfo> select(){
        return mongoTemplate.findAll(LogInfo.class);
        //·条件查询
        //mongoTemplate.find(query,LogInfo.class);
    }
}

切面类

@Component
@Aspect
@Slf4j
public class AopUtils {

    @Autowired
    private LogInfoService logInfoService;

    /**
     * 切入点表达式
     *      1.格式:方法修饰符 + 返回值类型 + 包名 + 类名 + 方法名 +方法参数
     */
    @Pointcut("execution(public * com.springboot.learning.controller.*.*(..))")
    public void log(){

    }

    //环绕通知
    @Around("log()")
    public Object arround(ProceedingJoinPoint joinPoint) {
        LogInfo logInfo = new LogInfo();
        try {
            log.info("前置通知");
            // 接收到请求,记录请求内容
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            //请求方式
            String requestMethod = request.getMethod();
            //请求的ip
            String requestIp = request.getRemoteAddr();
            //请求的url
            String requestUrl = request.getRequestURI().toString();
            //请求的参数
            Map<String,String[]> parameters = request.getParameterMap();
            Object[] args = joinPoint.getArgs();
            logInfo.setArgs(args);
            //请求时间
            Date requestDate = new Date();
            //请求精确时间
            logInfo.setGmtCreate(requestDate);
            //请求时间:yyyy-mm-dd
            DateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
            //格式转换
            String requestTime = dateFormat.format(requestDate);
            logInfo.setRequestTime(requestTime);
            logInfo.setParameters(parameters);
            logInfo.setRequestIp(requestIp);
            logInfo.setRequestMethod(requestMethod);
            logInfo.setRequestUrl(requestUrl);

            //调用目标方法
            /**
             * Spring AOP的环绕通知和前置、后置通知有着很大的区别,主要有两个重要的区别:
             *      1)目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,
             *         而前置和后置通知是不能决定的,它们只是在方法的调用前后执行通知而已,
             *         即目标方法肯定是要执行的。joinPoint.proceed()就是执行目标方法的代码。
             *      2)环绕通知可以控制返回对象,即可以返回一个与目标对象完全不同的返回值。虽然这很危险,但是却可以做到。
             */
            Object result = joinPoint.proceed();
            log.info("后置通知");
            //目标方法返回值
            logInfo.setReturnValue(result);
            return result;
        } catch (Throwable e) {
            //异常信息
            String errorMsg = e.getMessage();
            logInfo.setErrorMsg(errorMsg);
            return null;
        }finally {
            //插入MongoDB
            logInfoService.insert(logInfo);
        }
    }




//
//    /**
//     * 前置通知
//     *      方法执行前通知
//     */
//    @Before("log()")
//    public void before(JoinPoint joinPoint) throws Exception {
//
//    }
//
//
//    /**
//     * 后置通知
//     *      方法执行后通知
//     */
//    @After("log()")
//    public void after(JoinPoint joinPoint){
//        log.info("后置通知");
//
//    }
//
//    /**
//     * 方法返回通知
//     *      接收方法返回值
//     * @param result
//     */
//    @AfterReturning(returning = "result",pointcut = "log()")
//    public void afterReturning(Object result){
//        //获取方法返回值
//        LogInfo logInfo  = new LogInfo();
//        logInfo.setReturnValue(result);
//    }
//
//    /**
//     * 后置异常通知
//     *      当方法抛出异常时调用
//     */
//    @AfterThrowing("log()")
//    public void afterThrowing(JoinPoint joinPoint){
//
//    }
}

从controller层开始进行请求的监控,记录请求日志

在这里插入图片描述github

发布了254 篇原创文章 · 获赞 136 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_41922289/article/details/102519646
今日推荐