基于AOP实现登录日志和操作日志(新手入门版)

基于AOP实现登录日志和操作日志

  • 目录结构
  • 代码
  • PostMan测试代码
  • 控制台查看输出
  • 解析成JSON
  • 如果你觉得对你有帮助的话,请点赞收藏

目录结构

在这里插入图片描述

代码


package com.demo.mymaintest.constants;

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

/**
 * 系统日志注解
 *
 * @author Mark sunlightcs@gmail.com
 */
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.METHOD,ElementType.PARAMETER })
public @interface SysLog {

	String value() default "";

}

package com.demo.mymaintest.aop;


import com.alibaba.fastjson.JSONObject;
import com.demo.mymaintest.constants.SysLog;
import com.demo.mymaintest.entity.SysLogEntity;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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 java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 系统日志,切面处理类
 *
 * @author yhj
 */
@Aspect
@Component
public class SysLogAspect {

  // 定义切点 切点表达式指向SysLog注解,我们再业务方法上可以加上SysLog注解,然后所标注
  // 的方法都能进行日志记录
  @Pointcut("@annotation(com.demo.mymaintest.constants.SysLog)")
  public void logPointCut() {}

  @Around(value = "logPointCut()")
  private Object Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    HttpServletRequest request  = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    String requestURI = request.getRequestURI();
    String requestMethod = request.getMethod();
    String remoteAddr = request.getRemoteAddr();
    SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Long nowDate = System.currentTimeMillis(); // 步入时间戳
    String startTime = dateformat.format(nowDate);

    MethodSignature method1 = (MethodSignature) proceedingJoinPoint.getSignature();
    Class<?> currentClass = proceedingJoinPoint.getTarget().getClass();
    Object proceed = proceedingJoinPoint.proceed();
    List<Object> allArgs = Arrays.asList(proceedingJoinPoint.getArgs());
    List<Object> args =
        allArgs.stream()
            .map(
                arg -> {
                  if (!(arg instanceof HttpServletRequest)
                      && !(arg instanceof HttpServletResponse)) {
                    return arg;
                  } else {
                    return null;
                  }
                })
            .filter(arg -> arg != null)
            .collect(Collectors.toList());

    SysLogEntity sysLogEntity = new SysLogEntity();
    sysLogEntity.setUsername("登录人从request中获取");
    MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
    		Method method = signature.getMethod();
    SysLog syslog = method.getAnnotation(SysLog.class);
    if(syslog != null){
            //注解上的描述
            sysLogEntity.setOperation(syslog.value());
        }
    sysLogEntity.setRequestPath(requestURI);
    sysLogEntity.setRequestMethod(requestMethod);
    sysLogEntity.setCurrentTimeMills(startTime);
    sysLogEntity.setArgsType(getMethodArgumentTypeName(method1));
    sysLogEntity.setAllArgs(args);
    sysLogEntity.setResponseData(proceed != null ? proceed.toString() : "null");
    sysLogEntity.setExecuteTimeMills((System.currentTimeMillis() - nowDate) + "ms");
    sysLogEntity.setClassMethodLocation(currentClass.getName() + "." + method.getName());
    sysLogEntity.setRemoteAddr(remoteAddr);
    sysLogEntity.setNowTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    sysLogEntity.setResponseType(method.getReturnType().getName());

    // TODO 将sysLogEntity存入ES

    System.out.println(JSONObject.toJSONString(sysLogEntity));
    return proceed;
  }

  @AfterThrowing(value = "logPointCut()")
  private void AfterThrowing() {
    System.out.println("异常通知");
  }

  private Map<String, String> getMethodArgumentTypeName(MethodSignature method) {
    Map<String, String> map = new HashMap<>();
    String[] argTypeNames = method.getParameterNames();
    Class[] parameterTypes = method.getParameterTypes();
    for (int i = 0; i < parameterTypes.length; i++) {
      map.put(parameterTypes[i].getName(), argTypeNames[i]);
    }
    return map;
  }
}
package com.demo.mymaintest.controller;


import com.demo.mymaintest.constants.SysLog;
import com.demo.mymaintest.entity.BookEntity;
import com.demo.mymaintest.entity.SysUser;
import com.demo.mymaintest.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.*;

/**
 * @ClassName TestApiController
 * @Author yhj
 * @Date 2023/7/15 13:22
 * @Description
 * @Version 1.0
 */

@RestController
@RequestMapping("/api")
@Slf4j
public class TestApiController {

