springboot项目通过aop完成日志log切入

 上次JWT的技术。也是通过aop切面完成的,这次也是通过aop技术完成咱们系统日志的切入 收集。  对logstash+kibana 感兴趣的可以自己玩玩,或者等我给同学们分享一下。

 

1.废话不多,直接代码

 

LogAspect:
package com.hbg.hbgserver.aspect;

import com.hbg.common.exception.BusinessException;
import com.hbg.common.manager.IInterfaceLogManager;
import com.hbg.common.model.entity.InterfaceLog;
import com.hbg.hbgserver.util.JwtTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;

@Slf4j
@Component
@Aspect
@Order(1)
public class LogAspect {

    @Autowired
    private IInterfaceLogManager manager;

    @Pointcut("within(com.hbg.hbgserver.controller..*)")
    public void log() {

    }

    /**
     * @param point 获取上下文
     * @return
     * @Before在调用方法前执行
     */
    @Around("log()")
    public Object round(ProceedingJoinPoint point) throws Exception {
        Object result = null;
        long beginTime = System.currentTimeMillis();
        ServletRequestAttributes sra = null;
        try {
            //log.info("在目标方法之前执行!");
            sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (null == sra) {
                log.error("ServletRequestAttributes is null ");
                return point.proceed();
            }
            result = point.proceed();
        } catch (Throwable e) {
            log.info(e.getMessage());
            if (e instanceof Exception) {
                throw (Exception)e;
            }
        } finally {
            if (sra != null) {
                long endTime = System.currentTimeMillis();
                insertLog(point, sra, endTime - beginTime);
            }
            //log.info("方法执行完调用");
        }
        return result;
    }

    /**
     * 插入日志记录表
     *
     * @param point
     * @param sra
     * @param time
     */
    public void insertLog(ProceedingJoinPoint point, ServletRequestAttributes sra, long time) {
        HttpServletRequest request = sra.getRequest();
        HttpServletResponse response = sra.getResponse();
        //请求路径
        String url = request.getRequestURI();
        //ip
        String loginIp = request.getRemoteAddr();
        //返回状态码
        Integer status = response.getStatus();
        //参数
        Object[] argsObj = point.getArgs();
        //String token = Arrays.stream(argsObj).filter(arg -> arg.equals("token")).findFirst().orElse(0).toString();
        String token = request.getHeader("token");
        Long userId = null;
        if(StringUtils.isNotBlank(token)){
            userId = JwtTokenUtil.getUserIdByToken(token);
        }
        String args = Arrays.toString(argsObj);

        InterfaceLog interfaceLog = new InterfaceLog();
        interfaceLog.setActionName(url);
        interfaceLog.setParam(args);
        interfaceLog.setDealTime((int) time);
        interfaceLog.setStatus(status);
        interfaceLog.setUserId(userId);
        interfaceLog.setLoginIp(loginIp);
        manager.save(interfaceLog);
    }
}

 

完事。简单明了。一个类完成系统的日志,收集。 上面对aop机制不是很熟悉的同学,那就是学习spring 的aop机制,看这个代码是怎么运行插入日志。

 

 

还有一种玩法:

OperationLog:

package com.hbg.hbgadmin.annotation;

import com.hbg.common.enums.BackOperationEnum;

import java.lang.annotation.*;

/**
 * 后台操作日志Log
 * @author zxf
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLog {

    /**
     * 模块
     * @return
     */
    BackOperationEnum.Module module();

    /**
     * 操作类型
     * @return
     */
    BackOperationEnum.Operation operation();

    /**
     * 详细描述
     * @return
     */
    String desc() default "";

}

 

OperationLogAspect:

package com.hbg.hbgadmin.aspect;

import com.hbg.common.enums.BackOperationEnum;
import com.hbg.common.exception.BusinessException;
import com.hbg.common.model.entity.BackOperateLog;
import com.hbg.common.model.entity.BackUser;
import com.hbg.hbgadmin.annotation.OperationLog;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.Optional;

/**
 * 后台操作日志
 * @author zxf
 */
@Slf4j
@Component
@Aspect
public class OperationLogAspect {

    @Pointcut("@annotation(com.hbg.hbgadmin.annotation.OperationLog)")
    public void log() {
    }

