张三进阶之路 | 基于Spring AOP的Log收集

在这里插入图片描述

请在此添加图片描述

前情提要 📌

张三对于公司的日志处理系统不满意,认为其性能不佳且功能有限。为了展示自己的能力和技术实力,他决定利用Spring AOP(面向切面编程)开发一个更高效的日志处理系统,并将其存储在Redis中。

首先,张三分析了现有日志处理系统的不足之处,如性能瓶颈、日志格式不统一、存储容量有限等。然后,他开始着手设计和实现一个新的日志处理系统。

📟 使用Spring AOP进行日志拦截:张三利用Spring AOP的切面功能,为需要记录日志的方法添加了一个切面。在这个切面中,他可以捕获方法的调用信息,如方法名、参数、返回值等,并将这些信息作为日志内容。

📟 日志格式化:为了确保日志的一致性和可读性,张三设计了一种统一的日志格式。他将日志分为不同的级别,如DEBUG、INFO、WARN和ERROR,并为每个级别设置了不同的颜色和标签。

📟 Redis存储:张三选择将日志存储在Redis中,因为Redis是一个高性能的键值存储系统,适合存储大量的日志数据。他为每个日志级别创建了一个Redis列表,用于存储相应级别的日志。同时,他还设置了一个定时任务,定期清理过期的日志数据,以保持存储空间的整洁。

📟 监控与告警:为了方便监控日志系统的运行状况,张三还开发了一个简单的监控界面,可以实时查看各个日志级别的数量、存储空间使用情况等信息。此外,他还设置了一些告警规则,当某个日志级别的数量超过阈值时,会自动发送告警通知给相关人员。

经过一段时间的努力,张三成功地完成了这个基于Spring AOP的日志处理系统,并将其部署到了生产环境。公司同事对他的工作表示赞赏,认为这个新的日志处理系统不仅提高了性能,还提供了更多有用的功能。这无疑突显了张三的技术能力和对公司的贡献。

以下是一个简化的代码实现示例,展示了如何使用Spring AOP和Redis来实现日志处理系统。

场景实现 📌

💵 创建一个日志切面类**LoggingAspect**

在此过程中,我们创建了一个日志切面类LoggingAspect,它会拦截指定包路径下的所有方法调用。在方法调用完成后,它会将方法的调用信息(如方法名、参数、返回值等)作为日志内容,并将这些信息传递给LogService进行处理。

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

    @Autowired
    private LogService logService;

    @Pointcut("execution(* com.example.service.*.*(..))")
    public void logPointcut() {}

    @AfterReturning(pointcut = "logPointcut()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        logService.log(joinPoint, result);
    }
}

💵 创建一个日志服务类**LogService****:**

LogService负责将日志内容存储到Redis中。在这个示例中,我们使用了RedisTemplate来操作Redis。我们将日志内容存储在名为log的Redis列表中。

import org.aspectj.lang.JoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class LogService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public void log(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        String logMessage = String.format("Method: %s, Args: %s, Result: %s", methodName, Arrays.toString(args), result);

        // 将日志存储到Redis
        redisTemplate.opsForList().rightPush("log", logMessage);
    }
}

还可以为不同级别的日志创建不同的Redis列表:

public class LogService {

    // ...

    public void log(JoinPoint joinPoint, Object result, LogLevel logLevel) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        String logMessage = String.format("Method: %s, Args: %s, Result: %s", methodName, Arrays.toString(args), result);

        // 根据日志级别将日志存储到不同的Redis列表中
        String redisKey = "log:" + logLevel.name().toLowerCase();
        redisTemplate.opsForList().rightPush(redisKey, logMessage);
    }
}

也可以修改日志格式化:

public class LogService {

    // ...

    public void log(JoinPoint joinPoint, Object result, LogLevel logLevel) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        String logMessage = String.format("[%s] Method: %s, Args: %s, Result: %s", logLevel, methodName, Arrays.toString(args), result);

        // 根据日志级别将日志存储到不同的Redis列表中
        String redisKey = "log:" + logLevel.name().toLowerCase();
        redisTemplate.opsForList().rightPush(redisKey, logMessage);
    }
}

💵 配置RedisTemplate:

