网站首页 > 技术文章 正文
易哥,高级软件架构师、网络工程师、数据库工程师、注册电气工程师。
我们开发的业务系统通常会提供给很多人使用,那在使用的过程中,日志系统变得非常重要。
日志系统记录的用户行为有以下的作用:
- 从系统用户角度看:它展示了用户自身的操作历史和具体对象的变动历史,便于用户进行梳理
- 从系统管理员角度看:它可以记录了所有用户操作,便于我们定位异常行为
例如,在git的project操作中,我们就可以看到这样的操作日志展示:
对于这样的日志记录,我们可以在相关记录点添加对应的日志写入代码或者通过切面实现。然而,这样的日志展示是相对简单的,只是记录了操作行为的种类。而有时我们需要记录每个操作行为对操作对象引发的具体变动,例如展示出这样的结果:
这给日志记录带来了不小的挑战:
- 在一个系统中,可能涉及到多种对象(例如,学生、课程、老师),而每个对象的属性是完全不一样的
- 在一次操作中,可能改变了对象的一个或者多个属性,这也使得我们极难逐一记录
而今天,我们要介绍的是一套强大且易用的Java对象日志记录系统,支持对象属性变动的记录与查询。借助于它,我们可以方便地实现对象属性变动的记录与查询。
1 系统简介
ObjectLogger是一套强大且易用的对象日志记录系统。它能够将任意对象的变动日志记录下来,并支持查询。可以应用在用户操作日志记录、对象属性变更记录等诸多场景中。
该系统具有以下特点:
- 一站整合:系统支持日志的记录与查询,开发者只需再开发前端界面即可使用。
- 完全独立:与业务系统无耦合,可插拔使用,不影响主业务流程。
- 应用共享:系统可以同时供多个业务系统使用,互不影响。
- 简单易用:服务端直接jar包启动;业务系统有官方Maven插件支持。
- 自动解析:能自动解析对象的属性变化,并支持富文本的前后对比。
- 便于扩展:支持自定义对象变动说明、属性变动说明。支持更多对象属性类型的扩展。
整个项目包含四个部分:
- ObjectLoggerClient:能够集成到业务系统进行日志分析、发送jar包。可以从Maven官方仓库引入该jar包。该模块位于client子包下。
- ObjectLoggerServer:一个web服务,需要数据库的支持。它能够接收并保存ObjectLoggerClient发出的日志信息,支持日志的查询操作。该模块位于server子包下。
- react-object-logger:一个React前端组件,用于进行日志的前端展示。可以从npm官方仓库引入该组件。该子项目位于react-object-logger。
- ObjectLoggerDemo:一个业务端集成ObjectLoggerClient的示例。该模块位于demo子包下。
2 快速上手
2.1 创建数据库
使用该项目的/server/database/init_data_table.sql文件初始化两个数据表。
2.2 启动Server
下载该项目下最新的Server服务jar包,地址为/server/target/ObjectLoggerServer-*.jar。
启动下载的jar包。
java -jar ObjectLoggerServer-*.jar --spring.datasource.url=jdbc:{db}://{db_address}/{db_name} --spring.datasource.username={db_username} --spring.datasource.password={db_password}
上述命令中的用户配置项说明如下:
- db:数据库类型。如果使用MySql数据库则为mysql;如果使用SqlServer数据库则为sqlserver。
- db_address:数据库连接地址。如果数据库在本机则为127.0.0.1。
- db_name:数据库名,该数据库中需包含上一步初始化的两个数据表。
- db_username:数据库登录用户名。
- db_password:数据库登录密码。
启动jar成功后可以看到下面的界面:
使用浏览器访问下面的页面可以看到系统欢迎页面:
http://127.0.0.1:12301/ObjectLoggerServer/
访问上述地址可以看到下面的欢迎界面:
至此,ObjectLoggerServer系统已经搭建结束,可以接受业务系统的日志写入和查询操作。
3 业务系统接入
该部分讲解如何配置业务系统来将业务系统中的对象变化通过ObjectLoggerClient分析,然后记录到ObjectLoggerServer中。
这一部分的使用可以参照ObjectLoggerDemo项目,该项目给出了业务系统集成ObjectLoggerClient的详细示例。ObjectLoggerDemo的制品包可以从/demo/target/ObjectLoggerDemo-*.jar获得,无需其他配置直接运行java -jar ObjectLoggerDemo-*.jar便可以直接启动该项目。
可以直接根据启动页面的提示访问相关地址来体验:
3.1 引入依赖包
在pom中增加下面的依赖:
<dependency> <groupId>com.github.yeecode.objectlogger</groupId> <artifactId>ObjectLoggerClient</artifactId> <version>{最新版本}</version> </dependency>
3.2 添加对ObjectLoggerClient中bean的自动注入
3.2.1 对于SpringBoot应用
在SpringBoot的启动类前添加@ComponentScan注解,并在basePackages中增加ObjectLoggerClient的包地址:com.github.yeecode.objectlogger,如:
@SpringBootApplication @ComponentScan(basePackages={"{your_beans_root}","com.github.yeecode.objectlogger"}) public class MyBootAppApplication { public static void main(String[] args) { // 省略其他代码 } }
3.2.2 对于Spring应用
在applicationContext.xml增加对ObjectLoggerClient包地址的扫描:
<context:component-scan base-package="com.github.yeecode.objectlogger"> </context:component-scan>
3.3 完成配置
在application.properties中增加:
yeecode.objectLogger.serverAddress=http://{ObjectLoggerServer_address} yeecode.objectLogger.businessAppName={your_app_name} yeecode.objectLogger.autoLogAttributes=true
- ObjectLoggerServer_address:属性指向上一步的ObjectLoggerServer的部署地址,例如:127.0.0.1:12301
- your_app_name:指当前业务系统的应用名。以便于区分日志来源,实现同时支持多个业务系统
- yeecode.objectLogger.autoLogAttributes:是否对对象的所有属性进行变更日志记录
至此,业务系统的配置完成。已经实现了和ObjectLoggerServer端的对接。
4 日志查询
系统运行后,可以通过http://127.0.0.1:12301/ObjectLoggerServer/log/query查询系统中记录的日志,并通过传入参数对日志进行过滤。
通过这里,我们可以查询下一步中写入的日志。
5 日志展示
ObjectLogger有前端React组件react-object-logger支持,用于进行日志信息的展示。体验页面:react-object-logger 示例页面
展示效果如图:
感兴趣的用户可以前往react-object-logger进行了解。
同时也欢迎各位开发者开发面向其它前端技术栈的组件。
6 日志写入
业务系统在任何需要进行日志记录的类中引入LogClient。例如:
@Autowired private LogClient logClient;
6.1 简单使用
直接将对象的零个、一个、多个属性变化放入List<BaseAttributeModel>后调用logAttributes方法发出即可。List<BaseAttributeModel>置为null则表示此次对象无需要记录的属性变动。例如,业务应用中调用:
logClient.logAttributes( "CleanRoomTask", 5, "Tom", "add", "Add New Task", "Create a cleanRoomTask", "taskName is :Demo Task", null);
在ObjectLoggerServer中使用如下查询条件:
http://127.0.0.1:12301/ObjectLoggerServer/log/query?appName=ObjectLoggerDemo&objectName=CleanRoomTask&objectId=5
查询到日志:
{ "respMsg": "SUCCESS", "respData": [ { "id": 1, "appName": "ObjectLoggerDemo", "objectName": "CleanRoomTask", "objectId": 5, "operator": "Jone", "operationName": "start", "operationAlias": "Start a Task", "extraWords": "Begin to clean room...", "comment": "Come on and start cleaning up.", "operationTime": "2019-07-04T06:53:40.000+0000", "attributeModelList": [ { "attributeType": "NORMAL", "attributeName": "status", "attributeAlias": "Status", "oldValue": "TODO", "newValue": "DOING", "diffValue": null, "id": 1, "operationId": 1 } ] } ], "respCode": "1000" }
6.2 对象变动自动记录
该功能可以自动完成新老对象的对比,并根据对比结果,将多个属性变动一起写入日志系统中。使用时,要确保传入的新老对象属于同一个类。
例如,业务系统这样调用:
CleanRoomTask oldTask = new CleanRoomTask(); oldTask.setId(5); oldTask.setTaskName("Demo Task"); oldTask.setStatus("TODO"); oldTask.setDescription("Do something..."); CleanRoomTask newTask = new CleanRoomTask(); newTask.setId(5); newTask.setTaskName("Demo Task"); newTask.setStatus("DOING"); newTask.setDescription("The main job is to clean the floor."); newTask.setAddress("Sunny Street"); newTask.setRoomNumber(702); logClient.logObject( cleanRoomTask.getId().toString(), "Tom", "update", "Update a Task", null, null, oldTask, newTask);
则我们可以使用下面查询条件:
http://127.0.0.1:12301/ObjectLoggerServer/log/query?appName=ObjectLoggerDemo&objectName=CleanRoomTask&objectId=5
查询到如下结果:
{ "respMsg": "SUCCESS", "respData": [ { "id": 4, "appName": "ObjectLoggerDemo", "objectName": "CleanRoomTask", "objectId": 5, "operator": "Tom", "operationName": "update", "operationAlias": "Update a Task", "extraWords": null, "comment": null, "operationTime": "2019-07-04T07:22:59.000+0000", "attributeModelList": [ { "attributeType": "NORMAL", "attributeName": "roomNumber", "attributeAlias": "roomNumber", "oldValue": "", "newValue": "702", "diffValue": null, "id": 5, "operationId": 4 }, { "attributeType": "NORMAL", "attributeName": "address", "attributeAlias": "address", "oldValue": "", "newValue": "Sunny Street", "diffValue": null, "id": 6, "operationId": 4 }, { "attributeType": "NORMAL", "attributeName": "status", "attributeAlias": "Status", "oldValue": "TODO", "newValue": "DOING", "diffValue": null, "id": 7, "operationId": 4 }, { "attributeType": "TEXT", "attributeName": "description", "attributeAlias": "Description", "oldValue": "Do something...", "newValue": "The main job is to clean the floor.", "diffValue": "Line 1<br/> -: <del> Do something... </del> <br/> +: <u> The main job is to clean the floor. </u> <br/>", "id": 8, "operationId": 4 } ] } ], "respCode": "1000" }
7 对象属性过滤
有些对象的属性的变动不需要进行日志记录,例如updateTime、hashCode等。ObjectLoggerClient支持对对象的属性进行过滤,只追踪我们感兴趣的属性。
并且,对于每个属性我们可以更改其记录到ObjectLoggerClient系统中的具体方式,例如修改命名等。
要想启用这个功能,首先将配置中的yeecode.objectLogger.autoLogAttributes改为false。
yeecode.objectLogger.autoLogAttributes=true
然后在需要进行变化日志记录的属性上增加@LogTag注解。凡是没有增加该注解的属性在日志记录时会被自动跳过。
例如,注解配置如下则id字段的变动将被忽略。
private Integer id; @LogTag private String taskName; @LogTag(alias = "UserId", extendedType = "userIdType") private int userId; @LogTag(alias = "Status") private String status; @LogTag(alias = "Description", builtinType = BuiltinTypeHandler.TEXT) private String description;
该注解属性介绍如下:
- alias:属性别名。默认情况下会将属性名写入。
- builtinType:ObjectLoggerClient的内置类型,为BuiltinTypeHandler的值。默认为BuiltinTypeHandler.NORMAL。
- BuiltinTypeHandler.NORMAL:记录属性的新值和旧值,对比值为null
- BuiltinTypeHandler.RICHTEXT: 用户富文本对比。记录属性值的新值和旧值,并将新旧值转化为纯文本后逐行对比差异,对比值中记录差异
- extendedType:扩展属性类型。使用ObjcetLogger时,用户可以扩展某些字段的处理方式,此时,alias等信息均可以被用户自主覆盖。
8 属性处理扩展
很多情况下,用户希望能够自主决定某些对象属性的处理方式。例如,对于例子中Task对象的userId属性,用户可能想将其转化为姓名后存入日志系统,从而使得日志系统与userId完全解耦。
ObjectLoggerClient完全支持这种情况,可以让用户自主决定某些属性的日志记录方式。要想实现这种功能,首先在需要进行扩展处理的属性上为@LogTag的extendedType属性赋予一个字符串值。例如:
@LogTag(alias = "UserId", extendedType = "userIdType") private int userId;
然后在业务系统中声明一个Bean继承BaseExtendedTypeHandler,作为自由扩展的钩子。代码如下:
@Service public class ExtendedTypeHandler implements BaseExtendedTypeHandler { @Override public BaseAttributeModel handleAttributeChange(String extendedType, String attributeName, String attributeAlias, Object oldValue, Object newValue) { // TODO } }
接下来,当ObjectLoggerClient处理到该属性时,会将该属性的相关信息传入到扩展Bean的handleAttributeChange方法中,然后用户可以自行处理。传入的四个参数解释如下:
- extendedType:扩展类型值,即@LogTag注解的extendedType值。本示例中为userIdType。
- attributeName:属性名。本示例中为userId。
- attributeAlias:属性别名,@LogTag注解的alias值。本示例中为UserId。
- oldValue:该属性的旧值。
- newValue:该属性的新值。
例如我们可以采用如下的方式处理userIdType属性:
@Service public class ExtendedTypeHandler implements BaseExtendedTypeHandler { @Override public BaseAttributeModel handleAttributeChange(String extendedType, String attributeName, String attributeAlias, Object oldValue, Object newValue) { BaseAttributeModel baseAttributeModel = new BaseAttributeModel(); if (extendedType.equals("userIdType")) { baseAttributeModel.setOldValue("USER_" + oldValue); baseAttributeModel.setNewValue("USER_" + newValue); baseAttributeModel.setDiffValue(oldValue + "->" + newValue); } return baseAttributeModel; } }
9 总结
怎么样,是不是有了这一套系统之后,再负责的日志系统都变得简单起来。快收藏起来当作自己的秘技吧!
易哥,高级软件架构师、网络工程师、数据库工程师、注册电气工程师。现从事软件架构架构设计工作。
分享架构师知识! 欢迎关注我们,不错过每期的原创干货!
猜你喜欢
- 2024-11-22 前端容器化实践
- 2024-11-22 npm audit —— 守护Node.js前端代码安全的小助手
- 2024-11-22 程序员私活利器 (Jfinal)——找到一次请求的所有日志
- 2024-11-22 完美日记的微服务实践和优化思路
- 2024-11-22 线上程序出bug,排查起来很麻烦?一个好的日志系统是关键
- 2024-11-22 MySQL慢查询:慢SQL定位、日志分析与优化方案
- 2024-11-22 ELK Stack实用教程:让你的日志数据发挥更大的价值
- 2024-11-22 干货 | 企业如何快速采集分析日志?
- 2024-11-22 Spring Boot 整合 Apache Spark 进行日志分析?
- 2024-11-22 Windows系统服务器系统日志在哪里查看?
你 发表评论:
欢迎- 07-10Oracle 与 Google Cloud 携手大幅扩展多云服务
- 07-10分享收藏的 oracle 11.2.0.4各平台的下载地址
- 07-10Oracle 和 Microsoft 推出 Oracle Exadata 数据库服务
- 07-10Oracle Database@Azure 推进到南美等新区域并增加了新服务
- 07-10Oracle宣布推出 Oracle Database@AWS 的有限预览版
- 07-10Oracle与Nextcloud合作,推出主权云上的安全协作平台
- 07-10NodeRED魔改版连接MsSql、PostgreSQL、MySQL、OracleDB存储无忧
- 07-10对于企业数据云备份,“多备份”承诺的是成本更低,管理更高效#36氪开放日深圳站#
- 602℃几个Oracle空值处理函数 oracle处理null值的函数
- 594℃Oracle分析函数之Lag和Lead()使用
- 582℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 579℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 574℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 567℃【数据统计分析】详解Oracle分组函数之CUBE
- 554℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 548℃Oracle有哪些常见的函数? oracle中常用的函数
- 最近发表
-
- Oracle 与 Google Cloud 携手大幅扩展多云服务
- 分享收藏的 oracle 11.2.0.4各平台的下载地址
- Oracle 和 Microsoft 推出 Oracle Exadata 数据库服务
- Oracle Database@Azure 推进到南美等新区域并增加了新服务
- Oracle宣布推出 Oracle Database@AWS 的有限预览版
- Oracle与Nextcloud合作,推出主权云上的安全协作平台
- NodeRED魔改版连接MsSql、PostgreSQL、MySQL、OracleDB存储无忧
- 对于企业数据云备份,“多备份”承诺的是成本更低,管理更高效#36氪开放日深圳站#
- 解读丨《归档文件整理规则》— 电子文件元数据存储
- Data Guard跳归档恢复的实践(dataguard failover)
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端路由 (61)
- 前端数组 (73)
- 前端js面试题 (50)
- 前端定时器 (59)
- 前端获取当前时间 (50)
- Oracle RAC (76)
- oracle恢复 (77)
- oracle 删除表 (52)
- oracle 用户名 (80)
- oracle 工具 (55)
- oracle 内存 (55)
- oracle 导出表 (62)
- oracle约束 (54)
- oracle 中文 (51)
- oracle链接 (54)
- oracle的函数 (57)
- 前端调试 (52)
本文暂时没有评论,来添加一个吧(●'◡'●)