Spring Boot 使用自定义注解和自定义线程池实现异步日志记录

news2025/1/20 12:12:18

在这里插入图片描述

😄 19年之后由于某些原因断更了三年,23年重新扬帆起航,推出更多优质博文,希望大家多多支持~
🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志
🎐 个人CSND主页——Micro麦可乐的博客
🐥《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程,入门到实战
🌺《RabbitMQ》本专栏主要介绍使用JAVA开发RabbitMQ的系列教程,从基础知识到项目实战
🌸《设计模式》专栏以实际的生活场景为案例进行讲解,让大家对设计模式有一个更清晰的理解
💕《Jenkins实战》专栏主要介绍Jenkins+Docker+Git+Maven的实战教程,让你快速掌握项目CI/CD,是2024年最新的实战教程
如果文章能够给大家带来一定的帮助!欢迎关注、评论互动~

Spring Boot 使用自定义注解和自定义线程池实现异步日志记录

  • 1、前言
  • 2、异步日志记录的重要性
    • 2.1 提高性能
    • 2.2 减少延迟
    • 2.3 提升稳定性
    • 2.4 资源管理优化
  • 3、开始构建
    • 3.1 创建 Spring Boot 项目
    • 3.2 创建自定义注解类
    • 3.3 配置自定义线程池
    • 3.4 编写日志记录切面
    • 3.5 编写Controller中使用@LogAsync
  • 4、接口测试
  • 5、结语

1、前言

在我们日常开发工作中,日志记录是至关重要的部分。它不仅有助于调试和故障排除,还能提供系统运行的历史记录,帮助进行性能优化和安全监控。然而,日志记录也可能对系统性能产生影响,特别是在高并发环境下。因此,使用异步日志记录技术可以有效地提升系统性能和可靠性。

博主将带着大家一起探讨 Spring Boot 异步日志记录的优雅实现方法。

2、异步日志记录的重要性

2.1 提高性能

在传统的同步日志记录方式中,每次日志记录操作都需要等待日志写入完成,才能继续执行后续操作。这种方式在高并发环境下会导致明显的性能瓶颈。异步日志记录通过将日志写入操作放入独立的线程中执行,避免了主线程的阻塞,从而大幅提高了系统的整体性能。

2.2 减少延迟

在需要快速响应的应用程序中,如实时系统或高频交易系统,任何形式的延迟都会影响系统的响应时间。异步日志记录能将日志记录操作从主业务流程中剥离出来,减少了响应时间,提升了用户体验。

2.3 提升稳定性

在高负载情况下,同步日志记录可能会导致系统资源的争用,影响系统的稳定性。异步日志记录通过使用独立的线程池管理日志写入操作,避免了这种资源争用,提高了系统的稳定性和可靠性。

2.4 资源管理优化

异步日志记录允许更灵活地管理系统资源。通过配置线程池的大小和任务队列,可以更好地控制系统资源的使用,避免了因为日志记录过多导致的内存和磁盘 I/O 资源耗尽问题。

3、开始构建

3.1 创建 Spring Boot 项目

可以使用 Spring Initializr 创建项目,确保在 pom.xml 文件中添加了必要的依赖:

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring Boot Starter AOP -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
</dependencies>

3.2 创建自定义注解类

定义一个自定义注解类 LogAsync 用于标记需要异步记录的日志方法,并且系统会记录功能组、操作人、方法名、传递的参数等(仅模拟,大家根据自己项目需求定制)
代码如下:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAsync {
	// 可以根据需要添加属性 比如功能组
	String funGroup() default "";
}

3.3 配置自定义线程池

为了优化异步日志记录,我们需要配置一个自定义线程池

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;

@Configuration
@EnableAsync
public class AsyncConfig{

    @Bean(name = "logExecutor")
    public Executor logExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("LogExecutor-");
        executor.initialize();
        return executor;
    }
}

3.4 编写日志记录切面

创建一个AOP切面 LoggingAspect 来处理异步日志记录

import lombok.extern.slf4j.Slf4j;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.concurrent.Executor;


@Aspect
@Component
@Slf4j
public class LoggingAspect {

    private final Executor logExecutor;

    public LoggingAspect(@Qualifier("logExecutor") Executor logExecutor) {
        this.logExecutor = logExecutor;
    }

    @Pointcut("@annotation(LogAsync)")
    public void loggableMethods() {
    }

    @AfterReturning(pointcut = "loggableMethods()", returning = "result")
    @Async("logExecutor")
    public void logMethodCall(JoinPoint joinPoint, Object result) {
        //获取注解类
        LogAsync logAsync = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(LogAsync.class);
        //获取注解上的功能组
        String funGroup = logAsync.funGroup();
        //获取方法名
        String methodName = joinPoint.getSignature().getName();
        //获取参数
        Object[] args = joinPoint.getArgs();
        //方法返回结果  Object result
        log.info("funGroup: {}, Method: {}, Args: {}, Result: {}", funGroup, methodName, args, result);
        
        //TODO 这里可以加入日志入库操作
    }
}

