做影视网站/快速排名推荐
写在最前
如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。
源码地址(后端):mingyue: 🎉 基于 Spring Boot、Spring Cloud & Alibaba 的分布式微服务架构基础服务中心
源码地址(前端):mingyue-ui: 🎉 基于 Vue3 + TS + Vite + Element plus 等技术,适配 MingYue 后台微服务
文档地址:Wiki - Gitee.com
为什么要日志插件?
在Java应用程序中记录日志是一种良好的实践,它为开发、运维和支持团队提供了很多好处。以下是一些主要的理由:
-
故障排除和调试:
-
日志是定位和解决问题的重要工具。通过在关键代码路径和操作中插入日志语句,开发人员可以追踪应用程序的执行流程,快速定位潜在的错误和异常。
-
-
性能分析:
-
记录关键操作的执行时间、资源使用情况等信息,有助于性能分析和优化。通过分析日志,可以确定应用程序的瓶颈并改进性能。
-
-
安全审计:
-
记录关键的安全事件和用户活动,以便进行审计和检测潜在的安全威胁。登录失败、访问敏感信息等事件的记录对于安全监控至关重要。
-
-
系统状态监控:
-
通过记录系统状态和关键指标,可以实时监控应用程序的运行状况。这有助于及时发现和解决潜在的问题,以提高系统的稳定性和可用性。
-
-
版本追踪和审计:
-
在代码中记录版本信息、变更历史和代码提交信息,有助于追踪应用程序的演变过程。审计日志还可以用于追溯特定功能或问题的起源。
-
-
用户行为分析:
-
对于包含用户交互的应用程序,记录用户活动可以帮助了解他们的使用模式、偏好和行为。这对于改进用户体验和调整产品设计非常有帮助。
-
-
合规性和法规要求:
-
许多行业和法规要求记录关键事件和操作,以确保企业的合规性。通过日志记录,可以满足这些法规的要求,并提供审计证据。
-
-
持久化数据:
-
将日志存储在持久化介质中,例如文件或数据库,以便在应用程序重新启动后仍然可以访问日志。这有助于在系统故障或应用程序崩溃时还原状态并进行故障排除。
-
日志设计
日志类型
系统操作日志和用户登录日志是两种不同类型的日志,它们记录了系统中不同方面的活动
-
系统操作日志:记录系统的各种操作,包括但不限于增删改查、上传与下载文件等。
-
用户登录日志:记录用户登录和注销的信息。
记录方式
-
系统操作日志:采用注解(非侵入)方式记录;
-
用户登录日志:采用显式(侵入)方式记录;
日志插件
添加 mingyue-common-log 插件
<dependencies><dependency><groupId>com.csp.mingyue</groupId><artifactId>mingyue-common-security</artifactId></dependency> </dependencies>
Log 注解
@Target({ ElementType.PARAMETER, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Log { /*** 模块*/String module() default ""; /*** 功能*/BusinessType businessType() default BusinessType.OTHER; /*** 操作人类别*/OperatorUserType operatorUserType() default OperatorUserType.MANAGE; /*** 是否保存请求的参数*/boolean isSaveRequestData() default true; /*** 是否保存响应的参数*/boolean isSaveResponseData() default true; /*** 排除指定的请求参数*/String[] excludeParamNames() default {}; }
Log 切面
操作日志记录核心类
@Slf4j @Aspect @RequiredArgsConstructor @AutoConfiguration public class LogAspect { private final ServiceInstance serviceInstance; /*** 排除敏感属性字段*/public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; /*** 处理完请求后执行* @param joinPoint 切点*/@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {handleLog(joinPoint, controllerLog, null, jsonResult);} /*** 拦截异常操作* @param joinPoint 切点* @param e 异常*/@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {handleLog(joinPoint, controllerLog, e, null);} protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {// 日志记录开始时间Long startTime = System.currentTimeMillis(); // ========数据库日志========OperateLogEvent operateLog = new OperateLogEvent(); try {// 请求信息String ip = ServletUtils.getClientIP();operateLog.setReqIp(ip);operateLog.setServiceId(serviceInstance.getServiceId());operateLog.setReqAddress(AddressUtils.getRealAddressByIP(ip));operateLog.setReqUrl(StrUtil.sub(Objects.requireNonNull(ServletUtils.getRequest()).getRequestURI(), 0, 255)); operateLog.setStatus(BusinessStatus.SUCCESS.ordinal()); // 用户信息LoginUser loginUser = LoginHelper.getLoginUser();operateLog.setUserId(loginUser.getUserId());operateLog.setUserName(loginUser.getUsername()); if (e != null) {operateLog.setStatus(BusinessStatus.FAIL.ordinal());operateLog.setException(StrUtil.sub(e.getMessage(), 0, 2000));} // 设置方法名称String className = joinPoint.getTarget().getClass().getName();String methodName = joinPoint.getSignature().getName();operateLog.setMethod(className + "." + methodName + "()"); // 设置User-AgentoperateLog.setUserAgent(ServletUtils.getRequest().getHeader(HttpHeaders.USER_AGENT));// 设置请求方式operateLog.setReqMethod(ServletUtils.getRequest().getMethod());// 处理设置注解上的参数getControllerMethodDescription(joinPoint, controllerLog, operateLog, jsonResult);}catch (Exception exp) {// 记录本地异常日志log.error("异常信息:{}", exp.getMessage());}finally {Long endTime = System.currentTimeMillis();operateLog.setDuration(endTime - startTime);// 发布事件保存数据库SpringUtils.context().publishEvent(operateLog);}} /*** 获取注解中对方法的描述信息 用于Controller层注解* @param log 日志* @param operateLog 操作日志*/public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperateLogEvent operateLog,Object jsonResult) throws Exception {// 设置标题operateLog.setModule(log.module());// 设置 action 动作operateLog.setBusinessType(log.businessType().ordinal());// 设置操作人类别operateLog.setUserType(log.operatorUserType().ordinal());// 是否需要保存 request,参数和值if (log.isSaveRequestData()) {// 获取参数的信息,传入到数据库中。setRequestValue(joinPoint, operateLog, log.excludeParamNames());}// 是否需要保存 response,参数和值if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) {R resp = JSONUtil.toBean(JSONUtil.toJsonStr(jsonResult), R.class);operateLog.setRespMsg(resp.getMsg());operateLog.setRespCode(resp.getCode());operateLog.setRespResult(StrUtil.sub(JSONUtil.toJsonStr(jsonResult), 0, 2000));}} /*** 获取请求的参数,放到log中* @param operLog 操作日志* @throws Exception 异常*/private void setRequestValue(JoinPoint joinPoint, OperateLogEvent operLog, String[] excludeParamNames)throws Exception {Map<String, String> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());String requestMethod = operLog.getReqMethod();if (MapUtil.isEmpty(paramsMap) && HttpMethod.PUT.name().equals(requestMethod)|| HttpMethod.POST.name().equals(requestMethod)) {String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);operLog.setReqParams(StrUtil.sub(params, 0, 2000));}else {MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES);MapUtil.removeAny(paramsMap, excludeParamNames);operLog.setReqParams(StrUtil.sub(JSONUtil.toJsonStr(paramsMap), 0, 2000));}} /*** 参数拼装*/private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) {StringJoiner params = new StringJoiner(" ");if (ArrayUtil.isEmpty(paramsArray)) {return params.toString();}for (Object o : paramsArray) {if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {String str = JSONUtil.toJsonStr(o);Dict dict = JsonUtils.parseMap(str);if (MapUtil.isNotEmpty(dict)) {MapUtil.removeAny(dict, EXCLUDE_PROPERTIES);MapUtil.removeAny(dict, excludeParamNames);str = JSONUtil.toJsonStr(dict);}params.add(str);}}return params.toString();} /*** 判断是否需要过滤的对象。* @param o 对象信息。* @return 如果是需要过滤的对象,则返回true;否则返回false。*/@SuppressWarnings("rawtypes")public boolean isFilterObject(final Object o) {Class<?> clazz = o.getClass();if (clazz.isArray()) {return clazz.getComponentType().isAssignableFrom(MultipartFile.class);}else if (Collection.class.isAssignableFrom(clazz)) {Collection collection = (Collection) o;for (Object value : collection) {return value instanceof MultipartFile;}}else if (Map.class.isAssignableFrom(clazz)) {Map map = (Map) o;for (Object value : map.values()) {return value instanceof MultipartFile;}}return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse|| o instanceof BindingResult;} }
异步调用日志服务
@Slf4j @Component @RequiredArgsConstructor public class LogEventListener { private final RemoteLogService remoteLogService; /*** 保存系统日志记录*/@Async@EventListenerpublic void saveLog(OperateLogEvent operateLog) {log.info("保存系统日志记录落库「{}」", JSONUtil.toJsonStr(operateLog));remoteLogService.saveSysOperateLog(BeanUtil.copyProperties(operateLog, SysOperateLog.class));} }
自动注入日志类
org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.csp.mingyue.common.log.event.LogEventListener com.csp.mingyue.common.log.aspect.LogAspect
系统操作日志表设计
DROP TABLE IF EXISTS sys_operate_log; CREATE TABLE sys_operate_log (operate_log_id BIGINT(20) NOT NULL COMMENT '操作日志ID',module VARCHAR(50) DEFAULT '' COMMENT '模块',business_type INT(2) DEFAULT 0 COMMENT '业务类型(0其它 1新增 2修改 3删除)',method VARCHAR(100) DEFAULT '' COMMENT '方法名称',service_id VARCHAR(32) DEFAULT NULL COMMENT '服务ID',user_id BIGINT(20) NOT NULL COMMENT '用户ID',user_name VARCHAR(50) NOT NULL COMMENT '用户账号',user_type TINYINT(1) DEFAULT 0 COMMENT '用户类型(0其它 1系统用户)',user_agent VARCHAR(1000) DEFAULT NULL COMMENT '用户代理',req_ip VARCHAR(128) DEFAULT '' COMMENT '请求IP',req_address VARCHAR(255) DEFAULT '' COMMENT '请求地点',req_url VARCHAR(255) DEFAULT '' COMMENT '请求URL',req_method VARCHAR(20) DEFAULT NULL COMMENT '请求方式',req_params TEXT DEFAULT NULL COMMENT '请求参数',duration BIGINT NOT NULL COMMENT '执行时长,单位(ms)',resp_code INT DEFAULT NULL COMMENT '结果码',resp_msg VARCHAR(512) NULL DEFAULT '' COMMENT '结果提示',resp_result VARCHAR(2000) DEFAULT '' COMMENT '返回参数',status CHAR(1) DEFAULT 0 COMMENT '操作状态(0正常 1异常)',exception TEXT DEFAULT NULL COMMENT '异常信息',operate_time DATETIME NOT NULL COMMENT '操作时间',is_deleted CHAR(1) DEFAULT '0' COMMENT '删除标志(0正常,1删除)',create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',create_time DATETIME DEFAULT NULL COMMENT '创建时间',update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',update_time DATETIME DEFAULT NULL COMMENT '更新时间',PRIMARY KEY (operate_log_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC COMMENT='系统操作日志';
测试日志注解
使用注解
@Log(module = "用户管理", businessType = BusinessType.DELETE)
@DeleteMapping("{userId}") @Log(module = "用户管理", businessType = BusinessType.DELETE) @Operation(summary = "删除用户", parameters = { @Parameter(name = "userId", description = "用户ID", required = true) }) public R<Boolean> delUser(@PathVariable Long userId) {return R.ok(sysUserService.delUser(userId)); }
调用接口
调用完成后查看数据库是否存在该操作记录即可
curl -X 'DELETE' 'http://192.168.63.114:7100/system/sysUser/111111111' -H 'accept: */*' -H 'Authorization: UWapduuggQcNSqg1oQZ17ZyfPHDxxt8Q'
相关文章:

033-从零搭建微服务-日志插件(一)
写在最前 如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。 源码地址(后端):mingyue: 🎉 基于 Spring Boot、Spring Cloud & Alibaba 的分布式微服务架构基础服务中心 源…...

短期经济波动:均衡国民收入决定理论(三)
短期经济波动:国民收入决定理论(三) 文章目录 短期经济波动:国民收入决定理论(三)[toc]1 总需求曲线及其变动1.1 总需求曲线含义1.2 总需求曲线推导1.2.1 代数推导1.2.2 几何推导 1.3 AD曲线及其变动1.3.1 扩张性财政政策1.3.2 扩张性货币政策 2 总供给曲…...

电力感知边缘计算网关产品设计方案-网关软件架构
边缘计算网关采用ARM定制硬件平台架构,包含上位机端(内网)和FPGA网关端(外网)两部分,通过芯片间的高速信号总线实现边缘计算网关工业数据采集、数据实时传输、数据存储、网关状态信息收集等功能。 边缘计算网关上位机端(内网)重点完成工业数据采集、业务软件运算、客户…...

最新AI创作系统ChatGPT系统运营源码/支持最新GPT-4-Turbo模型/支持DALL-E3文生图
一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统,支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…...

Java使用Redis的几种客户端介绍
Redis是一种高性能的内存数据库,可以提供快速的数据读写操作。在Java中使用Redis,需要使用Redis客户端。目前,Java中常用的Redis客户端有以下几种: Jedis Jedis是Java中最流行的Redis客户端之一,它提供了丰富的API和…...

程序员的护城河
程序员的护城河 算法,一定是过硬的算法!!!举个栗子:算法不硬吃大亏写在最后 算法,一定是过硬的算法!!! 其实会什么技术不重要,掌握多少种编程语言也不重要&a…...

常见面试题-MySQL软删除以及索引结构
为什么 mysql 删了行记录,反而磁盘空间没有减少? 答: 在 mysql 中,当使用 delete 删除数据时,mysql 会将删除的数据标记为已删除,但是并不去磁盘上真正进行删除,而是在需要使用这片存储空间时…...

信号的机制——信号处理函数的注册
在 Linux 操作系统中,为了响应各种各样的事件,也是定义了非常多的信号。我们可以通过 kill -l 命令,查看所有的信号。 # kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP6) SIGABRT 7) SIGBUS …...

JS-项目实战-鼠标悬浮变手势(鼠标放单价上生效)
1、鼠标悬浮和离开事件.js //当页面加载完成后执行后面的匿名函数 window.onload function () {//get:获取 Element:元素 By:通过...方式//getElementById()根据id值获取某元素let fruitTbl document.getElementById("fruit_tbl");//table.rows:获取这个表格…...

redis运维(十一) python操作redis
一 python操作redis ① 安装pyredis redis常见错误 说明:由于redis服务器是5.0.8的,为了避免出现问题,默认最高版本的即可 --> 适配 ② 操作流程 核心:获取redis数据库连接对象 ③ Python 字符串前面加u,r,b的含义 原因: 字符串在…...

黑马程序员微服务 第五天课程 分布式搜索引擎2
分布式搜索引擎02 在昨天的学习中,我们已经导入了大量数据到elasticsearch中,实现了elasticsearch的数据存储功能。但elasticsearch最擅长的还是搜索和数据分析。 所以今天,我们研究下elasticsearch的数据搜索功能。我们会分别使用DSL和Res…...

什么是UV贴图?
UV 是与几何图形的顶点信息相对应的二维纹理坐标。UV 至关重要,因为它们提供了表面网格与图像纹理如何应用于该表面之间的联系。它们基本上是控制纹理上哪些像素对应于 3D 网格上的哪个顶点的标记点。它们在雕刻中也很重要。 为什么UV映射很重要? 默认情…...

从哪里下载 Oracle database 11g 软件
登入My Oracle Support,选择Patches & Updates 标签页,点击下方的Latest Patchsets链接: 然后单击Oracle Database,就可以下载11g软件了: 安装单实例数据库需要1和2两个zip文件,安装GI需要第3个zip文…...

Ingress安全网关
目录 文章目录 目录本节实战TCP 流量拆分🚩 实战:TCP 流量拆分-2023.11.15(测试成功) Ingress安全网关Kubernetes Ingress🚩 实战:Kubernetes Ingress-2023.11.15(测试成功) Ingress GatewayIngress Gateway🚩 实战&am…...

Jmeter控制RPS
一、前言 RPS (Request Per Second)一般用来衡量服务端的吞吐量,相比于并发模式,更适合用来摸底服务端的性能。我们可以通过使用 JMeter 的常数吞吐量定时器来限制每个线程的RPS。对于RPS,我们可以把他理解为我们的TPS,我们就不…...

【Nginx】转发配置nginx.conf
文章目录 NginxNginx主要作用转发配置相关问题参考推荐阅读 Nginx Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамбл…...

uniapp实现下载图片到本地
uniapp实现下载图片到本地 在uniapp开发中,可以使用uni.downloadFile方法实现下载文件功能,客户端直接发起一个 HTTP GET 请求,返回文件的本地临时路径。 const urlPath http://192.168.0.1:8080/fileApi/logo.png uni.downloadFile({url:…...

spring cloud-注册中心(Eureka)
一、服务注册中心组件(*) 定义:服务注册中心就是在整个微服务架构单独抽取一个服务,该服务不做项目中任何业务功能,仅用来在微服务中记录微服务、对微服务进行健康状态检查,及服务元数据信息存储常用的注册中心:eurek…...

004 OpenCV akaze特征点检测匹配
目录 一、环境 二、akaze特征点算法 2.1、基本原理 2.2、实现过程 2.3、实际应用 2.4、优点与不足 三、代码 3.1、数据准备 3.2、完整代码 一、环境 本文使用环境为: Windows10Python 3.9.17opencv-python 4.8.0.74 二、akaze特征点算法 特征点检测算法…...

openRPA开源项目源码编译
最近接触到了一个新的领域——RPA,RPA全称Robotic Process Automation,中文名为机器人流程自动化。RPA可以视作一个数字机器人,它可以通过程序来模拟人与软件系统的交互过程,代替人工将大量重复、有规则的计算机操作自动化&#x…...

飞书开发学习笔记(八)-开发飞书小程序Demo
飞书开发学习笔记(八)-开发飞书小程序Demo 一.小程序开发概述 1.1 小程序开发概述 飞书开发文档中查看:小程序开发概述 飞书小程序是指可以运行在飞书客户端中的小程序,小程序的一套代码可以适配 Android、iOS、PC 多平台,且用户体验与飞书…...

Unity UI 完全解决方案
Unity UI 完全解决方案 在我学习开发 unity 游戏尝试进行 UI 的构建的过程中,尝试寻找当前 Unity 最为推荐的 UI 构建方式,或者说最优的框架方案。 在中文网里寻找了半天,总感觉很多文章和教程给了方案,但又说不清楚为啥用这个方…...

为什么打开idea时,没有启动页面,如何解决?
更新idea2021.2后,当双击idea打开时,发现没有启动界面,直接进入IDEA界面,中间等待时间,让人误以为没有打开idea成功,使得多次点击idea图标。 解决方案就是 在idea界面菜单栏中找到帮助(Help)&a…...

golang - 嵌入静态文件打包
go-bindata - embed结合嵌入静态文件打包可执行二进制文件 ## embed 嵌入静态文件到可执行二进制文件 # 安装go-bindata go get -u github.com/jteeuwen/go-bindata/... # 打包静态文件 go-bindata web/... 执行次命令之后会在项目目录下生成bindata.go文件,示例命令中模板文…...

SQL题
[极客大挑战 2019]EasySQL 进行简单的尝试,就知道是单引号的字符型注入 万能密码进行一个简单的尝试 结果就出来了 还是要了解一下原理 输入的是1,形成的sql语句是错误的SELECT*FROM table_name WHERE username1and password123; 第一个单引号和第二个…...

GUN介绍
介绍 GNU(GNU’s Not Unix)是一个自由操作系统项目,名字是一个递归的 GNU’s Not Unix 缩写,其目标是创建一个类Unix的操作系统。 该项目由Richard Stallman于1983年发起,并由自由软件基金会(Free Softwa…...

《Effective C++》条款15
在资源管理类中提供对原始资源的访问 class A {... }; int day(const A* ptr) {... } int main() {shared_ptr<A> ptr(new A);cout << day(ptr) << endl; } 这样写是错误的。因为day函数要求的参数是指针,而你传的实际上是一个对象。 如何解决呢&…...

CTFd-Web题目动态flag
CTFd-Web题目动态flag 1. dockerhub注册2. dockerfile编写3. 上传到docker仓库4. 靶场配置5. 动态flag实现 1. dockerhub注册 想要把我们的web题目容器上传到docker仓库中,我们需要dockerhub官网注册一个账号,网址如下 https://hub.docker.com/2. dock…...

系列九、对象的生命周期和GC
一、堆细分 Java堆从GC的角度还可以细分为:新生代(eden【伊甸园区】、from【幸存者0区】、to【幸存者1区】)和老年代。 二、MinorGC的过程 复制>清空》交换 1、eden、from区中的对象复制到to区,年龄1 首先,当eden区…...

spark 窗口滑动用于在不同的数据块之间执行操作
在 Scala 中进行分布式执行,例如使用 Apache Spark,可以通过设置窗口滑动来实现不同 RDD 之间的关联处理。窗口滑动是一种窗口操作,用于在不同的数据块之间执行操作。 以下是一个简单的示例,演示如何在 Spark 中使用窗口滑动&…...