IT袋

当前位置:主页 > 经验教程 > 建站编程 >

Spring

Spring Event与AOP结合:优雅记录日志的艺术(2)

时间:2023-12-19 21:11:40 来源:IT袋 作者:马勇
导读:Spring,【步骤一】:配置application.yml 配置项目信息 server: port: 8890 【步骤二】:创建OptLogDTO类,用于封装操作日志信息 在 com.zbbmeta.dto 包下创建 OptLogDTO 类 @Data

Spring

【步骤一】:配置application.yml

配置项目信息

server:
  port: 8890

【步骤二】:创建OptLogDTO类,用于封装操作日志信息

com.zbbmeta.dto包下创建OptLogDTO

@Data
@Accessors(chain = true)
public class OptLogDTO implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 日志类型
     */
    private String type;
    /**
     * 日志标题
     */
    private String title;
    /**
     * 操作内容
     */
    private String operation;
    /**
     * 执行方法
     */
    private String method;
    /**
     * 请求路径
     */
    private String url;
    /**
     * 参数
     */
    private String params;
    /**
     * ip地址
     */
    private String ip;
    /**
     * 耗时
     */
    private Long executeTime;
    /**
     * 地区
     */
    private String location;
    /**
     * 创建人
     */
    private String createBy;
    /**
     * 创建时间
     */
    private Date startTime;
    /**
     * 更新时间
     */
    private Date endTime;
    /**
     * 异常信息
     */
    private String exception;
}

【步骤三】:定义事件类

com.zbbmeta.event包下创建事件类SysLogEvent

/**
 * 定义系统日志事件
 */
public class SysLogEvent extends ApplicationEvent {
    public SysLogEvent(OptLogDTO optLogDTO) {
        super(optLogDTO);
    }
}

【步骤四】:定义事件监听器

com.zbbmeta.listener包下创建监听器类SysLogListener

在监听器中可以将日志输出到数据库

/**
 * 异步监听日志事件
 */
@Component
public class SysLogListener {
    @Async//异步处理
    @EventListener(SysLogEvent.class)
    public void saveSysLog(SysLogEvent event) {
        OptLogDTO sysLog = (OptLogDTO) event.getSource();
        long id = Thread.currentThread().getId();
        //TODO 可以输出日志到数据库
        System.out.println("监听到日志操作事件:" + sysLog + " 线程id:" + id);
        //将日志信息保存到数据库...
    }
}

【步骤五】:定义切面

定义切入点表达式、配置切面(绑定切入点与通知关系),用于记录每次发送请求时方法名,参数,时间等信息

com.zbbmeta.aspect包下创建LogAspect

