springboot实现接口请求日志自动生成(日志自动埋点)

文章目录

  • 1.作用:
  • 2.原理:
  • 3.代码:
    • 一.config层
    • 二. mq层 :
    • 三.service层:
  • 4.效果图
  • 5.声明

1.作用:

springboot接口请求日志自动生成,实现接口日志自动埋点生成
1.统一日志生成格式;—方便查看
2.汇总请求参数和请求结果一次性输出日志数据 ,方便查询问题,节省查询请求问题时间;—很直观的日志,前后端问题排查快
3.通过日志自动生成减少编写日志时间,减少人力成本;—省编码时间
4.记录用户行为轨迹,记录接口时间,为后续风险监控,用户行为统计分析做铺垫;—记录数据

2.原理:

通过面向切面编程的形式,在不影响原有项目的业务(包括加解密)的同时,进行日志埋点

代码
配置模块

3.代码:

一.config层

1.bean类型调度

/*** 获取spring bean**/
@Component
public class SpringContextAware implements ApplicationContextAware {/*** Spring应用上下文*/private static ApplicationContext context;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {SpringContextAware.context = applicationContext;}/*** 获取Spring应用上下文* @return*/public static ApplicationContext getApplicationContext() {return context;}/*** 获取对象* @param name* @return* @throws BeansException*/public static Object getBean(String name) throws BeansException {return context.getBean(name);}
}

2.核心逻辑

ps:其中的ApiOperation 是swagger的注解,可以自定义注解实现自己的参数配置,
这里只是为了方便使用已有的swagger的ApiOperation 注解 的value(接口说明)和notes(接口发布说明)的数据

/**** 监控埋点* 打印请求和响应信息*/
@Aspect
@Component
public class WebLogAspect {@Autowiredprivate RedisTokenLoader redisTokenLoader;@Autowiredprivate MqProducer mqProducer;// 拿到日志对象//    slf4j的日志对象private final Logger log = LoggerFactory.getLogger(WebLogAspect.class);@Pointcut("execution( * com.jt.saas..*Controller.*(..))")public void webLog() {}/*** 读取会话中的token去取redis缓存信息* 有效期受控于redis.timeout** @return*/protected LoginInfoVo readLoginInfo(HttpServletRequest request) {String token = request.getHeader(HeaderConstant.HEADER_MINI_TOKEN);if (token == null) {// 非拦截接口的token校验String authorization = request.getHeader(HttpConstant.AUTHORIZATION);if (authorization != null) {if (authorization.length() > 7) {token = authorization.substring(7);}}}if (StringUtils.isBlank(token)) {return null;}return redisTokenLoader.readToken(token);}// 切点实现@Around("webLog()")public Object doAround(ProceedingJoinPoint pjp) throws Throwable {// 记录开始时间long start = System.currentTimeMillis();RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;assert servletRequestAttributes != null;HttpServletRequest request = servletRequestAttributes.getRequest();LoginInfoVo loginInfo = readLoginInfo(request);// 获取方法名String className = pjp.getTarget().getClass().getName();// 获取执行的方法名称String methodName = pjp.getSignature().getName();MethodSignature signature = (MethodSignature) pjp.getSignature();Method method = signature.getMethod();ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);// 定义返回参数Object result = null;// 获取方法入参// Object[] param = pjp.getArgs();// String requestBody = convertFormToString(request);// 执行目标方法result = pjp.proceed();ObjectMapper objectMapper = new ObjectMapper();String bodyData = null;try {bodyData = objectMapper.writeValueAsString(result);} catch (JsonProcessingException e) {throw new RuntimeException(e);}JSONObject  param= convertFormToJson(request);LogVo logVo=new LogVo();logVo.setUserId(loginInfo != null&&loginInfo.getMemberId()!=null ? loginInfo.getMemberId() : null);logVo.setUserName(loginInfo != null&&loginInfo.getNickname2()!=null ? loginInfo.getNickname2() : null);logVo.setMobile(loginInfo != null&&loginInfo.getMobile()!=null ? loginInfo.getMobile() : null);logVo.setModelName(apiOperation != null&&apiOperation.value()!=null ?apiOperation.value(): null);logVo.setRemark(apiOperation != null&&apiOperation.notes()!=null ?apiOperation.notes(): null);logVo.setUsedTime(System.currentTimeMillis() - start);logVo.setParamData(param);logVo.setResultData(bodyData.length() > 2000 ? "数据太大截取2000数据:"+bodyData.substring(0, 2000) :result);logVo.setMethodName(className+"."+methodName);logVo.setIp(request.getRemoteAddr());logVo.setUrlData(request.getRequestURL().toString());// 日志输出log.info("userRequest:{}",JSON.toJSONString(logVo));//每个项目自定义自己的mq去处理自定义日志统计或者分析行为mqProducer.sendDelay("MINI_LOGVO",JSON.toJSONString(logVo),100);return result;}private String convertFormToString(HttpServletRequest request) {Map<String, String> result = new HashMap<>(8);Enumeration<String> parameterNames = request.getParameterNames();while (parameterNames.hasMoreElements()) {String name = parameterNames.nextElement();result.put(name, request.getParameter(name));}try {return JSON.toJSONString(result);} catch (Exception e) {throw new IllegalArgumentException(e);}}private JSONObject convertFormToJson(HttpServletRequest request) {try {JSONObject jsonObject = new JSONObject();Enumeration<String> parameterNames = request.getParameterNames();while (parameterNames.hasMoreElements()) {String name = parameterNames.nextElement();jsonObject.put(name, request.getParameter(name));}return jsonObject;} catch (Exception e) {throw new IllegalArgumentException(e);}}
}