3.5 编写Controller中使用@LogAsync

创建一个TestUser类,作为接收参数

import lombok.Data;

@Data
public class TestUser {
    private String username;
    private String password;
}

创建 LogSampleController ,标注@LogAsync注解

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
public class LogSampleController {

    /**
     * 模拟保存
     * @param user 自定义用户类
     * @return
     */
    @LogAsync(funGroup = "用户模块")
    @PostMapping("/save-user")
    public ResponseEntity<String> saveUser(@RequestBody  TestUser user) {
        return new ResponseEntity<>("操作成功", HttpStatus.OK);
    }
}

4、接口测试

运行 Spring Boot 应用程序,使用测试工具访问 http:/your_host/api/save-user。观察控制台中可以看到异步日志记录的信息
在这里插入图片描述
控制台输出:
在这里插入图片描述

温馨提示
演示代码中,博主是获取 功能组、方法名、传递的参数、返回结果;
正常我们日志记录还会有很多主要信息,比如:操作人、修改前数据、修改后数据、修改时间等等,大家可以根据自己系统情况进行调整

5、结语

通过自定义注解Spring AOP自定义线程池,我们可以在 Spring Boot 应用中实现高效的异步日志记录。这种方法不仅提高了日志记录的灵活性,还能减小对主业务线程的影响。希望本文对您在实际项目中实现日志记录有所帮助。

这种方式在实际生产环境中非常实用,特别是在需要高效处理大量日志记录的场景下。通过合理配置线程池,可以确保日志记录的性能和稳定性。


在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Diffusers代码学习: IP-Adapter Inpainting

IP-Adapter还可以通过Inpainting自动管道和蒙图方式生成目标图片。 # 以下代码为程序运行进行设置&#xff0c;使用Inpainting 的自动管道&#xff0c; import os os.environ["HF_ENDPOINT"] "https://hf-mirror.com"from diffusers import AutoPipelin…

0基础学习区块链技术——链之间数据同步样例

大纲 创建区块新增链区块链直接替换 我们可以在https://blockchaindemo.io/体验这个过程。 创建区块 默认第一个链叫Satoshi(中本聪)。链上第一个区块叫“创世区块”——Genesis Block。后面我们会看到创建的第二条链第一个区块也是如此。 新增链 新创建的链叫Debby。默认…

android中调用onnxruntime框架

创建空白项目 安装Android Studio及创建空白项目参考&#xff1a;【安卓Java原生开发学习记录】一、安卓开发环境的搭建与HelloWorld&#xff08;详细图文解释&#xff09;_安卓原生开发-CSDN博客 切记&#xff1a;build configuration language 一定选择Groovy&#xff01;官…

向量化:机器学习中的效率加速器与数据桥梁

在机器学习领域的广袤天地中&#xff0c;向量化技术以其独特的魅力&#xff0c;为数据处理和模型训练注入了强大的动力。本文将深入探讨向量化在机器学习领域中的体现&#xff0c;剖析其如何助力模型实现高效的数据处理和精确的结果预测&#xff0c;并通过丰富的案例和详尽的数…

【人工智能Ⅱ】实验8:生成对抗网络

实验8&#xff1a;生成对抗网络 一&#xff1a;实验目的 1&#xff1a;理解生成对抗网络的基本原理。 2&#xff1a;学会构建改进的生成对抗网络&#xff0c;如DCGAN、WGAN、WGAN-GP等。 3&#xff1a;学习在更为真实的数据集上应用生成对抗网络的方法。 二&#xff1a;实验…

Vue2学习(04)

目录 一、组件的三大组成部分 二、组件的样式冲突scoped 三、scoped原理 ​编辑 四、data是一个函数 五、组件通信 六、props详解 七、非父子通信 1.eventbus事件总线(可以一传多)--->作用是在非父子组件之间&#xff0c;进行简易的消息传递&#xff08;复杂场景---&…

深拷贝、浅拷贝、引用拷贝