    @PostMapping("/add")
    @SysLog(value = "添加数据测试")
    public R test(@RequestBody BookEntity bookEntity) {
        bookEntity.setBookImg("testImg");
        bookEntity.setBookIntro("书籍简介");
        return R.ok(200,"添加成功",bookEntity);
    }
    @PostMapping("/login")
    @SysLog(value = "用户登录")
    public ResponseEntity login(@RequestBody SysUser sysUser){
            String ok = "恭喜你登录成功";
            if(sysUser.getUserName().equals("test")){
                return ResponseEntity.ok(ok);
            }
            return ResponseEntity.ok().body("登录失败");
    }
    @DeleteMapping("/delete")
    @SysLog("删除")
    public ResponseEntity<String> deleteById(@PathVariable("id") Integer id){
        return ResponseEntity.ok("删除成功");
    }
}

package com.demo.mymaintest.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.io.Serializable;

/**
 *
 *
 * @author yhj
 * @date 2023-06-12 09:47:10
 */
@Data
public class BookEntity implements Serializable {
	private static final long serialVersionUID = 1L;
	/**
	 *
	 */
	@TableId
	private Integer bookId;
	/**
	 * 书籍编号
	 */
	private String bookIsbn;
	/**
	 * 书籍名称
	 */
	private String bookName;
	/**
	 * 书籍价格
	 */
	private Float bookPrice;

	/**
	 * 书籍封面
	 */
	private String bookImg;
	/**
	 * 简介
	 */
	private String bookIntro;
	/**
	 * 已购数量
	 */
	private int bookPurchasedNumber;
}

package com.demo.mymaintest.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.util.Date;

/**
 * <p>
 * 用户表
 * </p>
 *
 * @author jerry
 * @since 2021-06-16
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)

public class SysUser implements Serializable {

private static final long serialVersionUID=1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * 用户编号
     */
    //@ApiModelProperty("用户编号")
    private String userCode;

    /**
     * 用户名
     */
    //@ApiModelProperty("用户名")
    private String userName;

    /**
     * 登录密码
     */
    //@ApiModelProperty("登录密码")
    private String password;

    /**
     * 真实姓名
     */
    //@ApiModelProperty("真实姓名")
    private String realName;

    /**
     * 手机号码
     */
    //@ApiModelProperty("手机号码")
    private String mobile;

    /**
     * 邮箱地址
     */
    //@ApiModelProperty("邮箱地址")
    private String email;

    /**
     * 组织机构id
     */
    //@ApiModelProperty("组织机构id")
    private Long orgId;

    /**
     * 组织机构名称
     */
    //@ApiModelProperty("组织机构名称")
    private String orgName;

    /**
     * 用户类型
     */
    //@ApiModelProperty("用户类型")
    private Integer userType;

    /**
     * 描述
     */
    //@ApiModelProperty("描述")
    private String userDesc;

    /**
     * 联系地址
     */
    //@ApiModelProperty("联系地址")
    private String address;

    /**
     * 传真
     */
    //@ApiModelProperty("传真")
    private String fax;

    /**
     * 邮编
     */
    //@ApiModelProperty("邮编")
    private String postalcode;

    /**
     * 状态(0:禁用 1:正常)
     */
    //@ApiModelProperty("状态(0:禁用 1:正常)")
    private Integer status;

    /**
     * 数据权限类型(role user group)
     */
    //@ApiModelProperty("数据权限类型(role user group)")
    private String permissionType;

    /**
     * 创建时间
     */
    //@ApiModelProperty("创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime;

    /**
     * 创建人
     */
    //@ApiModelProperty("创建人")
    private String createBy;

    /**
     * 更新时间
     */
    //@ApiModelProperty("更新时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date updateTime;

    /**
     * 更新人
     */
    //@ApiModelProperty("更新人")
    private String updateBy;

    /**
     * 是否删除(0-否 1-是)
     */
    //@ApiModelProperty("是否删除(0-否 1-是)")
    private Integer isDeleted;

    /**
     * 公司地址
     */
    //@ApiModelProperty("公司地址")
    private String orgAddress;

    /**
     * 部门id
     */
    //@ApiModelProperty("部门id")
    private String jobId;

    /**
     * 部门名称
     */
    //@ApiModelProperty("部门名称")
    private String jobName;


    /**
     * 过期期限
     */
    //@ApiModelProperty("过期期限")
    @JsonFormat(pattern = "yyyyMM", timezone = "GMT+8")
    private Date expire;

    private String orgCode;

}

PostMan测试代码

在这里插入图片描述

控制台查看输出

在这里插入图片描述

解析成JSON