最后,我们配置了一个RedisTemplate Bean,用于序列化和反序列化Redis的key和value值。这样,我们就可以将日志内容以结构化的方式存储在Redis中,并在需要时方便地进行查询和分析。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        template.setValueSerializer(genericJackson2JsonRedisSerializer);
        template.setHashValueSerializer(genericJackson2JsonRedisSerializer);

        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);

        template.afterPropertiesSet();
        return template;
    }
}

💵 定时任务:

我们创建了一个定时任务LogCleanupTask,它会定期清理过期的日志数据。我们使用了Spring的@Scheduled注解来实现定时任务,并使用RedisTemplate来操作Redis。在cleanupLogs方法中,我们遍历所有的日志列表,并根据日志的时间戳判断它们是否过期。如果过期,则将其从Redis中移除。

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class LogCleanupTask {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Scheduled(cron = "0 0 * * * ?") // 每小时执行一次
    public void cleanupLogs() {
        // 清理过期的日志数据,例如保留最近7天的日志
        String redisKeyPattern = "log:*";
        Set<String> keys = redisTemplate.keys(redisKeyPattern);
        for (String key : keys) {
            List<Object> logs = redisTemplate.opsForList().range(key, 0, -1);
            List<Object> logsToRemove = logs.stream()
                    .filter(log -> isExpired(log))
                    .collect(Collectors.toList());
            redisTemplate.opsForList().remove(key, 0, logsToRemove);
        }
    }

    private boolean isExpired(Object log) {
        // 判断日志是否过期,例如根据日志的时间戳和当前时间进行比较
        // ...
    }
}

💵 监控界面:

我们创建了一个监控界面LogMonitorController,它可以实时查看日志数据。我们使用了Spring的@RestController注解来创建一个RESTful API,并使用RedisTemplate来操作Redis。在getLogs方法中,我们遍历所有的日志列表,并将它们以JSON格式返回给客户端。客户端可以使用这些数据来实时监控日志系统的运行状况。

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LogMonitorController {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @GetMapping("/monitor/logs")
    public Map<String, Object> getLogs() {
        Map<String, Object> logs = new HashMap<>();
        String redisKeyPattern = "log:*";
        Set<String> keys = redisTemplate.keys(redisKeyPattern);
        for (String key : keys) {
            List<Object> logList = redisTemplate.opsForList().range(key, 0, -1);
            logs.put(key, logList);
        }
        return logs;
    }
}

❗❗❗ 请注意!!! 这个示例仅用于演示如何使用Spring AOP和Redis实现日志处理系统。在实际项目中,需要根据具体需求进行更多的定制和优化。例如,可以为不同级别的日志创建不同的Redis列表,以便更好地管理和查询日志数据。此外,还可以考虑使用更高级的日志框架,如Logback或Log4j2,以实现更丰富的日志功能和更好的性能。

Get知识点 📌

📣 AOP概念:AOP(面向切面编程)是一种编程范式,它允许开发者在不修改原有代码的情况下,对程序的某些方面进行增强。AOP通过将横切关注点(如日志记录、事务管理、权限控制等)与业务逻辑分离,使得代码更加模块化和可维护。

📣 Spring AOP:Spring AOP是Spring框架中的一个重要组件,它提供了声明式的AOP支持。Spring AOP使用代理模式来实现AOP,可以通过JDK动态代理或CGLIB代理来创建代理对象。Spring AOP支持多种类型的切面,如前置通知、后置通知、异常通知、环绕通知等。

📣 @Aspect — 此注释将类定义为一个方面,即关注点的模块化。该方面包含建议和要点。
📣 @Joinpoint — 连接点是程序执行中可以应用方面的一个点。在 Spring AOP 中,连接点是方法调用。
📣 @Advice — 建议是某个方面在特定连接点上采取的行动。有几种类型的建议,例如“之前”、“之后”、“周围”等。
📣 @Pointcut — 切点是一组应应用方面的连接点。它定义了一个模式,该模式与方面应截获的方法相匹配。可以使用表达式或注释来定义切点。

下面是 Spring AOP 注解的示例:

@Aspect
@Component
public class LoggingAspect {

    @Before("execution(public * com.example.myapp.service.*.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before " + joinPoint.getSignature().getName() + " method");
    }

    @After("execution(public * com.example.myapp.service.*.*(..))")
    public void logAfter(JoinPoint joinPoint) {
        System.out.println("After " + joinPoint.getSignature().getName() + " method");
    }

    @Around("execution(public * com.example.myapp.service.*.*(..))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Before " + joinPoint.getSignature().getName() + " method");
        Object result = joinPoint.proceed();
        System.out.println("After " + joinPoint.getSignature().getName() + " method");
        return result;
    }

    @Pointcut("execution(public * com.example.myapp.service.*.*(..))")
    public void serviceMethods() {}
}