深拷贝和浅拷贝的区别 1. 引用拷贝2. 对象拷贝 1. 引用拷贝 两个对象指向同一个地址值。 创建一个指向对象的引用变量的拷贝Teacher teacher new Teacher("Taylor",26); Teacher otherteacher teacher; System.out.println(teacher); System.out.println(otherte…

【面试干货】如何选择MySQL数据库存储引擎(MyISAM 或 InnoDB)

【面试干货】如何选择MySQL数据库存储引擎(MyISAM 或 InnoDB&#xff09; &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; MySQL数据库存储引擎是一个 关键 的考虑因素。MySQL提供了多种存储引擎&#xff0c;其中最常用的是 MyISAM 和 InnoD…

[word] word表格如何设置外框线和内框线 #媒体#笔记

word表格如何设置外框线和内框线 点击表格的左上角按钮从而选中表格 点击边框按钮边上的下拉箭头&#xff0c;选择边框和底纹 点击颜色边上的下拉箭头&#xff0c;选择红色 点击取消掉中间的边框&#xff0c;只保留外围边框 点击颜色边上的下拉箭头&#xff0c;选择另外一个颜…

2013.8.5-2024.5.10碳排放权交易明细数据

2013.8.5-2024.5.10碳排放权交易明细数据 1、时间&#xff1a;2013.8.5-2024.5.10 2、来源&#xff1a;各碳排放交易所 3、范围&#xff1a;各交易所城市 4、指标&#xff1a;行政区划代码、地区、所属省份、交易日期、交易品种、开盘价_元、最高价_元、最低价_元、成交均价…

【全部更新完毕】2024全国大学生数据统计与分析竞赛A题思路代码文章教学数学建模-抖音用户评论的文本情感分析

文章摘要部分&#xff1a; A 题&#xff1a; 抖音用户评论的文本情感分析 摘要 随着短视频平台的迅猛发展&#xff0c;抖音已成为全球最受欢迎的短视频分享平台之一。然而&#xff0c;随着用户数量和使用时长的增加&#xff0c;抖音团队需要不断优化平台功能、提升用户体验&…

生成纳秒级别的时间戳,高性能

问题 同步influxdb有些数据没有&#xff0c;不知道啥原因&#xff0c;后来百度发现时间需要唯一&#xff0c;毫秒还会重复&#xff0c;只能采用纳秒处理了 java实现 TimeStampUtils.java package com.wujialiang;/*** 获取纳秒值的工具类*/ public class TimeStampUtils {/…

面试题-Vue2和Vue3的区别

文章目录 1. 响应式系统2. 组合式 API (Composition API)3. Fragment (碎片)4. Teleport (传送门) 5. 性能改进6. 移除或改变的功能7. 构建工具8. TypeScript 支持 Vue 2 和 Vue 3 之间存在许多重要的区别&#xff0c;这些区别涵盖了性能、API 设计、组合式 API&#xff08;Com…

产品NPDP+项目PMP助你成长

前言 从管理的角度来讲,产品经理和项目经理的区别,我们应该吧项目经理和产品的区别分为一纵一横,那一纵就是我们的项目经理,项目经理在整个新产品研发过程中他扮演的是管理监督项目参与者的角色,其中包括研发部门、技术部门、市场部门或是销售部门等等。他所要做的事情就…

【Unity】Kafka、Mqtt、Wesocket通信

1 前言 最近研究了下kafka、mqtt、webocket插件在Unity网络通信中的应用&#xff0c;做下小总结吧。&#xff08;不想写笔记&#xff0c;但不写又会忘&#xff0c;痛苦&#xff09; 2 Kafka 先说结果&#xff1a;Kafka实现失败。 我会使用的方法是在VS里安装了Confluent.Kafka…

压缩大文件消耗电脑CPU资源达到33%以上

今天用7-Zip压缩一个大文件&#xff0c;文件大小是9G多&#xff0c;这时能听到电脑风扇声音&#xff0c;查看了一下电脑资源使用情况&#xff0c;确实增加了不少。 下面是两张图片&#xff0c;图片上有电脑资源使用数据。

05--Git分布式版本控制系统

前言&#xff1a;给后端工程师使用的版本控制器&#xff0c;本质上类似带时间标记的ftp&#xff0c;使用比较简单&#xff0c;就在这里归纳出来&#xff0c;供参考学习。 git1、概念简介 分布式版本控制系统&#xff08;Distributed Version Control System&#xff0c;DVCS&…

数据挖掘--分类

数据挖掘--引论 数据挖掘--认识数据 数据挖掘--数据预处理 数据挖掘--数据仓库与联机分析处理 数据挖掘--挖掘频繁模式、关联和相关性&#xff1a;基本概念和方法 数据挖掘--分类 数据挖掘--聚类分析&#xff1a;基本概念和方法 基本概念 决策树归纳 决策树:决策树是一…

从0开始学人工智能测试节选:Spark -- 结构化数据领域中测试人员的万金油技术(四)

上一章节我们了解了 shuffle 相关的概念和原理后其实可以发现一个问题&#xff0c;那就是 shuffle 比较容易造成数据倾斜的情况。 例如上一节我们看到的图&#xff0c;在这批数据中&#xff0c;hello 这个单词的行占据了绝大部分&#xff0c;当我们执行 groupByKey 的时候触发了…

关于怎么用Cubemx生成的USBHID设备实现读取一体的鼠标键盘设备(改进版)

主要最近做了一个要用STM32实现读取鼠标键盘一体的那种USB设备&#xff0c;STM32的界面上要和电脑一样的能通过这个USB接口实现鼠标移动&#xff0c;键盘的按键。然后我就很自然的去参考了正点原子的例程&#xff0c;可是找了一圈&#xff0c;发现正点原子好像用的库函数&#…