您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

如何快速搭建日志系统

时间:2019-10-15 09:43:52  来源:  作者:

我们开发的业务系统通常会提供给很多人使用,那在使用的过程中,日志系统变得非常重要。

日志系统记录的用户行为有以下的作用:

  • 从系统用户角度看:它展示了用户自身的操作历史和具体对象的变动历史,便于用户进行梳理
  • 从系统管理员角度看:它可以记录了所有用户操作,便于我们定位异常行为

例如,在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 总结

怎么样,是不是有了这一套系统之后,再负责的日志系统都变得简单起来。快收藏起来当作自己的秘技吧!


易哥,高级软件架构师、网络工程师、数据库工程师、注册电气工程师。现从事软件架构架构设计工作。

分享架构师知识! 欢迎关注我们,不错过每期的原创干货!



Tags:日志系统   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
Grafana Loki 是一个日志聚合工具,它是功能齐全的日志堆栈的核心。图片来自 包图网先看看结果有多轻量吧: Loki 是一个为有效保存日志数据而优化的数据存储。日志数据的高效索...【详细内容】
2021-09-14  Tags: 日志系统  点击:(97)  评论:(0)  加入收藏
一、日志收集的需求背景:&bull; 业务发展越来越庞大,服务器越来越多​ &bull; 各种访问日志、应用日志、错误日志量越来越多​ &bull; 开发人员排查问题,需要到服务器上查日志,...【详细内容】
2021-03-05  Tags: 日志系统  点击:(189)  评论:(0)  加入收藏
一、系统日志日志在所有应用系统中都占有极其重要的位置,本文主要介绍Linux系统内核的日志,另外还介绍了一些日志分析工具。1. 说明看Linux系统信息日志主要的两种途径: dmesg,...【详细内容】
2020-08-06  Tags: 日志系统  点击:(160)  评论:(0)  加入收藏
一、系统日志日志在所有应用系统中都占有极其重要的位置,本文主要介绍Linux系统内核的日志,另外还介绍了一些日志分析工具。1. 说明看Linux系统信息日志主要的两种途径: dmesg,...【详细内容】
2020-08-04  Tags: 日志系统  点击:(110)  评论:(0)  加入收藏
CAL(Central Application Logging)系统主要负责收集和处理 eBay 内部各个应用程序池的日志,日处理超过 3PB 的数据,供运维团队和开发团队日常监控使用。CAL 系统通 过 HTTP 接...【详细内容】
2020-06-28  Tags: 日志系统  点击:(77)  评论:(0)  加入收藏
最近,在对公司容器云的日志方案进行设计的时候,发现主流的ELK或者EFK比较重,再加上现阶段对于ES复杂的搜索功能很多都用不上最终选择了Grafana开源的Loki日志系统,下面介绍下Lok...【详细内容】
2020-06-15  Tags: 日志系统  点击:(88)  评论:(0)  加入收藏
MySQL 8.0中一个重要的新特性是对Redo Log子系统的重构,通过引入两个新的数据结构recent_written和recent_closed,移除了之前的两个热点锁:log_sys_t::mutex和log_sys_t::flush_order_mutex。...【详细内容】
2020-01-21  Tags: 日志系统  点击:(53)  评论:(0)  加入收藏
我们开发的业务系统通常会提供给很多人使用,那在使用的过程中,日志系统变得非常重要。日志系统记录的用户行为有以下的作用: 从系统用户角度看:它展示了用户自身的操作历史和具...【详细内容】
2019-10-15  Tags: 日志系统  点击:(97)  评论:(0)  加入收藏
日志是记录系统中各种问题信息的关键,也是一种常见的海量数据。 日志平台为集团所有业务系统提供日志采集、消费、分析、存储、索引和查询的一站式日志服务。主要为了解决...【详细内容】
2019-06-18  Tags: 日志系统  点击:(523)  评论:(0)  加入收藏
▌简易百科推荐
本文分为三个等级自顶向下地分析了glibc中内存分配与回收的过程。本文不过度关注细节,因此只是分别从arena层次、bin层次、chunk层次进行图解,而不涉及有关指针的具体操作。前...【详细内容】
2021-12-28  linux技术栈    Tags:glibc   点击:(3)  评论:(0)  加入收藏
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(2)  评论:(0)  加入收藏
程序是如何被执行的&emsp;&emsp;程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(9)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(8)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(7)  评论:(0)  加入收藏
今天想简单聊一聊 Token 的 Value Capture,就是币的价值问题。首先说明啊,这个话题包含的内容非常之光,Token 的经济学设计也可以包含诸多问题,所以几乎不可能把这个问题说的清...【详细内容】
2021-12-21  唐少华TSH    Tags:Token   点击:(10)  评论:(0)  加入收藏
实现效果:假如有10条数据,分组展示,默认在当前页面展示4个,点击换一批,从第5个开始继续展示,到最后一组,再重新返回到第一组 data() { return { qList: [], //处理后...【详细内容】
2021-12-17  Mason程    Tags:VUE   点击:(14)  评论:(0)  加入收藏
什么是性能调优?(what) 为什么需要性能调优?(why) 什么时候需要性能调优?(when) 什么地方需要性能调优?(where) 什么时候来进行性能调优?(who) 怎么样进行性能调优?(How) 硬件配...【详细内容】
2021-12-16  软件测试小p    Tags:性能调优   点击:(20)  评论:(0)  加入收藏
Tasker 是一款适用于 Android 设备的高级自动化应用,它可以通过脚本让重复性的操作自动运行,提高效率。 不知道从哪里听说的抖音 app 会导致 OLED 屏幕烧屏。于是就现学现卖,自...【详细内容】
2021-12-15  ITBang    Tags:抖音防烧屏   点击:(25)  评论:(0)  加入收藏
11 月 23 日,Rust Moderation Team(审核团队)在 GitHub 上发布了辞职公告,即刻生效。根据公告,审核团队集体辞职是为了抗议 Rust 核心团队(Core team)在执行社区行为准则和标准上...【详细内容】
2021-12-15  InfoQ    Tags:Rust   点击:(25)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条