写在最后 📌

日志使用在现代软件开发中非常重要,它可以帮助开发者和系统管理员监控程序运行状态、排查问题和调试代码。但是,日志使用也存在一些缺点,如干扰员工工作、信息整理工作量大、主观色彩和日志格式不统一等。因此,在使用日志时,需要权衡其优缺点,选择合适的日志记录方法,并确保日志数据的准确性和完整性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/900144.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

初识算法 · 二分查找(4)

目录 前言&#xff1a; 寻找峰值 题目解析 算法原理 算法编写 寻找旋转排序数组中的最小值 题目解析 算法原理 算法编写 寻找缺失的数字 题目解析 算法原理 算法编写 前言&#xff1a; ​本文的主题是二分查找&#xff0c;通过三道题目讲解&#xff0c;一道是寻找…

超越OpenAI GPT-4o,Yi-Lightning指南:中国AI大模型新巅峰

Yi-Lightning 是零一万物公司最新发布的旗舰模型&#xff0c;它在国际权威盲测榜单 LMSYS 上超越了硅谷知名 OpenAI GPT-4o-2024-05-13、Anthropic Claude 3.5 Sonnet&#xff0c;排名世界第六&#xff0c;中国第一&#xff0c;这标志着中国大模型首次实现超越 OpenAI GPT-4o 的…

InnoDB 存储引擎的底层逻辑架构白话-必知必会

目录标题 前言白话内存架构1. 自适应哈希索引自适应哈希索引的作用自适应哈希索引的工作原理自适应哈希索引与缓存的区别启用和禁用自适应哈希索引 2. Buffer pool3. Change buffer 下面我们就来详细分析一下&#xff0c;数据修改操作的步骤。4. Log Buffer 磁盘架构1. 系统表空…

一文了解AOSP是什么?

一文了解AOSP是什么&#xff1f; AOSP基本信息 基本定义 AOSP是Android Open Source Project的缩写&#xff0c;这是一个由Google维护的完全免费和开放的操作系统开发项目。它是Android系统的核心基础&#xff0c;提供了构建移动操作系统所需的基本组件。 主要特点 完全开源…

echarts散点图

