bizlog通用操作日志组件(使用篇)

news2024/11/18 22:27:55

引言

在这里插入图片描述
如上图所示,产品的新需求,需要将操作人在系统中具体编辑操作的变更内容记录下来。

按正常思路来说,无非就是将修改前后的对象字段逐个比较,再拼接为详细的操作描述记录到操作日志表中。如果是一个模块的需求,单独写个方法即可,但各个模块都有这部分需求,在各个模块中单独写就显得冗余了,功能代码与日志记录代码混在一起,代码可读性更差了。

因此,项目中引入了新的日志组件,统一实现团队项目中各模块记录操作日志的需求,将日志记录代码与功能代码分离,提升代码的可读性。

概述

在一个系统中,日志主要分为系统日志和操作日志。

系统日志主要是为开发排查问题提供依据。操作日志主要是对某条数据进行新增或者修改操作后进行记录,操作日志要求可读性强,因为它是给用户看的,比如订单的物流信息,用户需求知道在什么时候发生了什么事情。

本篇博客介绍的组件解决的就是该问题:「谁」在「什么时间」对「什么」做了「什么事」

快速开始

1. Maven依赖

<dependency>
   <groupId>io.github.mouzt</groupId>
   <artifactId>bizlog-sdk</artifactId>
   <version>3.0.3<version>
</dependency>

2. SpringBoot入口启用日志组件,添加@EnableLogRecord注解

tenant是代表租户的标识,一般一个服务或者一个业务下的多个服务都写死一个 tenant 就可以。示例如下:

@SpringBootApplication
@EnableTransactionManagement
@EnableLogRecord(tenant = "com.mzt.test")
public class Main {

    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}

3. 普通日志记录

下面给一个示例,在新增方法上加上@LogRecord注解,补上对应的参数,代码如下:

@LogRecord(
		operator = "{{#currentUser}}",
        fail = "创建订单失败,失败原因:「{{#_errorMsg}}」",
        subType = "MANAGER_VIEW",
        extra = "{{#order.toString()}}",
        success = "{{#order.purchaseName}}下了一个订单,购买商品「{{#order.productName}}」,测试变量 {{#innerOrder.productName}}",
        type = LogRecordType.ORDER, bizNo = "{{#order.orderNo}}")
public boolean createOrder(Order order) {
    log.info("【创建订单】orderNo={}", order.getOrderNo());
    // db insert order
    Order order1 = new Order();
    order1.setProductName("内部变量测试");
    LogRecordContext.putVariable("innerOrder", order1);
    return true;
}