二. mq层 :

异步处理日志通用逻辑,使用单例模式实现一个工厂实例,用Spring来获取具体的跟踪服务实现,以此来提供高度解耦和可扩展的服务注册与获取机制去处理指定接口自定义服务

@Component
public class TrackingMq {@Autowiredprivate TrackingCommonService trackingCommonService;@RabbitListener(bindings = {@QueueBinding(value = @Queue(value = "MINI_LOGVO", durable = "true"), exchange =@Exchange(value = "saasExchange", type = "x-delayed-message"), key = "MINI_LOGVO")},containerFactory = "firstFactory")@RabbitHandlerpublic void process(Message message) throws Exception {String msg = new String(message.getBody(), "UTF-8");LogVo logVo = JSON.parseObject(msg, LogVo.class);//通用的分析功能trackingCommonService.commonTracking(logVo);//自定义分析模块》按MethodName执行自定义功能;按需对接口进行自定义处理TrackingService trackingService= TrackingFactory.getInstance().get(logVo.getMethodName());if(trackingService!=null){trackingService.changeByTrackLog(logVo);}}}

三.service层:

通用处理,自定义处理,单例工厂
1.单例工厂

*** 单例工厂类** 追踪日志,触发自定义模块功能*/
public class TrackingFactory {private Map<String, TrackingService> map= new HashMap<>();public static class Holder {public static TrackingFactory instance = new TrackingFactory();}public static TrackingFactory getInstance() {return Holder.instance;}public TrackingService get(String methodName) {return map.get(methodName);}public TrackingFactory() {TrackingService listRankActivityRuleTrackingServiceImpl = (ListRankActivityRuleTrackingServiceImpl) SpringContextAware.getBean("listRankActivityRuleTrackingServiceImpl");map.put("com.jt.saas.mini.controller.activity.ActivityController.listRankActivityRule", listRankActivityRuleTrackingServiceImpl);}
}

2.通用日志处理

public interface TrackingCommonService {public void commonTracking(LogVo logVo);
}
@Service
public class TrackingCommonServiceImpl implements TrackingCommonService{private final Logger log = LoggerFactory.getLogger(TrackingCommonServiceImpl.class);public void outTimeCount(LogVo logVo){if(logVo.getUsedTime()>1000){log.error("日志分析:超时1s的接口:{},{}",logVo.getModelName(),logVo.getUsedTime());}}@Overridepublic void commonTracking(LogVo logVo) {outTimeCount(logVo);}
}

3.自定义指定接口日志处理

public interface TrackingService {/*** 日志触发自定义内容:数据分析,数据统计,风险监控,发送mq,保存数据入库等等* @param logVo*/void changeByTrackLog(LogVo logVo);
}

可以增加无数个自定义指定接口日志处理实现自己的处理日志方式

@Service
public class ListRankActivityRuleTrackingServiceImpl implements TrackingService {private final Logger log = LoggerFactory.getLogger(ListRankActivityRuleTrackingServiceImpl.class);private Map<String, Integer> map= new HashMap<>();@Overridepublic void changeByTrackLog(LogVo logVo) {//发送mq//发送kafaka//发送数据库//统计用户访问次数Integer i=map.get(logVo.getMethodName());if(i==null){i=1;}else{i=i+1;}map.put(logVo.getMethodName(), i);log.info("日志分析:{}:访问次数:{}",logVo.getModelName(),i);}
}

4.效果图

日志自动埋点生成效果图
通用日志分析效果图

5.声明

本文内容均为Bright_ Chen原创,版权均属于Bright_ Chen所有,未经授权,不得转载、摘编、复制或建立镜像。我们尊重他人知识版权,如有引用、摘录,请务必注明来源及作者,否则我们将依法追究其侵权责任。

根据《中华人民共和国著作权法》、《信息网络传播权保护条例》等相关法律法规,任何单位或个人不得擅自复制、转载、链接、抓取或以其他方式使用本站内容;已授权或合作的单位或个人,应在授权范围内使用,并注明来源。

对于违反本声明者,Bright_ Chen保留追究其法律责任的权利。同时,对于恶意抄袭、盗版等侵犯版权的个人和组织,我们将采取包括但不限于法律诉讼、公开曝光等措施维护合法权益。

我们鼓励和支持合法合规的信息共享,对于希望合法使用本文内容的机构或个人,请事先通过以下联系方式与我们取得联系,获取合法授权:

联系邮箱:1024347104@qq.com
感谢您对原创内容的尊重与支持!

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

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

相关文章

Redis 压缩列表与快速列表

Redis 压缩列表&#xff08;Ziplist&#xff09; Redis 的压缩列表&#xff08;Ziplist&#xff09;是一种用于存储小数据集的高效数据结构&#xff0c;特别适合于具有较小和相似数据长度的情况。它主要用于节省内存和提高性能。下面是关于 Redis 压缩列表的详细介绍&#xff…

恶补,正态分布

正态分布的公式如下&#xff1a; φ μ , σ 1 2 π σ ⋅ e − ( x − μ ) 2 2 σ 2 , x ∈ ( − ∞ , ∞ ) \varphi_{\mu,\sigma}\frac{1}{\sqrt{2\pi}\sigma}\cdot e^{-\frac{(x-\mu)^2}{2\sigma^2}},x\in(-\infty,\infty) φμ,σ​2π ​σ1​⋅e−2σ2(x−μ)2​,x∈…

19-4 LLM之野望 4 - 探索大模型的量化

什么是模型量化&#xff1f; 从本质上讲&#xff0c;模型量化就是为了提高效率。想象一下&#xff0c;你有一本非常厚的教科书&#xff08;就像那些老式百科全书一样&#xff09;&#xff0c;需要整天随身携带。很累吧&#xff1f;现在&#xff0c;如果你能把它缩小到一本漫画…

Postgresql导入几何数据的几种方式

postgis方式导入 1.直接使用postgis客户端方式导入 首先&#xff0c;电脑要安装postgresql和对应版本的postgis。然后通过postgis客户端软件连接到postgresql数据库。然后导入。具体详细操作如下所示&#xff1a; 第一步&#xff1a;首先要再postgis中创建数据库 Create da…

【关于使用swoole的知识点整理】

目录 &#xff08;1&#xff09;Swoole 如何理解&#xff0c;能解决你项目中的哪些痛点&#xff1f; &#xff08;2&#xff09;Swoole里的协程是什么&#xff0c;怎么用&#xff1f;为什么协程可以提高并发&#xff1f; &#xff08;3&#xff09;简述Swoole有哪些优点&…

怎样在 PostgreSQL 中进行用户权限的精细管理?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 怎样在 PostgreSQL 中进行用户权限的精细管理&#xff1f;一、权限管理的重要性二、PostgreSQL 中的权…

Linux云计算 |【第一阶段】ENGINEER-DAY2

主要内容&#xff1a; 磁盘空间管理fdisk、parted工具、开机自动挂载、文件系统、交换空间 KVM虚拟化 实操前骤&#xff1a; 1&#xff09;添加一块硬盘&#xff08;磁盘&#xff09;&#xff0c;需要关机才能进行操作&#xff0c;点击左下角【添加硬件】 2&#xff09;选择2…

Lamp 小白菜鸟从入门到精通

前言 “LAMP包”的脚本组件中包括了CGIweb接口&#xff0c;它在90年代初期变得流行。这个技术允许网页浏览器的用户在服务器上执行一个程序&#xff0c;并且和接受静态的内容一样接受动态的内容。程序员使用脚本语言来创建这些程序因为它们能很容易有效的操作文本流&#xff0…

2.0.PyTorch神经网络基础

层和块 块&#xff08;block&#xff09;可以描述单个层、由多个层组成的组件或整个模型本身。 使用块进行抽象的一个好处是可以将一些块组合成更大的组件&#xff0c; 这一过程通常是递归的。多个层被组合成块&#xff0c;形成更大的模型&#xff1a; #层 import torch from …

LeetCode做题记录(第二天)169. 多数元素

题目&#xff1a;169. 多数元素 标签&#xff1a;数组 哈希表 分治 计数 排序 题目信息&#xff1a; 思路一&#xff1a; 在题目中出现了计数&#xff0c;那我们就可以直接考虑考虑使用哈希表 unordered_map 即遍历的时候记录每个数的出现次数&#xff0c;当出现次数大于n/…

苍穹外卖跟练项目前端localhost打不开页面启动nginx报错[alert] could not open error log file问题解决

一、安装路径为纯英文 查看自己的安装路径是否为纯英文环境&#xff0c;刚开始下载的资料包是有中文路径的&#xff0c;要将资料包中的nginx-1.20.2文件夹复制一份然后粘贴到一个新建的纯英文的目录&#xff0c;我这里装到的是 D:\Program Files\nginx-1.20.2 二、删掉logs文件…

centos5离线安装git

1、首先下载安装包 下载地址1&#xff1a; https://src.fedoraproject.org/repo/pkgs/git/git-2.26.2.tar.xz/sha512/5d92d07b171c5cd6e89a29c1211c73c1c900cd51c74d690aebfb4a3d0e93b541b09b42b6d6a1a82f5c3d953096771f9a8605c63be139f559f58698c1a0eabcfc/ 下载地址2&#xf…

【AI学习】关于Scaling Law的相关学习

一、苦涩的教训 首先&#xff0c;学习一段重要话语&#xff1a; The biggest lesson that can be read from 70 years of AI research is that general methods that leverage computation are ultimately the most effective, and by a large margin. 从70年的人工智能研究中…

C语言 ——— 浮点数类型 在 内存中 的 存储模式

目录 浮点数存储规则 单\双精度浮点数 存储 S、M、E 的布局 有效数字M 和 指数位E 的特殊规定 浮点数在内存中是否存储的S、M、E 浮点数存储规则 根据国际标准IEEE754&#xff08;电气和电子工程协会&#xff09;规定&#xff1a;任意一个 浮点数F的二进制 都可以表示成…

Java练习05

tip&#xff1a; 在Java中&#xff0c;^ 运算符是用于按位异或&#xff08;XOR&#xff09;操作的&#xff0c;而不是用于指数运算。 要进行指数运算&#xff0c;你需要使用 Math.pow() 方法。可以接收两个double类型的参数。 public static double pow(double a, double b)…

从 Pandas 到 Polars 二十九:在Polars中进行机器学习预处理

Polars中的机器学习 在最近的时间里&#xff0c;我将探索在Polars中进行机器学习&#xff08;ML&#xff09;可以走到多远。 除了ML模型外&#xff0c;scikit-learn还提供了许多数据预处理功能。让我们看看在Polars中进行一些这样的预处理是否值得。 最小-最大缩放示例 简单…

<数据集>铁轨缺陷检测数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;844张 标注数量(xml文件个数)&#xff1a;844 标注数量(txt文件个数)&#xff1a;844 标注类别数&#xff1a;3 标注类别名称&#xff1a;[Spalling, Squat, Wheel Burn] 序号类别名称图片数框数1Spalling3315522…

abc362(abcde)

A - Buy a Pen&#xff08;模拟&#xff09; 题意&#xff1a;输入红笔r元&#xff0c;绿笔g元&#xff0c;蓝笔b元&#xff0c;不喜欢c笔&#xff0c;求最少花多少钱能买到一支笔 分析&#xff1a;比较除了c笔之外的两根笔&#xff0c;取最小值 代码&#xff1a; #include…

【ProtoBuf】通讯录实现(网络版)

Protobuf 还常用于通讯协议、服务端数据交换场景。那么在这个示例中&#xff0c;我们将实现一个网络版本的通讯录&#xff0c;模拟实现客户端与服务端的交互&#xff0c;通过 Protobuf 来实现各端之间的协议序列化。 需求如下&#xff1a; 客户端可以选择对通讯录进行以下操…

达梦数据库 DISQL连接数据库与执行SQL、脚本的方法

DISQL连接数据库与执行SQL、脚本的方法 1.DISQL介绍2.DISQL连接数据库的方法2.1 本地连接2.2 远程连接2.3 CONN连接 3.执行SQL、脚本的方法3.1 通过DISQL登录后在字符界面3.2 启动DISQL时运行脚本3.3 进入DISQL后&#xff0c;通过start命令运行脚本3.4 使用EDIT命令编辑脚本 1.…