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元。

浙公网安备 33010602011771号