参数说明:

  • SpEL 表达式:其中用双大括号包围起来的(例如:{{#order.purchaseName}})#order.purchaseName 是 SpEL表达式。Spring中支持的它都支持的。比如调用静态方法,三目表达式
  • type:是拼接在 bizNo 上作为 log 的一个标识。避免 bizNo 都为整数 ID 的时候和其他的业务中的 ID 重复。比如订单 ID、用户 ID 等,type可以是订单或者用户
  • bizNo:就是业务的 ID,比如订单ID,我们查询的时候可以根据 bizNo 查询和它相关的操作日志
  • subType:日志子类型,主要是便于对日志做分类,实现不同角色的人看到不同的日志
  • operator:操作人
  • success:方法调用成功后把 success 记录在日志的内容中
  • extra:支持记录操作的详情或者额外信息
  • fail:如果抛出异常则记录fail的日志,其中的 #_errorMsg 是取的方法抛出异常后的异常的 errorMessage

此时会打印操作日志 “张三下了一个订单,购买商品「XXX」,测试变量「内部变量测试」

4. 保存日志于存储介质中

实现ILogRecordService接口即可,在record方法中保存日志记录,查询日志可根据自身业务需求实现。

@Service
public class DbLogRecordServiceImpl implements ILogRecordService {

    @Resource
    private LogRecordMapper logRecordMapper;

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void record(LogRecord logRecord) {
        log.info("【logRecord】log={}", logRecord);
        LogRecordPO logRecordPO = LogRecordPO.toPo(logRecord);
        logRecordMapper.insert(logRecordPO);
    }

    @Override
    public List<LogRecord> queryLog(String bizKey, Collection<String> types) {
        return Lists.newArrayList();
    }

    @Override
    public PageDO<LogRecord> queryLogByBizNo(String bizNo, Collection<String> types, PageRequestDO pageRequestDO) {
        return logRecordMapper.selectByBizNoAndCategory(bizNo, types, pageRequestDO);
    }
}

通过上面简单的几步就能实现记录新增操作的日志,不用在业务代码中写一些繁琐的日志代码。下面再简单介绍一些组件的其他使用特性和示例。

其他使用

1. 自定义函数

在日志记录中,可能会存在字段值是ID,如订单号,而看到一堆数字并不知道对应的内容(订单名等)这日志记录的可读性很差。因此可以使用自定义函数,可在函数中实现通过ID查询到对应内容显示。

使用上只需要实现框架里面的IParseFunction的接口,实现两个方法:

1)functionName() 方法就返回注解上面的函数名;

2)apply()函数参数是 "{ORDER{#orderId}}"中SpEL解析的#orderId的值,这里是一个数字1223110,接下来只需要在实现的类中把 ID 转换为可读懂的字符串就可以了, 一般为了方便排查问题需要把名称和ID都展示出来,例如:"订单名称(ID)"的形式。

示例代码:

//使用了自定义函数,主要是在 {{#orderId}} 的大括号中间加了 functionName
@LogRecord(success = "更新了订单{ORDER{#orderId}},更新内容为...",
        type = LogRecordType.ORDER, bizNo = "{{#order.orderNo}}",
        extra = "{{#order.toString()}}")
public boolean update(Long orderId, Order order) {
    return false;
}

// 还需要加上函数的实现
@Slf4j
@Component
public class OrderParseFunction implements IParseFunction {

    @Override
    public boolean executeBefore() {
        return true;
    }

    @Override
    public String functionName() {
        return "ORDER";
    }

    @Override
    public String apply(Object value) {
        log.info("@@@@@@@@");
        if (StringUtils.isEmpty(value)) {
            return "";
        }
        log.info("###########,{}", value);
        Order order = new Order();
        order.setProductName("xxxx");
        return order.getProductName().concat("(").concat(value.toString()).concat(")");
    }
}

2. 使用方法参数之外的变量

可以在方法中通过 LogRecordContext.putVariable(variableName, Object) 的方法添加变量,第一个对象为变量名称,后面为变量的对象

3. diff特性

用于快速比较并记录两个对象之间不同的字段内容。

1)使用@DiffLogField标注字段含义

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {
    @DiffLogField(name = "订单ID", function = "ORDER")
    private Long orderId;
    @DiffLogField(name = "订单号")
    private String orderNo;
    private String purchaseName;
    private String productName;
    @DiffLogField(name = "创建时间")
    private LocalDateTime createTime;

    @DiffLogField(name = "创建人")
    private UserDO creator;
    @DiffLogField(name = "更新人")
    private UserDO updater;
    @DiffLogField(name = "列表项", function = "ORDER")
    private List<String> items;

    @DiffLogField(name = "拓展信息", function = "extInfo")
    private String[] extInfo;

    @Data
    public static class UserDO {
        @DiffLogField(name = "用户ID")
        private Long userId;
        @DiffLogField(name = "用户姓名")
        private String userName;
    }
}

name:是生成的 DIFF 文案中 Field 的中文, function: 自定义函数,例如可以把用户ID映射成用户姓名。

2)在更新方法上,使用日志注解

@LogRecord(success = "更新订单。{_DIFF{#oldOrder, #newOrder}}",
        type = LogRecordType.ORDER, bizNo = "{{#newOrder.orderNo}}",
        extra = "{{#newOrder.toString()}}")
