Springboot接口项目如何使用AOP记录日志

一、 背景

一直想给项目构建一个统一的日志收集系统,先迈出第一步,构建一个日志收集类,用AOP实现无侵入日志收集

二、 环境

1.此随笔内容基于spring boot项目

2.数据库为mysql 5.7.9版本

3.jdk 版本为1.8

三、 说明

此版采用数据库存储,之后考虑使用elasticsearch等工具存储

四、 内容

1、构建日志采集实体类:BaseLogMessage

public class BaseLogMessage {
  private String serverIP;
  private String appName;
  private String method;
  private String type;
  private String userCode;
  private String uri;
  private String operationName;
  private String operationStatus;
  private long startTime;
  private Object parameter;
  private Object result;
  private int SpendTime;
 // 此处省略get、set
}

2、构建一个配置文件读取类,用于读取配置文件中的系统名称:SystemPropetiesUtil

@Configuration
public class SystemPropetiesUtil {
  @Value("${spring.application.name}")
  private String sysName;//系统名称<br>  // 此处省略get、set<br>}

3、新建一个AOP类,在控制器方法上作为切点,执行日志收集: LogAspect

@Aspect
@Component
public class LogAspect {
  @Autowired
  private SystemPropetiesUtil systemPropetiesUtil;

  //定义切点方法
  @Pointcut("execution(public * cq..campus.prevented.controller.*.*(..))")
  public void controllerLog() {
  }

  public static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);

