架构师必看!操作日志系统搭建秘技
【大咖・来了 第7期】10月24日晚8点观看《智能导购对话机器人实践》
在Java开发中,我们经常会遇到一个棘手的问题:记录用户的操作行为。
某些操作是相对简单的,我们可以逐条记录。但是某些操作行为却很难记录,例如编辑操作。在某一次操作中,用户可能编辑了对象A的几个属性,而下一次操作中用户可能编辑了对象B的几个属性。如果我们针对对象A、对象B的属性变化分别进行记录,则整个操作十分复杂。而且,会与业务操作高度耦合。
而今天我们介绍的是一个叫ObjectLogger的系统,它是一个强大且易用的Java对象日志记录系统,能够分析任何对象的属性变化,实现对象变化的记录与查询。
因此,它可以应用在用户操作日志记录、对象属性变更记录等诸多场景中。简单易用,实为利器。
基于它,我们可以很方便地实现下面的效果。
该系统为github开源项目,地址为:https://github.com/yeecode/ObjectLogger
下面我们简单介绍下该系统。基于它,我们可以非常方便地搭建一套日志记录系统。
1 系统特点
该系统具有以下特点:
- 一站整合:系统支持日志的记录与查询,开发者只需再开发前端界面即可使用。
- 完全独立:与业务系统无耦合,可插拔使用,不影响主业务流程。
- 应用共享:系统可以同时供多个业务系统使用,互不影响。
- 简单易用:服务端直接jar包启动;业务系统有官方Maven插件支持。
- 自动解析:能自动解析对象的属性变化,并支持富文本的前后对比。
- 便于扩展:支持自定义对象变动说明、属性变动说明。支持更多对象属性类型的扩展。
2 快速上手
2.1 创建数据库
使用该项目的/server/database/init_data_table.sql文件初始化两个数据表。
2.2 启动Server
下载该项目下***的Server服务jar包,地址为/server/target/ObjectLogger-*.jar。
启动下载的jar包。
java -jar ObjectLogger-*.jar --spring.datasource.driver-class-name={db_driver} --spring.datasource.url=jdbc:{db}://{db_address}/{db_name} --spring.datasource.username={db_username} --spring.datasource.password={db_password}
上述命令中的用户配置项说明如下:
- db_driver:数据库驱动。如果使用MySql数据库则为com.mysql.jdbc.Driver;如果使用SqlServer数据库则为com.microsoft.sqlserver.jdbc.SQLServerDriver。
- db:数据库类型。如果使用MySql数据库则为mysql;如果使用SqlServer数据库则为sqlserver。
- db_address:数据库连接地址。如果数据库在本机则为127.0.0.1。
- db_name:数据库名,该数据库中需包含上一步初始化的两个数据表。
- db_username:数据库登录用户名。
- db_password:数据库登录密码。
启动jar包后,系统默认的服务地址为:
http://127.0.0.1:8080/ObjectLogger/
访问上述地址可以看到下面的欢迎界面:
至此,ObjectLogger系统已经搭建结束,可以接受业务系统的日志写入和查询操作。
3 业务系统接入
该部分讲解如何配置业务系统来将业务系统中的对象变化记录到ObjectLogger中。
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.objectLoggerClient,如:
- @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.objectLoggerClient">
- </context:component-scan>
3.3 完成配置
在application.properties中增加:
- object.logger.add.log.api=http://{ObjectLogger_address}/ObjectLogger/log/add
- object.logger.appName={your_app_name}
- object.logger.autoLog=true
- ObjectLogger_address:属性指向上一步的ObjectLogger的部署地址,例如:127.0.0.1:8080
- your_app_name:指当前业务系统的应用名。以便于区分日志来源,实现同时支持多个业务系统
- object.logger.autoLog:是否对对象的所有属性进行变更日志记录
至此,业务系统的配置完成。已经实现了和ObjectLogger的Server端的对接。
4 日志查询
系统运行后,可以通过/ObjectLogger/log/query查询系统中记录的日志,并通过传入参数对日志进行过滤。
通过这里,我们可以查询下一步中写入的日志。
5 日志写入
业务系统在任何需要进行日志记录的类中引入LogClient。例如:
- @Autowired
- private LogClient logClient;
5.1 简单使用
直接将对象的零个、一个、多个属性变化放入actionItemModelList中发出即可。actionItemModelList置为null则表示此次对象无需要记录的属性变动。例如,业务应用中调用:
- logClient.sendLogForItems("TaskModel",5,"actor name","addTask","add Task","via web page","some comments",null);
在ObjectLogger中使用如下查询条件:
- http://{your_ObjectLogger_address}/ObjectLogger/log/query?appName=myBootApp&objectName=TaskModel&objectId=5
查询到日志:
- {
- "respMsg": "成功",
- "respData": [
- {
- "id": 16,
- "appName": "myBootApp",
- "objectName": "TaskModel",
- "objectId": 5,
- "actor": "actor name",
- "action": "addTask",
- "actionName": "add Task",
- "extraWords": "via web page",
- "comment": "some comments",
- "actionTime": "2019-04-10T10:56:15.000+0000",
- "actionItemModelList": []
- }
- ],
- "respCode": "1000"
- }
5.2 对象变动自动记录
该功能可以自动完成新老对象的对比,并根据对比结果,将多个属性变动一起写入日志系统中。使用时,要确保传入的新老对象属于同一个类。
例如,业务系统这样调用:
- TaskModel oldTaskModel = new TaskModel();
- oldTaskModel.setId(9);
- oldTaskModel.setTaskName("oldName");
- oldTaskModel.setUserId(3);
- oldTaskModel.setDescription(" <p>the first line</p>
- " +
- " <p>the second line</p>
- " +
- " <p>the 3th line</p>");
- TaskModel newTaskModel = new TaskModel();
- newTaskModel.setId(9);
- newTaskModel.setTaskName("newName");
- newTaskModel.setUserId(5);
- newTaskModel.setDescription(" <p>the first line</p>
- " +
- " <p>the second line</p>
- " +
- " <p>the last line</p>");
- logClient.sendLogForObject(9,"actor name","editTask","edit Task","via app",
- "some comments",oldTaskModel,newTaskModel);
则我们可以使用下面查询条件:
- http://{your_ObjectLogger_address}/ObjectLogger/log/query?appName=myBootApp&objectName=TaskModel&objectId=9
查询到如下结果:
- {
- "respMsg": "成功",
- "respData": [
- {
- "id": 15,
- "appName": "myBootApp",
- "objectName": "TaskModel",
- "objectId": 9,
- "actor": "actor name",
- "action": "editTask",
- "actionName": "edit Task",
- "extraWords": "via app",
- "comment": "some comments",
- "actionTime": "2019-04-10T10:56:17.000+0000",
- "actionItemModelList": [
- {
- "id": 18,
- "actionId": 15,
- "attributeType": "NORMAL",
- "attribute": "taskName",
- "attributeName": "TASK",
- "oldValue": "oldName",
- "newValue": "newName",
- "diffValue": null
- },
- {
- "id": 19,
- "actionId": 15,
- "attributeType": "USERID",
- "attribute": "userId",
- "attributeName": "USER",
- "oldValue": "USER:3",
- "newValue": "USER:5",
- "diffValue": "diffValue"
- },
- {
- "id": 20,
- "actionId": 15,
- "attributeType": "TEXT",
- "attribute": "description",
- "attributeName": "DESCRIPTION",
- "oldValue": ""\t<p>the first line</p>\n\t<p>the second line</p>\n\t<p>the 3th line</p>"",
- "newValue": ""\t<p>the first line</p>\n\t<p>the second line</p>\n\t<p>the last line</p>"",
- "diffValue": "第6行变化:<br/> -:<del> the 3th line </del> <br/> +:<u> the last line </u> <br/>"
- }
- ]
- }
- ],
- "respCode": "1000"
- }
6 对象属性过滤
有些对象的属性的变动不需要进行日志记录,例如updateTime、hashCode等。ObjectLogger支持对对象的属性进行过滤,只追踪我们感兴趣的属性。
并且,对于每个属性我们可以更改其记录到ObjectLogger系统中的具体方式,例如修改命名等。
要想启用这个功能,首先将配置中的object.logger.autoLog改为false。
- object.logger.autoLog=false
然后在需要进行变化日志记录的属性上增加@LogTag注解。凡是没有增加该注解的属性在日志记录时会被自动跳过。
例如,注解配置如下则id字段的变动将被忽略。
- private Integer id;
- @LogTag(name = "TaskName")
- private String taskName;
- @LogTag(name = "UserId", extendedType = "userIdType")
- private int userId;
- @LogTag(name = "Description", builtinType = BuiltinTypeHandler.TEXT)
- private String description;
该注解属性介绍如下:
- name:必填,对应写入日志后的attributeName值。
- builtinType:ObjectLogger的内置类型,为BuiltinTypeHandler的值。默认为BuiltinTypeHandler.NORMAL。
- BuiltinTypeHandler.NORMAL:记录属性的新值和旧值,对比值为null
- BuiltinTypeHandler.TEXT: 用户富文本对比。记录属性值的新值和旧值,并将新旧值转化为纯文本后逐行对比差异,对比值中记录差异
- extendedType:扩展属性类型。使用ObjcetLogger时,用户可以扩展某些字段的处理方式。
7 属性处理扩展
很多情况下,用户希望能够自主决定某些对象属性的处理方式。例如,对于例子中Task对象的userId属性,用户可能想将其转化为姓名后存入日志系统,从而使得日志系统与userId完全解耦。
ObjectLogger完全支持这种情况,可以让用户自主决定某些属性的日志记录方式。要想实现这种功能,首先在需要进行扩展处理的属性上为@LogTag的extendedType属性赋予一个字符串值。例如:
- @LogTag(name = "UserId", extendedType = "userIdType")
- private int userId;
然后在业务系统中声明一个Bean继承BaseExtendedTypeHandler,作为自由扩展的钩子。代码如下:
- @Service
- public class ExtendedTypeHandler implements BaseExtendedTypeHandler {
- @Override
- public BaseActionItemModel handleAttributeChange(String attributeName, String logTagName, Object oldValue, Object newValue) {
- return null;
- }
- }
接下来,当ObjectLogger处理到该属性时,会将该属性的相关信息传入到扩展Bean的handleAttributeChange方法中,然后用户可以自行处理。传入的四个参数解释如下:
- extendedType:扩展类型值,即@LogTag注解的extendedType值。本示例中为userIdType。
- attributeName:属性名。本示例中为userId。
- logTagName:@LogTag注解的name值,可能为null。本示例中为UserId。
- oldValue:该属性的旧值。
- newValue:该属性的新值。
例如我们可以采用如下的方式处理userIdType属性:
- public BaseActionItemModel handleAttributeChange(String extendedType, String attributeName, String logTagName, Object oldValue, Object newValue) {
- BaseActionItemModel baseActionItemModel = new BaseActionItemModel();
- if (extendedType.equals("userIdType")) {
- baseActionItemModel.setOldValue("USER_" + oldValue);
- baseActionItemModel.setNewValue("USER_" + newValue);
- baseActionItemModel.setDiffValue(oldValue + "->" + newValue);
- }
- return baseActionItemModel;
- }
8 总结
怎么样,是不是ObjectLogger https://github.com/yeecode/ObjectLogger 的存在极大地方便了我们的日志记录操作。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
华为鸿蒙谣言四起 为您还原真实的鸿蒙OS
【大咖・来了 第7期】10月24日晚8点观看《智能导购对话机器人实践》 在这个世界上,有一种粉叫做“黑粉”,有一种杀叫做“捧杀”。在美国对华为进行全面封锁,美国高科技企业对华为施以断供的严峻形势下,华为祭出了鸿蒙系统予以反击。一时间,有人震惊了,有人沸腾了,更有甚者让13亿人民都落泪了…… 华为鸿蒙谣言四起 还有一些人,把华为跟爱国强制联系在一起,进行道德绑架。但相比之下,那些为了博取眼球骗流量的造谣者和传谣者更为可恨。今天我们就为您揭穿网络上那些关于华为鸿蒙的虚假谣言,为您还原一个真实的鸿蒙OS。 目前网络上流传最广的就是一张关于小米手机推送鸿蒙系统的截图,相信很多网友多曾经在社交媒体上看到过。其实这个谣言目前已经得到官方辟谣,这个是利用了小米的丢失找回功能,上面的文字也是用户可以随意输入的“找回信息”。 流传最广的小米推送鸿蒙系统谣言 这个谣言利用了民众的爱国热情,以及很多人对小米手机系统不熟悉的漏洞。其实就算是小米用户,相信很多人也不知道会有这样的一个“找回模式”可以输入文本,因此便在网络上大量疯传。不过“激活装备”和那个非400的电话足以令人怀疑。 其实任何谣言大部分都有破绽可...
- 下一篇
架构成长之路:分布式系统如何设计,看看Elasticsearch是怎么做的
【大咖・来了 第7期】10月24日晚8点观看《智能导购对话机器人实践》 分布式系统类型多,涉及面非常广,不同类型的系统有不同的特点,批量计算和实时计算就差别非常大。这篇文章中,重点会讨论下分布式数据系统的设计,比如分布式存储系统,分布式搜索系统,分布式分析系统等。 我们先来简单看下Elasticsearch的架构。 Elasticsearch 集群架构 Elasticsearch是一个非常著名的开源搜索和分析系统,目前被广泛应用于互联网多种领域中,尤其是以下三个领域特别突出。一是搜索领域,相对于solr,真正的后起之秀,成为很多搜索系统的不二之选。二是Json文档数据库,相对于MongoDB,读写性能更佳,而且支持更丰富的地理位置查询以及数字、文本的混合查询等。三是时序数据分析处理,目前是日志处理、监控数据的存储、分析和可视化方面做得非常好,可以说是该领域的***者了。 Elasticsearch的详细介绍可以到官网查看。我们先来看一下Elasticsearch中几个关键概念: 节点(Node):物理概念,一个运行的Elasticearch实例,一般是一台机器上的一个进程。 索引(In...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Hadoop3单机部署,实现最简伪集群
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果