public boolean diff(Order oldOrder, Order newOrder) {
    //....
    return false;
}

4. 日志记录与业务逻辑一起回滚

默认日志记录错误不影响业务的流程,若希望日志记录过程如果出现异常,让业务逻辑也一起回滚,在 @EnableLogRecord 中 joinTransaction 属性设置为 true, 另外 @EnableTransactionManagement order 属性设置为0 (让事务的优先级在@EnableLogRecord之前)

@EnableLogRecord(tenant = "com.mzt.test", joinTransaction = true)
@EnableTransactionManagement(order = 0)
public class Main {

    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}

总结

通过本篇博客对bizlog的简单介绍与使用教程,相信你一定感受到了该组件的魅力所在。如果你的项目中正好有同样的需求,而你还是在业务逻辑代码中实现,不妨试试该日志框架,简单好用。

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

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

相关文章

用HTML+CSS做一个学生抗疫感动专题网页设计作业网页

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

springboot如何修改thymeleaf中默认的页面路径及默认的后缀名呢?

转自: springboot如何修改thymeleaf中默认的页面路径及默认的后缀名呢? 下文讲述springboot修改thymefleaf修改页面默认路径及后缀名的方法分享&#xff0c;如下所示: 实现思路:只需在配置文件中修改相应的配置即可&#xff0c;如&#xff1a;application.yaml spring:thym…

MySQL单表查询操作详解,不做CRUD程序员

在我们对数据进行操作时&#xff0c;查询无疑是至关重要的&#xff0c;查询操作灵活多变&#xff0c;我们可以根据开发的需求&#xff0c;设计高效的查询操作&#xff0c;把数据库中存储的数据展示给用户。 文章目录前言1. 基础查询1.1 基础查询语法1.2 基础查询练习2. 条件查询…

大数据路线

一、概念部分 1.1 大数据、数仓、数据湖、中台的概念 区别数仓数据湖使用场景批处理&#xff0c;BI&#xff0c;数据可视化机器学习、预测分析、数据分析Schema写入型读取型数据源类型OLTP为主的结构化数据loT&#xff0c;日志&#xff0c;各个端等结构非结构均可性价比需要快…

牛客刷题总结——Python入门08:面向对象、正则表达式

&#x1f935;‍♂️ 个人主页: 北极的三哈 个人主页 &#x1f468;‍&#x1f4bb; 作者简介&#xff1a;Python领域优质创作者。 &#x1f4d2; 系列专栏&#xff1a;《牛客题库-Python篇》 &#x1f310;推荐《牛客网》——找工作神器|笔试题库|面试经验|实习经验内推&am…

Design A Youtube

title: Notes of System Design No.05 — Design a Youtube description: ‘Design a Youtube’ date: 2022-05-14 13:45:37 tags: 系统设计 categories: 系统设计 01. Funtional Requirements 02. Non Functional Requirements 03.Assumption 04 API 05 High Level Design 上…

05 MSYS2中安装树莓派32位和64位ARM交叉编译工具

作者将狼才鲸创建日期2022-11-14 Gitee源码和工程地址&#xff1a;才鲸嵌入式 / 开源安防摄像机&#xff08;嵌入式软件&#xff09;CSDN文章地址&#xff1a;项目介绍&#xff1a;开源安防摄像机&#xff08;嵌入式软件&#xff09; 4.3 MSYS2中安装32位和64位ARM交叉编译工具…

1524_AURIX TC275存储分布_下

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 继续前面的学习&#xff0c;这一次把这个小章节的剩余信息看完。 这一部分是外设相关的寄存器地址区间描述&#xff0c;看起来一个模块的地址空间占用基本都是256个字节。具体包括什么暂时…

Unity技术手册-UGUI零基础详细教程-Graphic Raycaster 射线检测和Canvas Group