  @Around("controllerLog()")
  public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
    long startTime = System.currentTimeMillis();
    //获取当前请求对象
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();
    //记录请求信息
    BaseLogMessage baseLogMessage = new BaseLogMessage();
    //1.获取到所有的参数值的数组
    Object[] args = joinPoint.getArgs();
    Signature signature = joinPoint.getSignature();
    MethodSignature methodSignature = (MethodSignature) signature;
    //2.获取到方法的所有参数名称的字符串数组
    String[] parameterNames = methodSignature.getParameterNames();
    Object result = joinPoint.proceed();
    Method method = methodSignature.getMethod();
    if (method.isAnnotationPresent(ApiOperation.class)) {
      ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
      baseLogMessage.setOperationName(apiOperation.value());
    }
    long endTime = System.currentTimeMillis();
    String urlStr = request.getRequestURL().toString();
    baseLogMessage.setUri(urlStr);
    baseLogMessage.setType("操作日志");
    baseLogMessage.setServerIP(getRemoteIP(request));
    baseLogMessage.setMethod(request.getMethod());
    baseLogMessage.setAppName(systemPropetiesUtil.getSysName());
    baseLogMessage.setResult(result);
    baseLogMessage.setParameter(getParameter(method, joinPoint.getArgs()));
    baseLogMessage.setSpendTime((int) (endTime - startTime));
    baseLogMessage.setStartTime(endTime);
    LOGGER.info("{}", JsonUtils.objectToJson(baseLogMessage));
    // 数据库存储操作
    return result;
  }

  /**
   * 异常返回通知,用于拦截异常日志信息 连接点抛出异常后执行
   *
   * @param joinPoint 切入点
   * @param e     异常信息
   */
  @AfterThrowing(pointcut = "controllerLog()", throwing = "e")
  public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
    long startTime = System.currentTimeMillis();
    if(null==kafkaClient){
      kafkaClient = KafkaProducerClient.getInstance(systemPropetiesUtil.getKafkaHost());
     //  redisClient= RedisClient.getInstance(systemPropetiesUtil.getReidsHost(), Integer.parseInt(systemPropetiesUtil.getRedisPort()), "");
    }
    // 获取RequestAttributes
    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
    // 从获取RequestAttributes中获取HttpServletRequest的信息
    HttpServletRequest request = (HttpServletRequest) requestAttributes
        .resolveReference(RequestAttributes.REFERENCE_REQUEST);
    String urlStr = request.getRequestURL().toString();
    BaseLogMessage baseLogMessage = new BaseLogMessage();
    Signature signature = joinPoint.getSignature();
    MethodSignature methodSignature = (MethodSignature) signature;
    Method method = methodSignature.getMethod();
    StringBuffer strbuff = new StringBuffer();
    for (StackTraceElement stet : elements) {
      strbuff.append(stet + "\n");
    }
    String message = exceptionName + ":" + exceptionMessage + strbuff.toString();
    try {
      Object[] args = joinPoint.getArgs();
      String[] parameterNames = methodSignature.getParameterNames();
      long endTime = System.currentTimeMillis();
      baseLogMessage.setUri(urlStr);
      baseLogMessage.setType("异常日志");
      baseLogMessage.setServerIP(getRemoteIP(request));
      baseLogMessage.setMethod(request.getMethod());
      baseLogMessage.setAppName(systemPropetiesUtil.getSysName());
      baseLogMessage.setResult(message);
      baseLogMessage.setParameter(getParameter(method, joinPoint.getArgs()));
      baseLogMessage.setSpendTime((int) (endTime - startTime));
      baseLogMessage.setStartTime(endTime);
      LOGGER.info("{}", JsonUtils.objectToJson(baseLogMessage));
     // 数据库存储操作
    } catch (Exception e2) {
      e2.printStackTrace();
    }

  }

  /**
   * 根据方法和传入的参数获取请求参数
   */
  private Object getParameter(Method method, Object[] args) {
    List<Object> argList = new ArrayList<>();
    Parameter[] parameters = method.getParameters();
    for (int i = 0; i < parameters.length; i++) {

      RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
      if (requestBody != null) {
        argList.add(args[i]);
      }
      RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
      if (requestParam != null) {
        Map<String, Object> map = new HashMap<>();
        String key = parameters[i].getName();
        if (!StringUtils.isEmpty(requestParam.value())) {
          key = requestParam.value();
        }
        map.put(key, args[i]);
        argList.add(map);
      }
    }
    if (argList.size() == 0) {
      return null;
    } else if (argList.size() == 1) {
      return argList.get(0);
    } else {
      return argList;
    }
  }
 /**
   * 获取请求ip
   */
  public static String getRemoteIP(HttpServletRequest request) {
    String ip =null;
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
      ip = request.getHeader("X-Forwarded-For");
    }
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
      ip = request.getHeader("Proxy-Client-IP");
    }
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
      ip = request.getHeader("WL-Proxy-Client-IP");
    }
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
      ip = request.getRemoteAddr();
    }
    if (ip != null) {
      //对于通过多个代理的情况,最后IP为客户端真实IP,多个IP按照','分割
      int position = ip.indexOf(",");
      if (position > 0) {
        ip = ip.substring(0, position);
      }
    }
    return ip;
  }
}  

五、 问题

