SpringBoot 使用自定义注解(基于AOP) 实现操作日志管理

Springboot整合AOP和注解实现日志记录——Java入职第十二天 本文讲解什么是AOP,介绍AOP的常用注解,并且利用Springboot整合AOP实现日志记录,同时分析AOP记录日志的不足,加入自定义注解,更加细化精确的记录日志 阅读详情

操作日志的记录

因为我们不光要记录代码的运行,如(logback log4j),而且还应该记录用户的行为(Controller 请求日志),这叫做业务运行日志。

业务运行日志的作用

  1. 记录用户的行为 用于后续的分析
  2. 记录用户的所有的操作

日志实现的思路

1.我们需要记录哪些数据(谁在什么时间干了什么事情,结果如何)? 数据要存入到哪里(存储到数据库表,推送到指定日志存储服务,打印到日志文件等)?

2.在项目中什么位置记录(一般在Controller请求,进行around 环绕增强)

如何实现记录功能

实现方式:注解+AOP

Aop日志记录 具体代码实现

aop的使用流程,这里使用注解式aop来实现
具体步骤:

  1. 设置切入点
    1. 可以切在方法上
    2. 可以切在注解上
      @Transactional 事务注解 注解加在类上 aop 切在注解上
      
  2. 写增强 日志记录增强
    1. 获取日志的相关信息用户的id ip地址, 时间, 操作的描述, 类型等信息
    2. 将日志对象 添加到指定服务(文件,数据库,系统)

             

但是,操作的描述如何获取呢?

     使用自定义注解:

  1. 在 目标 方法上添加自定义注解 (@Logger) 如下

  2. 在增强中获取注解(@Logger)的属性获取

具体实现

依赖引入

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>eu.bitwalker</groupId>
        <artifactId>UserAgentUtils</artifactId>
        <version>1.21</version>
    </dependency>

 

自定义注解

package com.wang.chao.micro.log.annotation;

import com.wang.chao.micro.log.enums.LogSeverity;
import java.lang.annotation.*;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Logger {

    /**
     * 日志来源 操作模块
     *
     * @return
     */
    String operSource() default "";

    /**
     * 日志级别
     *
     * @return
     */
    LogSeverity severity() default LogSeverity.WARNING;

    /**
     * 日志操作名称
     *
     * @return
     */
    String operName() default "";

    /**
     * 是否 持久化(persistence)
     *
     * @return
     */
    boolean isPersistence() default false;
}

使用AOP统一处理日志

package com.wang.chao.micro.log.annotation.aspect;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wang.chao.micro.id.genertor.IdWorker;
import com.wang.chao.micro.log.annotation.Logger;
import com.wang.chao.micro.log.bean.ExceptionOperLog;
import com.wang.chao.micro.log.bean.OperationLog;
import com.wang.chao.micro.log.service.LoggerSupport;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
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 java.lang.reflect.Method;

@Component
@Aspect
@Slf4j
public class LoggerAspect {
  private static final  ObjectMapper mapper = new ObjectMapper();

    @Pointcut("@annotation(com.wang.chao.micro.log.annotation.Logger)")
    public void logger() {
    }

    @Around("logger()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        Object ret = pjp.proceed();
        OperationLog operLog = buildOperationLog(pjp, ret);
       // log.info(writeValueAsString(operLog));
        LoggerSupport.addAuditLog(operLog);
        return ret;

    }

