Spring 接口日志切片记录

1、注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author * @program * @description 忽略统一响应注解定义* @packagename * @date **/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {/*** 模块*/public String title() default "";public BusinessType businessType() default BusinessType.SELECT;/*** 是否保存请求的参数*/public boolean isSaveRequestData() default true;/*** 是否保存响应的参数*/public boolean isSaveResponseData() default true;}

2、接口类型

public enum BusinessType {/*** 保存**/SAVE,/*** 删除**/DELETE,/*** 查询**/SELECT;}

3、日志实体

@Data
@TableName("user_log")
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "用户日志", description = "用户日志")
public class UserLog implements Serializable {private static final long serialVersionUID = 1L;/*** 自增主键id*/@TableId(value = "id", type = IdType.AUTO)@ApiModelProperty(value = "主键")private Long id;/*** 用户编号*/@ApiModelProperty(value = "用户编号")private Long userNo;/*** 用户账号*/@ApiModelProperty(value = "用户账号")private String userAccount;@ApiModelProperty(value = "客户端ID")private String clientId;/*** 用户名称*/@ApiModelProperty(value = "用户名称")private String userName;/*** 操作方法*/@ApiModelProperty(value = "操作方法")private String callMethod;/*** 操作结果*/@ApiModelProperty(value = "操作结果")private String callResult;/*** 操作时间*/@ApiModelProperty(value = "操作时间")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")private Date operationTime;/*** 操作内容*/@ApiModelProperty(value = "操作内容")private String operationContent;/*** 操作IP*/@ApiModelProperty(value = "操作IP")private String operationIp;private String operationParam;private String operationResult;private String operationMsg;/*** 操作客户端*/@ApiModelProperty(value = "操作客户端")private String operationClient;/*** 操作地理位置*/@ApiModelProperty(value = "操作地理位置")private String operationAddress;}

4、日志记录

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hierway.tool.utils.AddressUtils;
import com.hierway.tool.utils.RequestHeaderUtils;
import com.hierway.user.bo.OauthInfo;
import com.hierway.user.entity.UserLog;
import com.hierway.user.feign.IUserLogClient;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*** @author * @program * @description 接口日志记录* @packagename * @date **/
@Aspect
@Component
@Slf4j
public class LogHandlerAspect {@Value("${spring.log-filter.enabled:true}")private Boolean enabled;@Value("${spring.log-filter.type:SAVE,DELETE}")private List<String> filterType;@Resourceprivate IUserLogClient userLogClient;private ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 20, 60, TimeUnit.MINUTES,new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.DiscardPolicy());/*** 切点*//*** 处理完请求后执行** @param joinPoint 切点*/@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult){try {if(!enabled||!filterType.contains(controllerLog.businessType().toString())){return;}UserLog userLog = new UserLog();userLog.setOperationTime(new Date());String className = joinPoint.getTarget().getClass().getName();String methodName = joinPoint.getSignature().getName();userLog.setCallMethod(className + "." + methodName);userLog.setOperationContent(controllerLog.title());// 是否需要保存request,参数和值if (controllerLog.isSaveRequestData()){userLog.setOperationParam("");// 获取参数的信息,传入到数据库中。Object[] params = joinPoint.getArgs();for (int i = 0; i < params.length; i++) {Object param =  params[i];userLog.setOperationParam(userLog.getOperationParam()+"参数"+i+":"+JSON.toJSONString(param)+";");}}// 是否需要保存response,参数和值if (controllerLog.isSaveResponseData() && jsonResult!=null){userLog.setOperationResult(JSON.toJSONString(jsonResult));}userLog.setCallResult("成功");userLog.setOperationIp(RequestHeaderUtils.getIpAddr());userLog.setOperationClient(RequestHeaderUtils.getClient());userLog.setOperationAddress(AddressUtils.getAddresses(userLog.getOperationIp()));executor.execute(new Runnable() {@Overridepublic void run() {try {userLogClient.create(userLog);}catch (Exception ex){log.error("保存用户日志失败",ex);}}});}catch (Exception ex){log.error("请求日志失败",ex);}}/*** 拦截异常操作** @param joinPoint 切点* @param e 异常*/@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e){try {if(!enabled||!filterType.contains(controllerLog.businessType().toString())){return;}UserLog userLog = new UserLog();userLog.setOperationTime(new Date());String className = joinPoint.getTarget().getClass().getName();String methodName = joinPoint.getSignature().getName();userLog.setCallMethod(className + "." + methodName);userLog.setOperationContent(controllerLog.title());// 是否需要保存request,参数和值if (controllerLog.isSaveRequestData()){userLog.setOperationParam("");// 获取参数的信息,传入到数据库中。Object[] params = joinPoint.getArgs();for (int i = 0; i < params.length; i++) {Object param =  params[i];userLog.setOperationParam(userLog.getOperationParam()+"参数"+i+":"+JSON.toJSONString(param)+";");}}userLog.setOperationMsg(e.getMessage());userLog.setCallResult("失败");userLog.setOperationIp(RequestHeaderUtils.getIpAddr());userLog.setOperationClient(RequestHeaderUtils.getClient());userLog.setOperationAddress(AddressUtils.getAddresses(userLog.getOperationIp()));executor.execute(new Runnable() {@Overridepublic void run() {try {userLogClient.create(userLog);}catch (Exception ex){log.error("保存用户日志失败",ex);}}});}catch (Exception ex){log.error("请求日志失败",ex);}}}

5、请求头获取类

public class RequestHeaderUtils {public static String getClient(){HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String agent = request.getHeader("user-agent");StringTokenizer st = new StringTokenizer(agent,";");//得到用户的浏览器名st.nextToken();return agent;}public static String getIpAddr(){HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();if (request == null){return null;}String ip = null;// X-Forwarded-For:Squid 服务代理String ipAddresses = request.getHeader("X-Forwarded-For");if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)){// Proxy-Client-IP:apache 服务代理ipAddresses = request.getHeader("Proxy-Client-IP");}if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)){// WL-Proxy-Client-IP:weblogic 服务代理ipAddresses = request.getHeader("WL-Proxy-Client-IP");}if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)){// HTTP_CLIENT_IP:有些代理服务器ipAddresses = request.getHeader("HTTP_CLIENT_IP");}if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)){// X-Real-IP:nginx服务代理ipAddresses = request.getHeader("X-Real-IP");}// 有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IPif (ipAddresses != null && ipAddresses.length() != 0){ip = ipAddresses.split(",")[0];}// 还是不能获取到,最后再通过request.getRemoteAddr();获取if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)){ip = request.getRemoteAddr();}return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;}
}

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

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

相关文章

LeetCode 接雨水 木桶理论、dp预处理

原题链接&#xff1a; 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题面&#xff1a; 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a…

计算机里的神灵(SCIP)

计算机程序的构造和解释 我找到计算机里的神灵了&#xff0c;开心一刻 下面是从MIT官网下载的 SCIP求值器&#xff08;解释器&#xff09;的代码&#xff0c;这个官网是个宝藏库 还有其他视频课程和 SCIP的问题答案和可运行代码 链接&#xff1a;https://ocw.mit.edu/courses/6…

VS2022 编译protobuf , qt 使用

一、下载源码 protobuf: 同步 https://github.com/protocolbuffers/protobuf (gitee.com) 下载如v3.11.2 版本 二、下载CMake 三、编译 1、在1处选择源码目录下的cmake 目录&#xff1b;在2处选择一处空目录&#xff08;自己随便建&#xff09; 2、点击config&#xff0c;选择…

系统架构设计师-大数据

目录 一、大数据 1、大数据架构 2、大数据技术生态 3、Lambda架构 4、Kappa架构 5、Lambda架构与Kappa架构对比 一、大数据 1、大数据架构 大数据是指其大小或复杂性无法通过现有常用的软件工具&#xff0c;以合理的成本并在可接受的时限内对其进行捕获、管理和处理的数据集。…

【rhce考试时间是每年什么时候呢?】

RHCE9.0 新技术 公开课 10月11日&#xff0c;12日 感兴趣可留言 如果你是一个系统管理员&#xff0c;或者正朝着这个方向努力前进&#xff0c;那么你可能已经听过RHCE这个词。RHCE是Red Hat Certified Engineer的缩写&#xff0c;是全球公认的Linux系统工程师认证之一。通过获…

获取热门电影算法

功能#2&#xff1a;获取热门电影 为我们的“Netflix”项目实现“获取热门电影”功能。 我们将介绍以下内容 描述 解决方案 复杂性措施 时间复杂度 空间复杂度 描述# 现在&#xff0c;我们需要建立一个标准&#xff0c;以便将来自多个国家的顶级电影组合成一个单一的顶级电影…

postman访问新建项目报404

"status": 404 查看项目&#xff0c;发现启动类和代码执行部分没有在同一个包下&#xff0c;导致controller的访问没有注册到启动类下&#xff1b;

定义现代化实时数据仓库,SelectDB 全新产品形态全面发布

导读&#xff1a;9 月 25 日&#xff0c;2023 飞轮科技产品发布会在线上正式召开&#xff0c;本次产品发布会以 “新内核、新图景” 为主题&#xff0c;飞轮科技 CEO 马如悦全面解析了现代化数据仓库的演进趋势&#xff0c;宣布立足于多云之上的 SelectDB Cloud 云服务全面开放…

【设计模式】五、原型模式

文章目录 概述示例传统的方式的优缺点原型模式原理结构图-uml 类图 原型模式解决克隆羊问题的应用实例Sheep类实现clone()运行原型模式在 Spring 框架中源码分析 深入讨论-浅拷贝和深拷贝浅拷贝的介绍 小结 概述 示例 克隆羊问题 现在有一只羊 tom&#xff0c;姓名为: tom, 年…

【轮趣-科大讯飞】M260C 环形六麦测试 1 - 产品介绍与配置

原文发布在飞书上&#xff0c;想要的伙伴请联系我&#xff0c;懒得把飞书链接放这了

二十二、MySQL联合查询

1、基础概念 &#xff08;1&#xff09;语法&#xff1a; select …… from …… union [all] select …… from …… &#xff08;2&#xff09;理解&#xff1a; 所谓的联合查询&#xff0c;就是对多个条件查询结果进行联合处理&#xff0c;取其并集。 2、实际操作 &…

K8S:pod集群调度及相关操作

文章目录 一.pod集群调度概念1.调度约束( List-Watch组件)2.List-Watch的工作机制&#xff08;1&#xff09;List-Watch的工作机制流程&#xff08;2&#xff09;List-Watch的工作机制图示 3.调度的过程&#xff08;1&#xff09;调度的任务&#xff08;2&#xff09;调度选择p…

Java 设计模式——抽象工厂模式

目录 1.概念2.结构3.实现4.优缺点5.使用场景6.模式扩展7.JDK源码解析——Collection.iterator方法 1.概念 &#xff08;1&#xff09;Java 设计模式——工厂方法模式 中考虑的是一类产品的生产&#xff0c;如畜牧场只养动物、电视机厂只生产电视机等。这些工厂只生产同种类产品…

sqlmap tamper脚本编写

文章目录 tamper脚本是什么&#xff1f;指定tamper脚本运行sqlmap安全狗绕过tamper脚本 tamper脚本是什么&#xff1f; SQLMap 是一款SQL注入神器&#xff0c;可以通过tamper 对注入payload 进行编码和变形&#xff0c;以达到绕过某些限制的目的。但是有些时候&#xff0c;SQLM…

Qt创建线程(使用moveToThread方法创建子线程)

1.moveTothread方法: &#xff08;1&#xff09;要使用moveToThread方法必须继承与QObject类 &#xff08;2&#xff09;创建任务对象时不能指定父对象 例子&#xff1a; MyWork* work new MyWork(this); // error MyWork* work new MyWork; // ok &#xff08;3&#…

InputAction的使用

感觉Unity中InputAction的使用&#xff0c;步步都是坑。 需求点介绍 当用户长按0.5s 键盘X或者VR left controller primaryButton (即X键)时&#xff0c;显示下一个图片。 步骤总览 创建InputAction资产将该InputAction资产绑定到某个GameObject上在对应的script中&#xf…

[Linux]多线程编程

[Linux]多线程编程 文章目录 [Linux]多线程编程pthread_create函数pthread_join函数pthread_exit函数pthread_cancel函数pthread_self函数pthread_detach函数理解线程库和线程id Linux操作系统下&#xff0c;并没有真正意义上的线程&#xff0c;而是由进程中的轻量级进程&#…

9、SpringBoot_日志使用

三、日志 1.日志的使用 使用 RestController public class LogController {public static final Logger log LoggerFactory.getLogger(LogController.class);GetMapping("/index")public String index(){log.info("请求info 信息");log.debug("请…

Django的设计模式及模板层

Django的设计模式及模板层 设计模式MVC和MVT MVC 代表 Model-View-Controller(模型-视图-控制器)模式。 M 模型层(Model),主要用于对数据库层的封装 V 视图层(View),用于向用户展示结果 (WHAT HOW) C 控制(Controller&#xff0c;用于处理请求、获取数据、返回结果(重要) 作…

JetBrains常用插件

Codota AI Autocomplete Java and JavaScript&#xff1a;自动补全插件 Background Image plus&#xff1a;背景图片设置 rainbow brackets&#xff1a;彩虹括号&#xff0c;便于识别 CodeGlance2&#xff1a; 类似于 Sublime 中的代码缩略图&#xff08;代码小地图&#xff…