{
    "allArgs":[                           // 所有入参
        {
            "bookId":123,
            "bookImg":"testImg",
            "bookIntro":"书籍简介",
            "bookIsbn":"123213414",
            "bookName":"test",
            "bookPrice":12.77,
            "bookPurchasedNumber":0
        }
    ],
    "argsType":{                        // 入参实体类
        "com.demo.mymaintest.entity.BookEntity":"bookEntity"
    },
    "classMethodLocation":"com.demo.mymaintest.controller.TestApiController.test",     // 方法
    "currentTimeMills":"2023-07-21 16:30:49",     // 时间
    "executeTimeMills":"9ms",   // 运行时间
    "nowTime":"2023-07-21 16:30:49",  // 时间
    "operation":"添加数据测试",      //  controller上面的注释
    "remoteAddr":"0:0:0:0:0:0:0:1",  // 这个其实是127.0.0.1 没有进行封装
    "requestMethod":"POST",       
    "requestPath":"/api/add",     // 路径  
    "responseData":"{msg=添加成功, code=200, data=BookEntity(bookId=123, bookIsbn=123213414, bookName=test, bookPrice=12.77, bookImg=testImg, bookIntro=书籍简介, bookPurchasedNumber=0)}",
    "responseType":"com.demo.mymaintest.utils.R",    // 所有返参
    "username":"登录人从request中获取"     // 根据项目详情从token或者session中获取登录人
}

如果你觉得对你有帮助的话,请点赞收藏

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

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

相关文章

Emvirus: 基于 embedding 的神经网络来预测 human-virus PPIs【Biosafety and Health,2023】

研究背景&#xff1a; Human-virus PPIs 预测对于理解病毒感染机制、病毒防控等十分重要&#xff1b;大部分基于 machine-learning 预测 human-virus PPIs 的方法利用手动方法处理序列特征&#xff0c;包括统计学特征、系统发育图谱、理化性质等&#xff1b;本文作者提出了一个…

全志F1C200S嵌入式驱动开发(spi-nor image制作)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 一般soc系统里面添加spi-nor flash芯片,特别是对linux soc来说,都是把它当成文件系统来使用的。spi-nor flash和spi-nand flash相比,虽然空间小了点,但是胜在稳定,这是很多工业…

linux编译内核

新安装的ubuntu18&#xff0c;补齐依赖工具包。 sudo apt install vim sudo apt install net-tools sudo apt-get install libncurses5-dev libssl-dev build-essential openssl sudo apt-get install flex sudo apt-get install bison -y sudo apt-get install openssh-s…

数据结构【栈和队列】

第三章 栈与队列 一、栈 1.定义&#xff1a;只允许一端进行插入和删除的线性表&#xff0c;结构与手枪的弹夹差不多&#xff0c;可以作为实现递归函数&#xff08;调用和返回都是后进先出&#xff09;调用的一种数据结构&#xff1b; 栈顶&#xff1a;允许插入删除的那端&…

logback-spring.xml日志配置文件详解

目录 前言logback-spring.xml 配置 前言 打印日志是一个系统的基本功能&#xff0c;系统出现异常可以通过查找日志弄清楚是什么原因&#xff0c;从而更加快速地定位问题&#xff0c;修复系统。 logback-spring.xml 配置 文件位置 具体配置 <?xml version"1.0"…

代理模式(java)

目录 结构 静态代理案例 代码实现 售票类 火车站类 代理类 测试类 优缺点 优点 缺点 结构 代理&#xff08;Proxy&#xff09;模式分为三种角色&#xff1a; 抽象主题&#xff08;Subject&#xff09;类&#xff1a; 通过接口或抽象类声明真实主题和代理对象实现的业务…

Windows Server 2022 中文版、英文版下载 (updated Jul 2023)

Windows Server 2022 中文版、英文版下载 (updated Jul 2023) Windows Server 2022 正式版&#xff0c;2023 年 7 月更新 请访问原文链接&#xff1a;https://sysin.org/blog/windows-server-2022/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&a…

【HTML5】拖放详解及实现案例

文章目录 效果预览代码实现 效果预览 代码实现 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>一颗不甘坠落的流星</title><style>#div1,#div2 {float: left;width: 100px;height: 27px;margin: 10px;paddin…

性能测试Ⅱ(压力测试与负载测试详解)

协议 性能理论&#xff1a;并发编程 &#xff0c;系统调度&#xff0c;调度算法 监控 压力测试与负载测试的区别是什么&#xff1f; 负载测试 在被测系统上持续不断的增加压力&#xff0c;直到性能指标(响应时间等)超过预定指标或者某种资源(CPU&内存)使用已达到饱和状…

