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,一经查实,立即删除!

相关文章

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…

怎样在 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文件…

【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的二进制 都可以表示成…

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

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

【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.…

【中项】系统集成项目管理工程师-第3章 信息技术服务-3.1内涵与外延与3.2原理与组成

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&…

OV7670寄存器读出0x00或0xFF

文章目录 问题描述原因分析解决方案 问题描述 OV7670的输出图像异常&#xff0c;怀疑寄存器没有正确配置&#xff0c;在SignalTap中观察到SIO_D在读出阶段一直为高或低 寄存器读出0x00 寄存器读出0xFF 原因分析 在确保电源、时钟和读写时序没有问题的情况下&#xff0c;有…

PCB系统学习(1)--PCB印制电路板

PCB印制电路板 1.1PCB的定义1.2PCB的层叠结构1.2.1PCB单层板1.2.2PCB双层板1.2.3PCB四层板 1.3PCB的通孔&#xff0c;盲孔&#xff0c;埋孔1.4元器件的符号与封装1.5PCB的生产过程 1.1PCB的定义 PCB(PrintedCircuitBoard)&#xff0c;中文即印制电路板&#xff0c;或印刷线路板…

Linux--多线程

今日内容 线程的结束机制&#xff1a; 1.不同与进程没有孤儿线程和僵尸线程。 2.主进程结束&#xff0c;任意生成的次线程都会结束。(因为共享的进程空间被回收了)。 3.次线程的正常结束不会影响主线程的运行。 子线程的回收策略&a…

《0基础》学习Python——第十八讲__爬虫\<1>

一、什么是爬虫 爬虫是一种网络数据抓取的技术。通过编写程序&#xff08;通常使用Python&#xff09;&#xff0c;爬虫可以自动化地访问网页&#xff0c;解析网页内容并提取出所需的数据。爬虫可以用于各种用途&#xff0c;如搜索引擎的索引&#xff0c;数据分析和挖掘&#x…

NVIDIA 完全过渡到开源 GPU 内核模块

目录 支持的 GPU安装程序更改将包管理器与 CUDA 元包配合使用使用 runfile使用安装帮助程序脚本包管理器详细信息apt&#xff1a;基于 Ubuntu 和 Debian 的发行版dnf&#xff1a;Red Hat Enterprise Linux、Fedora、Kylin、Amazon Linux 或 Rocky Linuxzypper&#xff1a;SUSE …

怎么关闭 Windows 安全中心,手动关闭 Windows Defender 教程

Windows 安全中心&#xff08;也称为 Windows Defender Security Center&#xff09;是微软 Windows 操作系统内置的安全管理工具&#xff0c;用于监控和控制病毒防护、防火墙、应用和浏览器保护等安全功能。然而&#xff0c;在某些情况下&#xff0c;用户可能需要关闭 Windows…