一、类似散点图折线图不展示折线 option {grid: {left: 10,right: 20,top: 35,bottom: 15,containLabel: true},tooltip: {show: true,trigger: item,backgroundColor: "rgba(0,0,0,0)", // 提示框浮层的背景颜色。formatter: function (params) {var html <d…

基于Python的B站视频数据分析与可视化

基于Python的B站视频数据分析与可视化 爬取视频、UP主信息、视频评论 功能列表 关键词搜索指定帖子ID爬取指定UP主的主页爬取支持评论爬取生成评论词云图支持数据存在数据库支持可视化 部分效果演示 关键词搜索爬取 指定UP主的主页爬取 指定为黑马的了 爬取视频的时爬取评论…

基于大模型的Milvus向量数据库的背景与实战应用,计算与索引机制,Python代码实现

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下基于大模型的Milvus向量数据库的背景与实战应用&#xff0c;计算与索引机制&#xff0c;Python代码实现。本文详细介绍了milvus向量数据库的原理&#xff0c;并通过具体的数据样例和完整的Python代码实现&#xff0…

NodeJS连接MySQL 8.4报错:code: ‘ER_TABLEACCESS_DENIED_ERROR‘

NodeJS连接MySQL 8.4报错&#xff1a;code: ER_TABLEACCESS_DENIED_ERROR { code: ER_TABLEACCESS_DENIED_ERROR, errno: 1142, sqlMessage: "SELECT command denied to user 用户名localhost for table 表名", sqlState: 42000, index: 0, sql: SELECT …

SCR相对标准偏差、氨氮比、截面速度,多平面计算

SCR截面速度、氨氮比等标准及相对标准偏差计算。 程序用来处理fluent通过xyplot导出的数据&#xff0c;导出可以选择多个平面&#xff0c;可计算标准偏差SD、相对标准偏差RSD&#xff0c;平均速度,适用于求解多个平面 # -*- coding: utf-8 -*- """ Created on …

maven本地打jar包依赖

本地工程的pom文件中引入了mysql依赖&#xff0c;但是在maven库中没有拉下来&#xff0c;可以到mysql官网下载jar包&#xff0c;使用maven手动打包到本地仓库中&#xff1a; 官网地址&#xff1a;MySQL :: Download MySQL Connector/J (Archived Versions) 在jar包所在位置的路…

jupyter界面修改成中文教程

在系统变量里面增加一个变量名&#xff1a;LANG 变量值&#xff1a;zh_ CN.UTF8 成功修改成为中文

什么是JavaBean?

什么是JavaBean&#xff1f;—— Java开发中的数据封装利器 在Java开发中&#xff0c;JavaBean 是一个非常实用且常见的设计模式&#xff0c;它用于简洁、高效地封装和传递数据。随着Java应用的广泛使用&#xff0c;JavaBean成为许多开发者不可或缺的工具。在本文中&#xff0c…

【linux】centos7卸载默认的jdk

查看是否已经安装java java -version 查看java文件 rpm -qa | grep java 卸载相关包 rpm -e --nodeps java-1.8.0-openjdk-headless-1.8.0.262.b10-1.el7.x86_64rpm -e --nodeps python-javapackages-3.4.1-11.el7.noarchrpm -e --nodeps tzdata-java-2020a-1.el7.noarchrpm…

Labview通讯测试耗时

写法 写命令立即读出 写命令后立即读出&#xff0c;在同一时间不能有多个地方写入&#xff0c;因此需要在整个写入后读出过程加锁 项目中会存在多个循环并行执行该VI&#xff0c;轮询PLC指令 在锁内耗时&#xff0c;就是TCP读写的实际耗时为5-8ms&#xff0c;在主VI六个循环…

【面试经典150】day 7

目录 1.买卖股票的最佳时机 II 2.跳跃游戏 3.跳跃游戏 II 4.H 指数 5.O(1) 时间插入、删除和获取随机元素 6.除自身以外数组的乘积 7.加油站 8.分发糖果 1.买卖股票的最佳时机 II class Solution {public int maxProfit(int[] prices) {//和1相比&#xff0c;这个可以一直买…

【WebSocket实战】——创建项目初始架构

这一篇文章主要是为了介绍如何在visual中创建一个项目并服务于我们要做的websockt项目&#xff0c;所以这里如果已经懂得的人&#xff0c;可以直接跳过。 目录 1&#xff09;创建空白解决方案 2&#xff09;创建asp.NET Core项目 3&#xff09;创建winform项目作为客户端1 …

53页 PPT煤炭行业数字化转型规划方案

▲关注智慧方案文库&#xff0c;学习9000多份最新解决方案&#xff0c;其中 PPT、WORD超过7000多份 &#xff0c;覆盖智慧城市多数领域的深度知识社区&#xff0c;稳定更新4年&#xff0c;日积月累&#xff0c;更懂行业需求。 53页 PPT煤炭行业数字化转型规划方案 通过对煤企高…

乐维网管平台(一):如何精准掌控 IP 管理

业网络已成为支撑业务运转的关键基础设施&#xff0c;而在企业网络管理中&#xff0c;IP 管理至关重要&#xff0c;它就像是网络秩序的守护者&#xff0c;确保网络的高效运行、安全可靠。 一、为什么企业要进行 IP 管理 1. 优化资源分配 IP 地址作为网络中的重要资源&#xf…

驱动-----向内核新加文件

编译的过程是: 1.先复制一个默认的配置到.config(存放make menuconfig的配置结果)文件。 2.make menuconfig来可视化的选择编译的对象。 3.编译与否保存在.config里面 4.然后就makefile,使用.config中的配置 接下来就是加自己的驱动文件,把自己的文件编译加到内核里面…

探索 CSS Houdini:轻松构建酷炫的 3D 卡片翻转动画

在本文中&#xff0c;我将通过构建一个3D翻卡动画来探索Houdini的功能。这将帮助你了解Houdini的核心概念&#xff0c;并引导你完成实际的代码实现。你不仅能够掌握 Houdini 的核心概念&#xff0c;还可以跟随实际的代码实现&#xff0c;逐步完成这个动画效果。 我们将深入探讨…