Baumer工业相机堡盟工业相机如何通过BGAPI SDK获取相机当前实时帧率(C++)

Baumer工业相机堡盟工业相机如何通过BGAPISDK里函数来计算相机的实时帧率&#xff08;C&#xff09; Baumer工业相机Baumer工业相机的帧率的技术背景Baumer工业相机的帧率获取方式CameraExplorer如何查看相机帧率信息在BGAPI SDK里通过函数获取相机帧率 Baumer工业相机通过BGAP…

成都爱尔蔡裕:泡在“糖”里的脆弱血管,暴露在眼睛深处

糖尿病是一组由多病因引起的以慢性高血糖为特征的终身性代谢性疾病。长期血糖增高&#xff0c;大血管、微血管受损并危及心、脑、肾、周围神经、眼睛、足等。医生临床数据显示&#xff0c;糖尿病发病后10年左右&#xff0c;将有30%&#xff5e;40%的患者至少会发生一种并发症&a…

Spring使用注解进行对象装配(DI)

通过五大类注解可以更便捷的将对象存储到 Spring 中&#xff0c;同样也可以使用注解将已经储存的对象取出来&#xff0c;直接赋值到注解所在类的一个属性中&#xff0c;这一个过程也叫做对象的装配或者叫对象的注入&#xff0c;即 DI。 一. 什么是对象装配 获取 Bean 对象也叫…

【算法基础:搜索与图论】3.6 二分图(染色法判定二分图匈牙利算法)

文章目录 二分图介绍染色法判定二分图例题&#xff1a;860. 染色法判定二分图 匈牙利匹配二分图最大匹配匈牙利匹配算法思想例题&#xff1a;861. 二分图的最大匹配 二分图介绍 https://oi-wiki.org/graph/bi-graph/ 二分图是图论中的一个概念&#xff0c;它的所有节点可以被…

如何模拟实现分布式文件存储

如何解决海量数据存不下的问题 传统做法是是在宕机存储。但随着数据变多&#xff0c;会遇到存储瓶颈 单机纵向扩展&#xff1a;内存不够加内存&#xff0c;磁盘不够家磁盘。有上限限制&#xff0c;不能无限制加下去 多机横向扩展&#xff1a;采用多台机器存储&#xff0c;一…

MYSQL练习一答案

练习1答案 构建数据库 数据库 数据表 answer开头表为对应题号答案形成的数据表 表结构 表数据 答案&#xff1a; 1、查询商品库存等于50的所有商品&#xff0c;显示商品编号&#xff0c;商 品名称&#xff0c;商品售价&#xff0c;商品库存。 SQL语句 select good_no,good…

贪心算法重点内容

贪心算法重点内容 4.1部分背包 按照单位重量的价值排序 4.2最小生成树 两种算法 4.3单源最短路径 4.4哈夫曼树

深入学习java虚拟机||JVM内存结构五大模型

目录 程序计数器 栈 虚拟机栈 垃圾回收是否涉及栈内存&#xff1f; 栈内存分配越大越好吗&#xff1f; 方法内的局部变量是否线程安全&#xff1f; 栈内存溢出 本地方法栈 堆 方法区 先看内存图总览 程序计数器 定义&#xff1a;全称P r o g r a m C o u n t e r R e …

Pytorch个人学习记录总结 06

目录 神经网络-卷积层 torch.nn.Conv2d 神经网络-最大池化的使用 torch.nn.MaxPool2d 神经网络-卷积层 torch.nn.Conv2d torch.nn.Conv2d的官方文档地址 CLASS torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride1, padding0, dilation1, groups1, biasTrue,…

探秘MySQL底层架构:设计与实现流 程一览

点赞还是要求一下的&#xff0c;万一屏幕前的大漂亮&#xff0c;还有大帅哥就点赞了呢&#xff01;&#xff01;&#xff01;&#xff01; Author: 源码时代 Raymon老师 说在前头 Mysql&#xff0c;作为一款优秀而广泛使用的数据库管理系统&#xff0c;对于众多Java工程师来…

发布npm包流程

发布npm包的步骤如下&#xff1a; 在终端中通过 npm init 命令创建一个新的npm包&#xff0c;按照提示填写包的信息&#xff0c;如包名称、版本、描述、作者、许可证等。 在包的根目录下创建一个 index.js 文件&#xff0c;编写你的代码。 确认你已经注册了npm账号&#xff0…