1、如果方法正常执行,不进入AOP类,请检查AOP的切点是否书写正确。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Springboot使用@Valid 和AOP做参数校验及日志输出问题

    项目背景 最近在项目上对接前端的的时候遇到了几个问题 1.经常要问前端要请求参数 2.要根据请求参数写大量if...else,代码散步在 Controller 中,影响代码质量 3.为了解决问题1,到处记日志,导致到处改代码 解决方案 为了解决这类问题,我使用了@Valid 做参数校验,并使用AOP记录前端请求日志 1.Bean实体类增加注解 对要校验的实体类增加注解,如果实体类中有List结构,就在List上加@Valid @Valid注解 注解 备注 @Null 只能为null @NotNu

  • SpringBoot中使用AOP打印接口日志的方法

    前言 AOP 是 Aspect Oriented Program (面向切面)的编程的缩写.他是和面向对象编程相对的一个概念.在面向对象的编程中,我们倾向于采用封装.继承.多态等概念,将一个个的功能在对象中来实现.但是,我们在实际情况中也发现,会有另外一种需求就是一类功能在很多对象的很多方法中都有需要.例如有一些对数据库访问的方法有事务管理的需求,有很多方法中要求打印日志.按照面向对象的方式,那么这些相同的功能要在很多地方来实现或者在很多地方来调用.这就非常繁琐并且和这些和业务不相关的需求耦合太

  • SpringBoot AOP处理请求日志打印功能代码实例

    设计原则和思路: 元注解方式结合AOP,灵活记录操作日志 能够记录详细错误日志为运营以及审计提供支持 日志记录尽可能减少性能影响 操作描述参数支持动态获取,其他参数自动记录. 代码实例如下 @Slf4j @Aspect @Configuration public class RequestAopConfig { @Autowired private HttpServletRequest request; private static final ThreadLocal<Long> START_

  • springboot如何使用AOP做访问请求日志

    这篇文章主要介绍了springboot如何使用AOP做访问请求日志,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 springboot中使用AOP做访问请求日志:这次引入springboot的aop和日志 1.pom.xml引入: <!--springBoot的aop,已经集成了spring aop和AspectJ--> <dependency> <groupId>org.springframework.boot<

  • springboot配置aop切面日志打印过程解析

    这篇文章主要介绍了springboot配置aop切面日志打印过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.SpringBoot Aop说明 1. Aop AOP(Aspect-Oriented Programming,面向切面编程),它利用一种"横切"的技术,将那些多个类的共同行为封装到一个可重用的模块.便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性. 2. AOP相关概念: Aspect

  • 详解基于SpringBoot使用AOP技术实现操作日志管理

    操作日志对于程序员或管理员而言,可以快速定位到系统中相关的操作,而对于操作日志的管理的实现不能对正常业务实现进行影响,否则即不满足单一原则,也会导致后续代码维护困难,因此我们考虑使用AOP切面技术来实现对日志管理的实现. 文章大致内容: 1.基本概念 2.基本应用 3.日志管理实战 对这几部分理解了,会对AOP的应用应该很轻松. 一.基本概念 项目 描述 Aspect(切面) 跨越多个类的关注点的模块化,切面是通知和切点的结合.通知和切点共同定义了切面的全部内容--它是什么,在何时和何处完成其功

  • 使用SpringBoot+AOP实现可插拔式日志的示例代码

    一.AOP AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型. 二.实现 引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot

  • Springboot接口项目如何使用AOP记录日志

    一. 背景 一直想给项目构建一个统一的日志收集系统,先迈出第一步,构建一个日志收集类,用AOP实现无侵入日志收集 二. 环境 1.此随笔内容基于spring boot项目 2.数据库为mysql 5.7.9版本 3.jdk 版本为1.8 三. 说明 此版采用数据库存储,之后考虑使用elasticsearch等工具存储 四. 内容 1.构建日志采集实体类:BaseLogMessage public class BaseLogMessage { private String serverIP; pr

  • SpringBoot开发技巧之使用AOP记录日志示例解析

    目录 为什么要用AOP? 常用的工作场景 必须知道的概念 AOP 的相关术语 Spring 中使用注解创建切面 实战应用-利用AOP记录日志 定义日志信息封装 统一日志处理切面 为什么要用AOP? 答案是解耦! Aspect Oriented Programming 面向切面编程.解耦是程序员编码开发过程中一直追求的.AOP也是为了解耦所诞生. 具体思想是:定义一个切面,在切面的纵向定义处理方法,处理完成之后,回到横向业务流. AOP 主要是利用代理模式的技术来实现的.具体的代理实现可以参考这篇

  • SpringBoot项目中使用AOP的方法

    本文介绍了SpringBoot项目中使用AOP的方法,分享给大家,具体如下: 1.概述 将通用的逻辑用AOP技术实现可以极大的简化程序的编写,例如验签.鉴权等.Spring的声明式事务也是通过AOP技术实现的. 具体的代码参照 示例项目 https://github.com/qihaiyan/springcamp/tree/master/spring-aop Spring的AOP技术主要有4个核心概念: Pointcut: 切点,用于定义哪个方法会被拦截,例如 execution(* cn.sp

  • springboot 启动项目打印接口列表的实现

    目录 springboot 启动项目打印接口列表 环境 修改配置文件 Springboot项目添加接口入参统一打印 新建注解,用于实现参数打印功能的增强 自定义序列化规则 写参数打印增强,这里选择环绕增强 springboot 启动项目打印接口列表 环境 springboot 2.3.2.RELEASE 修改配置文件 logging: level: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandle

  • springboot vue项目后端列表接口分页模糊查询

    目录 基于 springboot+vue 的测试平台开发 一.分页插件 二.实现接口 1. 编写 Service 层 2. 编写 Controller 层 三.测试接口 1. 测试分页 2. 测试条件查询 基于 springboot+vue 的测试平台开发 继续更新 打开项目管理,就需要看到列表里展示项目数据,比如这样(截图是这个前端框架的demo,仅作示意): 那么对应到我们平台的项目管理功能,就需要有: 列表展示添加的项目数据 可以通过项目名称查询指定的项目 新增项目 编辑项目 其他功能..

  • 基于springboot实现一个简单的aop实例

    简介 AOP(Aspect-Oriented Programming:面向切面编程) aop能将一些繁琐.重复.无关业务的逻辑封装起来,在一个地方进行统一处理,常用于日志记录.事务管理.权限控制等,aop能在不改变原有代码逻辑的基础上对某个方法.某类方法.或者整个类进行无侵入式的加强,有效降低了代码耦合度,并且提高了项目扩展性: ok废话说完,进入正题,如何实现一个aop 要实现aop,首先你要知道你拿aop来干啥,我们今天就以记录日志来说,因为这个最常用,一般对于重要的数据库操作,我们需要记录

  • SpringBoot实现项目健康检查与监控

    Spring Boot 最主要的特性就是AutoConfig(自动配置),而对于我们这些使用者来说也就是各种starter, Spring Boot-Actuator 也提供了starter,为我们自动配置,在使用上我们只需要添加starter到我们的依赖中,然后启动项目即可. <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-ac

  • 解决Vue调用springboot接口403跨域问题

    最近在做一个前后端分离的项目, 前端用的是Vue后端使用的是springboot, 在项目整合的时候发现前端调用后端接口报错403跨域请求问题 前端跨域请求已解决, 那么问题就出在后端了, 找了一些资料找到了很多种方法, 这里说两个简单粗暴的. 注意:"@CrossOrigin"注解要求jdk1.8及以上版本, SpringMVC 4.2及以上版本 1. 在controller层上添加@Configuration注解, 如果没有效果请制定RequestMapping总的method类型

  • Springboot创建项目的图文教程(idea版本)

    原文地址:http://www.javayihao.top/detail/84 一:概述 由于springboot项目,不管是java工程还是web工程都可以直接以jar方式运行,所以推荐创建jar工程,这里创建jar工程项目为例. 二:两种方式创建springboot项目 1.第一种方式 手动在idea中new一个新的项目.选择maven工程 完成的结构如图 然后在pom文件继承spring-boot-starter-parent依赖接口完成创建 <?xml version="1.0&q

  • springboot~nexus项目打包要注意的地方示例代码详解

    一个使用maven制作框架包时,会有一个主项目,然后它有多个子项目框架组成,很少一个工具包一个工程,像springboot,springcloud都是这种结构,主项目用来管理一些依赖包的版本,这对于框架型项目来说是很必要的,而对于业务项目来说,因为目前都是推荐使用微服务的轻量方式,所以不建议用多项目绑定一个大项目的方式,而都是一个服务一个项目. 主pom文件 主项目的pom文件用来管理依赖包版本,一般在dependencyManagement节点去声明它们的版本号,这样在子项目里可以不声明相同包

随机推荐