往期文章分享点击跳转>《导航贴》- Unity手册&#xff0c;系统实战学习点击跳转>《导航贴》- Android手册&#xff0c;重温移动开发 本文约3千字&#xff0c;新手阅读需要6分钟&#xff0c;复习需要2分钟 【收藏随时查阅不再迷路】 &#x1f449;关于作者 众所周知&#…

outsystems合集系列(三)

outsystemsModeling DataDatabase Entities的介绍如何创建Database Entities如何用excel快速导入真实数据到entity?如何用excel快速创建entity并导入真实数据?Static Entities的介绍Modeling Data 这一节我将介绍在outsystems中建模数据(model data)的一些思路。注意在这里我…

shellcode 中 null byte 的成因和避免方式总结

背景 shellcode 中要避免 null byte&#xff08;\x00&#xff09;这个是个通用的概念&#xff08;windows&#xff0c;linux 都是一样&#xff09;&#xff0c;因为栈溢出的数据作为字符串写入到栈上&#xff0c;\x00 会作为字符串终止符&#xff0c;毁掉整个 shellcode。 这…

HTML+CSS个人静态网页设计

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

【AGC】安全规则resource.data获取不到字段

问题背景 在表结构里有但resource.data获取不到uid字段 解决该问题 request变量是指请求&#xff0c;request.resource.data是请求数据的所有字段和值的映射&#xff1b;resource变量是指所请求的数据在数据库中保存的状态&#xff0c;resource.data是数据库中保存数据的所有…

教师工作量管理系统思路(链表应用)

教师工作量管理系统思路&#xff08;链表应用&#xff09; 文章目录教师工作量管理系统思路&#xff08;链表应用&#xff09;题目描述&#xff1a;初始信息菜单部分数据结构功能实现查询历史信息从键盘录入信息信息删除和修改工作量计算如何存储到工作量信息链表中&#xff1f…

Bootstrap响应式轮播效果网页(1+X Web前端开发中级 例题)

文章目录 &#x1f4c4;题目要求 &#x1f9e9;说明 &#x1f9e9;效果图 &#x1f4bb;HTML代码 &#x1f3af;实现效果 &#x1f4f0;完整答案 &#x1f4c4;题目要求 阅读下列说明、效果图和HTML代码&#xff0c;进行静态网页开发&#xff0c;填写&#xff08;1&…

springboot12总结篇(9 10 11)

1.视图解析 以重定向为例 如果返回字符串 则会调用视图解析器thymeleafResolver判断返回值为普通字符串&#xff0c;返回thymeleafView调用其render方法&#xff0c;拼接直接访问 2.异常处理 404 ControllerAdvie 处理异常类 ExceptionHandler ControllerAdvie 处理异常类…

适配器模式

思考适配器模式 适配器模式通过转换已有的接口&#xff0c;达成目标需要的接口 适配器模式还可以将多种差异化接口适配成同一接口做统一输出 1.适配器模式的本质 适配器模式的本质是:转换匹配,复用功能。 适配器通过转换调用已有的实现&#xff0c;从而能把已有的实现匹配成需要…

【Spring】——4、使用@Scope注解设置组件的作用域

&#x1f4eb;作者简介&#xff1a;zhz小白 公众号&#xff1a;小白的Java进阶之路 专业技能&#xff1a; 1、Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理 2、熟悉Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理&#xff0c;具备⼀定的线…

VCED:学习Jina的简单操作

文章目录VCED:学习Jina的简单操作在pycharm里连接docker环境几个简单的jina demoimagetextvideoVCED:学习Jina的简单操作 在pycharm里连接docker环境 在pycharm里找到docker环境 New一个环境 在docker里进入terminal&#xff0c;找到python 位置 成功得到decker环境 运行jina…

004_步进电机实验

步进电机是将电脉冲信号转变为角位移或线位移的开环控制元件。本章主要从步进电机的结构、工作原理、电机参数分别介绍&#xff0c;最后通过实验来实现步进电机运动的简单控制。本章所要实现的功能是&#xff1a;通过 ULN2003 驱动模块控制 28BYJ48 步进电机运行方向及速度&…