    @AfterThrowing(value = "logger()", throwing = "throwable")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable throwable) {
        // 保存异常日志记录
        OperationLog operLog = buildExceptionOperationLog(joinPoint, throwable);
        LoggerSupport.addAuditLog(operLog);
    }

    private OperationLog buildExceptionOperationLog(JoinPoint joinPoint, Throwable t) {
        ExceptionOperLog operLog = new ExceptionOperLog(buildOperationLog(joinPoint, null));
        operLog.setExceptionName(t.getClass().getName()); // 异常名称
        operLog.setExceptionMessage(stackTraceToString(t.getClass().getName(), t.getMessage(), t.getStackTrace())); // 异常信息
        return operLog;
    }

    private OperationLog buildOperationLog(JoinPoint joinPoint, Object ret) {
        OperationLog operLog = new OperationLog();
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        operLog.setTranceId(IdWorker.nextId());
        operLog.setStartTime(System.currentTimeMillis());
        operLog.setUrl(request.getRequestURL().toString());
        operLog.setMethod(request.getMethod());

        // 从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取切入点所在的方法
        Method method = signature.getMethod();
        // 获取操作日志注解信息
        Logger loggerAnnotation = method.getAnnotation(Logger.class);
        if (loggerAnnotation != null) {
            operLog.setSource(loggerAnnotation.operSource());
            operLog.setSeverity(loggerAnnotation.severity());
            operLog.setOperName(loggerAnnotation.operName());
            operLog.setPersistence(loggerAnnotation.isPersistence());
        }
        operLog.setRequestMethod(signature.getDeclaringTypeName() + "." + signature.getName());
        operLog.setRemoteAddr(request.getRemoteAddr());
        operLog.setArgs(writeValueAsString(request.getParameterMap()));
        //获取请求头中的User-Agent
        UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
        operLog.setUserAgent(userAgent);
        // pjp.proceed():当我们执行完切面代码之后,还有继续处理业务相关的代码。proceed()方法会继续执行业务代码,并且其返回值,就是业务处理完成之后的返回值。
        operLog.setEndTime(System.currentTimeMillis());
        operLog.setResult(ret);
        return operLog;
    }

    private String writeValueAsString(Object object){
        String json = null;
        try {
            json = mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
           log.error("object covert json exception,",e);
        }
        return json;
    }
    /**
     * 转换异常信息为字符串
     *
     * @param exceptionName    异常名称
     * @param exceptionMessage 异常信息
     * @param elements         堆栈信息
     */
    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuffer strbuff = new StringBuffer();
        for (StackTraceElement stet : elements) {
            strbuff.append(stet + "\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
        return message;
    }
}

另外日志处理采用了阻塞队列进行数据缓冲处理,日志对象具体消费使用多线程实现。

具体日志服务代码如下:

https://gitee.com/mystarry-sky/microservice-component/tree/master/log-genertor

https://gitee.com/mystarry-sky/microservice-component/

 

 

hive sql每个用户的累积访问次数 我们有如下的用户访问数据 userId visitDate visitCount u01 2017/1/21 5 u02 2017/1/23 6 u03 2017/1/22 8 u04 2017/1/20 3 u01 2017/1/23 6 u01 2017/2/21 8 U02 2017/1/23 6 U01 2017/2/22 4 要求使用SQL... 阅读详情

相关推荐

C语言的文件读取与写入操作

学了一年的C语言了,现在回过头来做一下总结。并且博客开了挺长一段时间却没有谢什么实际的东西。现在做下总结,对之后的学习帮助应该挺大的。与大家共勉! 现在,我先来介绍一下C语言的文件读取与写入的原理和具体操作方法。 C语言文件读取与写入是通过将文件看成一个字符序列进行读入和写出的。所以读取与写入文件就用到了指针,而这个指针是一个特殊的指针,我们称为文件指针。 指针名称是

wzyxdwll的博客 3万+

java 登录用户ip地址