@Slf4j
@Aspect
@Component
public class LogAspect {
    @Autowired
    private final ApplicationContext applicationContext;
    public LogAspect(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
    @Pointcut("execution(* *..*Controller.*(..))")
    public void pointcut() {
    }
    /**
     * 环绕通知,使用Pointcut()上注册的切入点
     * @param point
     * @return
     */
    @Around("pointcut()")
    public Object recordLog(ProceedingJoinPoint point) throws Throwable {
        Object result = new Object();
        // 获取request
        HttpServletRequest request = RequestHolder.getHttpServletRequest();
        // 判断为空则直接跳过执行
        if (ObjectUtils.isEmpty(request)){
            return point.proceed();
        }
        // 获取注解里的value值
        Method targetMethod = resolveMethod(point);
        // 打印执行时间
        Date now = DateUtil.date();
        // 请求方法
        String method = request.getMethod();
        String url = request.getRequestURI();
        // 获取IP和地区
        String ip = RequestHolder.getHttpServletRequestIpAddress();
        String region = IPUtil.getCityInfo(ip);
        //获取请求参数
        // 参数
        Object[] args = point.getArgs();
        String requestParam = getArgs(args, request);
        Date end = null;
        // 计算耗时
        long tookTime = 0L;
        try {
            result = point.proceed();
        } finally {
            end = DateUtil.date();
            tookTime = DateUtil.between(now, end, DateUnit.SECOND);
        }
        // 如果是登录请求,则不获取用户信息
        String userName = "springboot葵花宝典";
        // 封装optLogDTO
        OptLogDTO optLogDTO = new OptLogDTO();
        optLogDTO.setIp(ip)
                .setCreateBy(userName)
                .setMethod(method)
                .setUrl(url)
                .setStartTime(now)
                .setEndTime(end)
                .setType("1")
                .setOperation(String.valueOf(result))
                .setLocation(StrUtil.isEmpty(region) ? "本地" : region)
                .setExecuteTime(tookTime)
                .setParams(JSON.toJSONString(requestParam));
        ApplicationEvent event = new SysLogEvent(optLogDTO);
        //发布事件
        applicationContext.publishEvent(event);
        long id = Thread.currentThread().getId();
        System.out.println("发布事件,线程id:" + id);
        return result;
    }
    /**
     * 配置异常通知
     *
     * @param point join point for advice
     * @param e exception
     */
    @AfterThrowing(pointcut = "pointcut()", throwing = "e")
    public void logAfterThrowing(JoinPoint point, Throwable e) {
        // 打印执行时间
        long startTime = System.nanoTime();
        Date now = DateUtil.date();
        OptLogDTO optLogDTO = new OptLogDTO();
        // 获取IP和地区
        String ip = RequestHolder.getHttpServletRequestIpAddress();
        String region = IPUtil.getCityInfo(ip);
        // 获取request
        HttpServletRequest request = RequestHolder.getHttpServletRequest();
        // 请求方法
        String method = request.getMethod();
        String url = request.getRequestURI();
        // 获取注解里的value值
        Method targetMethod = resolveMethod((ProceedingJoinPoint) point);
        optLogDTO.setExecuteTime( DateUtil.between(now, DateUtil.date(), DateUnit.SECOND))
                .setIp(ip)
                .setLocation(region)
                .setMethod(method)
                .setUrl(url)
                .setType("2")
                .setException(getStackTrace(e));
        // 发布事件
        log.info("Error Result: {}", optLogDTO);
        ApplicationEvent event = new SysLogEvent(optLogDTO);
        //发布事件
        applicationContext.publishEvent(event);
        long id = Thread.currentThread().getId();
        System.out.println("发布事件,线程id:" + id);
    }
    private Method resolveMethod(ProceedingJoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Class<?> targetClass = point.getTarget().getClass();
        Method method = getDeclaredMethod(targetClass, signature.getName(),
                signature.getMethod().getParameterTypes());
        if (method == null) {
            throw new IllegalStateException("无法解析目标方法: " + signature.getMethod().getName());
        }
        return method;
    }
    /**
     * 获取堆栈信息
     */
    public static String getStackTrace(Throwable throwable) {
        StringWriter sw = new StringWriter();
        try (PrintWriter pw = new PrintWriter(sw)) {
            throwable.printStackTrace(pw);
            return sw.toString();
        }
    }
    private Method getDeclaredMethod(Class<?> clazz, String name, Class<?>... parameterTypes) {
        try {
            return clazz.getDeclaredMethod(name, parameterTypes);
        } catch (NoSuchMethodException e) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null) {
                return getDeclaredMethod(superClass, name, parameterTypes);
            }
        }
        return null;
    }
    /**
     * 获取请求参数
     * @param args
     * @param request
     * @return
     */
    private String getArgs(Object[] args, HttpServletRequest request) {
        String strArgs = StrUtil.EMPTY;
        try {
            if (!request.getContentType().contains("multipart/form-data")) {
                strArgs = JSONObject.toJSONString(args);
            }
        } catch (Exception e) {
            try {
                strArgs = Arrays.toString(args);
            } catch (Exception ex) {
                log.warn("解析参数异常", ex);
            }
        }
        return strArgs;
    }
}

相关阅读

  • 网站静态模板怎么做 web静态网页模板

    网站静态模板怎么做 web静态网页模板

    全面的为大家介绍网站静态模板怎么做和web静态网页模板的相关知识,接下来IT袋小编就来介绍。 相信大家应该都知道在整个网站的制作过程当中,网页制作是整个网站建设的重中之重。创建

  • 什么是CDN网络 CDN网络有哪些优点

    什么是CDN网络 CDN网络有哪些优点

    IT袋网网为大家说一说什么是CDN网络的教程内容,下面为详细的介绍。 随着互联网的不断发展,网络服务的速度和质量成为了互联网公司的重要竞争力之一。为了提升用户的访问速度和用户体

  • 网站网页的制作方法 网页设计与网站建设教程

    网站网页的制作方法 网页设计与网站建设教程

    IT袋网为你介绍网站网页的制作方法和网页设计与网站建设教程方面的介绍,如有不对的地方欢迎指正! 随着各种网页制作工具的普及,现在不懂技术的个人也能顺利建站了。不过使用网页制

  • Ceph版本发行生命周期 Ceph版本的发行与生命周期

    Ceph版本发行生命周期 Ceph版本的发行与生命周期

    为关注IT袋网网的网友们详解Ceph版本发行生命周期的介绍,一起来了解了解吧。 Ceph从Nautilus版本(14.2.0)开始,每年都会有一个新的稳定版发行,预计是每年的3月份发布,每年的新版本都会起