    @Around(value = "log() && @annotation(operationLog)",
            argNames = "joinPoint, operationLog")
    public Object round(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Exception {
        Object result = null;
        final BackUser[] backUsers = {null};
        try {
            Optional.ofNullable(SecurityUtils.getSubject()).map(Subject::getPrincipal).ifPresent(object -> {
                backUsers[0] = (BackUser) object;
            });
            result = joinPoint.proceed();
        } catch (Throwable e) {
            if (!(e instanceof BusinessException)) {
                log.error(e.getMessage(), e);
            }
            if (e instanceof Exception) {
                throw (Exception)e;
            }
        } finally {
            if (backUsers[0] != null) {
                insertLog(backUsers[0], operationLog.module(), operationLog.operation(),
                        OperationLogHelper.formatOperationDesc(operationLog.desc()));
            }
        }
        return result;
    }

    /**
     * 插入日志记录表
     * @param backUser  后台用户
     * @param module    模块枚举
     * @param operation 操作枚举
     * @param desc      描述
     */
    @Async
    public void insertLog(BackUser backUser, BackOperationEnum.Module module, BackOperationEnum.Operation operation, String desc) {
        new BackOperateLog().setBackUserId(backUser.getId())
                .setOperateType(operation.name()).setDesc(desc)
                .setModuleName(module.getName())
                .setModuleType(module.getType()).insert();
    }
}

OperationLogHelper:

package com.hbg.hbgadmin.aspect;

import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

/**
 * @author zxf
 * @date 2020/9/10 20:36
 */
@Slf4j
public final class OperationLogHelper {

    static final ThreadLocal<List<Object>> OP_THREAD_LOCAL = ThreadLocal.withInitial(Lists::newArrayList);

    /**
     * 设置参数
     * @param object    参数
     */
    public static void addParams(Object ...object) {
        for (Object o : object) {
            OP_THREAD_LOCAL.get().add(o);
        }
    }

    /**
     * 格式化参数
     * @param desc 详细描述
     * @return
     */
    static String formatOperationDesc(String desc) {
        try {
            if (OP_THREAD_LOCAL.get().size() == 0) {
                return desc;
            }
           return String.format(desc, OP_THREAD_LOCAL.get().toArray(new Object[0]));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return StrUtil.EMPTY;
        } finally {
            OP_THREAD_LOCAL.remove();
        }

    }


}

 

 

 

 

使用方式:

那就是在service方法实现使用:

 

 

顺便给大家一个枚举新玩法:

BackOperationEnum
package com.hbg.common.enums;

import lombok.Getter;

/**
 * 后台操作 枚举
 *
 * @author zxf
 */
public class BackOperationEnum {

    /**
     * 模块
     */
    @Getter
    public enum Module {
        /**
         * 划分模块用以记录用户的操作
         */
        GOODS(1, "商品"),
        ORDER(2, "订单"),
        MEMBER(3, "会员"),
        PUSHER(4, "推手"),
        CUSTOMER_SERVICE(5, "客服"),
        LIVE_VIDEO(6, "直播"),
        SETTING(7, "设置"),
        COUPON(9,"优惠券"),
        AFTER_SALE(8, "售后");


        /**
         * 类型:对应数据库记录类型
         */
        private int type;

        /**
         * 操作的文字描述
         */
        private String name;

        Module(int type, String name) {
            this.type = type;
            this.name = name;
        }
    }

    /**
     * 操作
     */
    @SuppressWarnings("NonAsciiCharacters")
    @Getter
    public enum Operation {
        /**
         * 商品
         */
        商品列表, 商品发布, 商品编辑, 商品上下架, 商品删除, 保存分类, 删除分类, 保存分组, 删除分组, 删除分组商品, 添加或编辑供应商, 删除供应商, 保存规格, 删除规格,


        /**
         * 订单
         */
        客服添加备注, 查询列表, 订单详情, 手动发货, 导出待发货订单, 订单批量发货, 订单发货记录, 订单发货明细, 订单评价审核, 订单评价删除, 订单评价列表查询,订单收货地址修改,
        交易记录导出,交易记录查询,

        /**
         * 设置
         */
        新增广告位, 编辑广告位, 删除广告位, 保存运费模板, 删除运费模板, 新增导航分类, 编辑导航分类, 删除导航分类,
        修改商城设置, 修改支付设置, 修改交易设置, 修改短信设置, 修改售后设置,
        /**
         * 售后
         */
        售后审核, 售后退换货审核, 售后确认收货审核, 售后退货退款列表导出, 售后换货列表导出, 售后退款单列表导出, 售后订单列表导出,
        新增退款原因, 删除退款原因, 修改退款原因, 订单主动部分退款, 订单主动整单退,


        /**
         * 直播
         */
        直播间查询列表, 直播间交易转化, 删除直播间, 获取回放,

        /**
         * 会员
         */
        会员列表, 会员详情, 会员编辑资料,
        /**
         * 推手
         */
        推手列表,
        /**
         * 限时折扣
         */
        添加限时折扣, 根据id查询,
        /**
         * 优惠券
         */
        定向发放优惠券,创建优惠券,修改优惠券,删除用户优惠券,


    }
}

以上就是aop 基于日志的玩法。这都是实际业务开发的玩法,不懂自行学习。  如果实在看不懂,还可以联系我购买源码-保证实用- 一次服务10元。

 

posted @ 2020-11-10 14:42  技术专家  阅读(409)  评论(0)    收藏  举报