/** * 获取用户真实IP地址,不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址, * <p> * 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值,究竟哪个才是真正的用户端的真实IP呢? * 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。 * <p> * 如:X-Forward...

小心仔的博客 742

别再死记硬背菜单了!用Workbench搞定你的第一个ANSYS结构分析(附模型文件)

本文为零基础用户提供使用ANSYS Workbench完成第一个结构分析的完整指南。通过悬臂梁案例详细讲解从几何导入、材料定义、网格划分到边界条件设置的全流程,帮助初学者快速掌握有限元分析的基本操作,并附赠实用模型文件。

weixin_34233974的博客 307

Spring Boot 通过AOP+自定义注解实现日志管理

Spring Boot 通过AOP+自定义注解实现日志管理

lixuanhong的博客 1183

从“Segmentation fault”到流畅运行:深入解析CARLA Engine crash与Signal 11报错排查指南

本文深入解析了CARLA仿真平台运行时遭遇的Engine crash与Signal 11(Segmentation fault)报错。文章指出,该错误的根本原因常与NVIDIA显卡驱动版本不兼容有关,并提供了从日志分析、常见方案试错到驱动安全降级的完整排查指南,帮助用户从崩溃走向流畅运行。

pca5navigator的博客 462

常用本体建模工具

常用本体建模工具: Apollo、OntoStudio、TopBraid Composer、Semantic Turkey、Knoodl、Chimaera、OliEd、WebODE、Kmgen和DOME Protégé Protégé[1]是一款由斯坦福大学编写并维护的开源本体建模和编辑工具,其支持Web版本和PC版本,使用OWL语言(关于OWL的详细介绍可参看前文2.1节内容)对知识进行表示,读者可以通过Protégé的官方网站访问Web版本或下载桌面版本。Protégé最早开发于1987年,最初目的是

qq_40503347的博客 6162

使用AOP记录SpringBoot项目编辑前后字段的具体改变

文章地址: https://www.jianshu.com/p/fd411f4020fc

万万没想到的博客 8222

工业机器人关节拆装流程_一文读懂模块化直驱电机及机器人关节模组

私信“干货”二字,即可领取138G伺服与机器人专属及电控资料!如果说芯片是智能行业最为重要的核心元件,那么电机几乎可以说是支撑了整个自动化和大工业生产以及现代社会最为重要的部件,全世界90%的能源是由电机发出来的,而70%是由电机消耗调的,电机可以说是整个人类社会能源循环的占有绝对地位的产品。普通伺服电机伺服电机其实已经算是电动机中比较高端的类型,特别是永磁同步类型的伺服电机,由于其启动快速,惯量...

weixin_32159771的博客 2153

springboot aop 自定义注解方式实现完善日志记录(完整源码)

一:功能简介 本文主要记录如何使用aop切面的方式来实现日志记录功能。 主要记录的信息有: 操作人,方法名,参数,运行时间,操作类型(增删改查),详细描述,返回值。 二:项目结构图 三:代码实现 1.配置文件 这里只有两个配置: 1)server.port=11000,设置项目启动的端口号,防止被其他服务占用; 2)spring.aop.auto=true,开启spring的aop配置,简单明了,不需要多配置其他的配置或注解。 application.yml文件 server:

m0_59853401的博客 1522

IMX335摄像头原理图设计,已量产

从工程组织上看,IMX335 模组和 IMX415 模组属于同一类产品:核心矛盾不是外设多,而是高速图像链路、电源域和调试接口要同时兼顾。ATK-MCIMX335 这张图的结构也很直接,三路 LDO、本地 24MHz 可选时钟、4-lane MIPI 数据对和一组主板侧标准接口,构成了一张非常典型的量产摄像头子板。

不脱发的程序猿 436

Laser-Marking 激光振镜打标机的开源代码 - 下载 - 搜珍网

为了方便开发者的使用和定制化需求,我们在这里提供激光振镜打标机的开源代码,并附带详细的描述。首先,让我们来了解一下激光振镜打标机的基本原理。振镜系统由两个可移动的反射镜组成,可以控制激光束在水平和垂直方向上的偏转角度,从而实现对工件的精确控制。通过这段开源代码,开发者可以根据自己的需求对激光振镜打标机进行二次开发和定制化。你可以修改振镜角度、打标速度等参数,也可以添加其他功能,例如自动识别和追踪等。首先,我们创建了一个串口对象,用于与激光振镜打标机进行通信。函数实现了对用户输入文本的打标操作。

TechProX的博客 1278

什么是AOP?如何使用AOP写一个方便的自定义日志功能?

用AOP的方式,以自定义注解,注释到方法上实现日志记录。

左小涩° 1781

YOLOV12进阶:MSDA多尺度空洞注意力机制解析与实战,突破检测尺度瓶颈

在目标检测中,“尺度差异”是个老大难问题——大目标占据画面大半,小目标可能只有几个像素,普通模型很难同时精准检测。而MSDA(Multi-Scale Dilated Attention,多尺度空洞注意力)就像给模型配备了“变焦眼镜”,能同时关注不同尺度的目标,让YOLOV12在大、中、小目标检测上都能表现出色。这篇教程会带你从原理到实操,掌握这个强大的注意力机制,让你的检测模型真正“通吃”各种尺度的目标。MSDA的“多尺度空洞注意力”设计,是目标检测突破尺度瓶颈的关键一步。

know_F0727的博客 263

Springboot自定义注解实现操作日志管理

面试前一定少不了刷题,为了方便大家复习,我分享一波个人整理的面试大全宝典Java核心知识整理Java核心知识Spring全家桶(实战系列)其他电子书资料Step3:刷题既然是要面试,那么就少不了刷题,实际上春节回家后,哪儿也去不了,我自己是刷了不少面试题的,所以在面试过程中才能够做到心中有数,基本上会清楚面试过程中会问到哪些知识点,高频题又有哪些,所以刷题是面试前期准备过程中非常重要的一点。“,”));面试前一定少不了刷题,为了方便大家复习,我分享一波个人整理的面试大全宝典Java核心知识整理。

2401_85954546的博客 1405

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

一张思维导图带你学会SpringBoot使用AOP实现日志管理功能

基本概念:在不改变原有功能的逻辑,增加新的功能。应用场景权限控制日志处理事务控制下面以对产品数据增删改查功能,进行日志管理功能为例,对AOP中的核心概念作出介绍,请参考下表概念解析对应日志管理功能核心关注点业务逻辑的主要功能,应用程序主要关注的部分产品数据的增删改查横切关注点与核心关注点相关但不属于核心关注点的功能,在系统的多个模块或组件中散布记录产品操作的日志通知在特定切入点执行时要执行的代码,实现横切关注点的具体逻辑。可以在目标方法执行之前、之后或抛出异常时执行。

DaenCode的博客 1177

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

SpringBoot使用@Aspect进行日志管理@Log实现

文章目录前言一、AOP是什么?二、注解介绍二、使用步骤1.导入jar2.数据库表3.实体类4.核心注解类5.工具类6.Service类6.Controller类总结 前言 本文章主要是SpringBoot使用@Aspect进行日志管理 @Log实现日志切入 一、AOP是什么? AOP(Aspect Oriented Programming,⾯向切⾯编程)是通过预编译⽅式和运⾏期动态代理实现程序功能的统⼀维护的⼀种技术。AOP是OOP的延续,是软件开发中的⼀个热点,也是Spring框架中的⼀个重要内容,是

qq_40912838的博客 2112

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Springboot AOP 自定义注解实现系统日志

一、添加AOP依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> 二、AOP切点类 @Slf4j @Comp...

Liyiming 1349

SpringBoot使用Aop自定义注解展示日志信息

我还为大家准备了一套体系化的架构师学习资料包以及BAT面试资料,供大家参考及学习已经将知识体系整理好(源码,笔记,PPT,学习视频)找小编(vip1024c)领取7:mybatisPlus代码生成器pomweb依赖mysql依赖mysqlruntimemybatis-plus依赖3.3.1.tmp我还为大家准备了一套体系化的架构师学习资料包以及BAT面试资料,供大家参考及学习已经将知识体系整理好(源码,笔记,PPT,学习视频)

2401_85354273的博客 594

【SpringBoot应用篇】【AOP+注解】SpringBoot使用Aspect AOP注解实现日志管理

SpringBoot使用Aspect AOP注解实现日志管理(二)项目结构pomcommonpomannotationenumsresultservicesqlpomapplication.ymlentitymapper启动类aspectcontroller测试查看 项目结构 使用maven聚合工程进行项目开发,实际工作中经常以这种形式开发。 pom <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.a

输入技术、输出想法 308

SpringBoot整合aop日志管理并写入数据库

面向切面编程AOPSpringBoot整合aop日志管理1.前期准备1.1 配置1.2 涉及知识点2.aop(Aspect Oriented Programming)2.1 aop的五种通知2.2 使用注解2.3 实现aop日志功能自定义注解类切面处理类将注解加到需要记录的方法体上获取用户ip结语获取用户ip注意的点:1024!!快乐快乐 SpringBoot整合aop日志管理 1.前期准备 1.1 配置 导入依赖 <dependency> <groupId>or

mf_sprite的博客 1562

java 自定义注解+AOP实现日志记录

ssm版本: 1、首先自定义一个注解,该注解有两个属性,一个是模块名,一个是操作的内容。该注解是用来修饰Service层中的方法的。 2、创建一个切面类,该切面使用@Aspect和@Component注解修饰,该页面需要注入一个HttpSession对象。 注意:一个普通的java类是不能注入HttpSession对象的,因此需要在web.xml文件中配置 org.springframework.web.util.IntrospectorCleanupListener org.springfram

qq_43842093的博客 2153

SpringBoot 实现异步记录复杂日志

点击上方“芋道源码”,选择“设为星标”管她前浪,还是后浪?能浪的浪,才是好浪!每天 10:33更新文章,每天掉亿点点头发...源码精品专栏原创 | Java 2021超神之路,很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络应用框架 Netty 源码解析消息中间件 RocketMQ 源码解析数据库中间件 Sharding-JDBC 和 MyCAT 源码解析作业调度中间件 E...

芋艿V 394

使用Springboot的AOP日志拦截获取前端网站的操作记录

随着我们的不断学习,我们的技术不断沉淀,做出来的项目也不断成熟所以,我们的网站怎么能没有日志记录呢annotation包下面的Log,是自定义注解,字段的解释下面的注释中很清晰哦/*** @author 徐一杰* Aop日志拦截自定义注解。

泡泡的博客 1573

【项目demo04】AOP日志管理

我准备开一个系列,就是写一些在简要的学习项目中可能会用到的奇奇怪怪的功能,比如线程池或者统一异常处理类 取名为【项目demo】系列 然后将会同步到GitHub中:https://github.com/Livorth/FunctionalLearning AOP日志管理 AOP是个好东西,用的地方很多,但是这里仅介绍作为日志管理的使用 总的来说,使用AOP的思路是差不多的 使用自定义注解 使用AOP环绕通知代理有对应注解的方法,然后在环绕的时候进行对应操作 在需要使用日志方法上加上注解,进行测试 在这里

Alivorth的博客 315

Spring boot实现AOP记录操作日志

前言 在实际的项目中,特别是管理系统中,对于那些重要的操作我们通常都会记录操作日志。比如对数据库的CRUD操作,我们都会对每一次重要的操作进行记录,通常的做法是向数据库指定的日志表中插入一条记录。这里就产生了一个问题,难道要我们每次在 CRUD的时候都手动的插入日志记录吗?这肯定是不合适的,这样的操作无疑是加大了开发量,而且不易维护,所以实际项目中总是利用AOP(Aspect Oriented P...

zhuguang10的博客 6751

《SpringBoot学习篇》(5)AOP+自定义注解实现日志管理

用到的AOP注解:@Aspect  @Pointcut  @After 首先看一下如何调用自定义注解: @MyLog(module="老师模块", method="查询全部") @RequestMapping("/all") public List&lt;Teacher&gt; all(){ List&lt;Teacher&gt; ts = teacherSrv.getAll(); ...

冲冲冲 1582
上一篇: 雪花算法 IdWorker Snowflake JAVA实现方案 - 单机版
下一篇: SpringBoot中Inteceptor 之 请求处理器拦截器HandlerInterceptor
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

仰望星空@脚踏实地

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值