随着互联网、IT、大数据等技术的爆发式发展,企业系统产生的大量爆发数据。对于保存在数据库中的业务数据,可以通过DBus数据总线+Wormhole流式处理平台的日志方式实时地无侵入同步和落地到任意sink端,提供下游系统分析使用;对于业务系统产生的日志数据,这些包含了业务高低峰、用户轨迹、系统异常/错误信息、调用链等诸多信息,也蕴含着无价的宝藏。一些公司通过埋点等方式和手段,往日志数据里输出他们想要监控和跟踪的信息,以便提供客观的数据支撑,做出更高效、更准确的决策。
DBus-https://github.com/bridata/dbus
Wormhole-https://github.com/edp963/wormhole
在这种背景下,各种各样的日志收集、结构化、分析工具如雨后春笋般出现,业界目前已有不少发展得比较成熟的方案,例如:Logstash、Filebeat、Flume、Fluentd、Chukwa. scribe、Splunk等,更有整合了日志采集、转换分析和展示的ELK集成方案。这些方案各有所长。在结构化日志这个方面,大多采用配置正则表达的方式:用于提取日志中模式比较固定、通用的部分,例如日志时间、日志类型、行号等。对于真正的和业务比较相关的信息,我们权且称之为message部分,原因主要是这部分千变万化,很难有一个特定、通用的模式来一劳永逸地描述及囊括所有情形。然而,这部分内容对于企业来说,比前面相对固定的部分更有价值。
本文就是介绍一种通过简单、可视化、所见即所得即席验证的方式,为用户提供高效、灵活地采集和加工利用数据日志的工具。
常见的日志处理方案
目前业界常见的日志分析代表为ELK:
-
通过Logstash实时收集和转换数据,保存到Eleaticsearch中,通过Kibana进行展示。
-
或者通过Filebeat 作为前端抓取数据,Logstash进行数据转换,保存到Eleaticsearch中,通过Kibana进行展示。
结构化日志的原理
DBUS设计的数据日志同步方案如下:
-
日志抓取端采用业界流行的组件(例如Logstash、Flume、Filebeat等)。一方面便于用户和业界统一标准,方便用户的整合;另一方面也避免无谓的重造轮子。抓取数据称为原始数据日志(raw data log)放进Kafka中,等待处理。
-
提供可视化界面,配置规则来结构化日志。用户可配置日志来源和目标。同一个日志来源可以输出到多个目标。每一条“日志源-目标”线,中间数据经过的规则处理用户根据自己的需求来自由定义。最终输出的数据是结构化的,即:有schema约束,可以理解为类似数据库中的表。
-
所谓规则,在DBUS中,即“规则算子”。DBUS设计了丰富易用的过滤、拆分、合并、替换等算子供用户使用。用户对数据的处理可分多个步骤进行,每个步骤的数据处理结果可即时查看、验证;可重复使用不同算子,直到转换、裁剪得到自己需要的数据。
-
将配置好的规则算子组运用到执行引擎中,对目标日志数据进行预处理,形成结构化数据,输出到Kafka,供下游数据使用方使用。
系统流程图如下所示:
根据配置,我们支持同一条原始日志,能提取为一个表数据,或者可以提取为多个表数据。
每个表是结构化的,满足相同的schema。
-
每个表是一个规则 算子组的合集,可以配置1个到多个规则算子组
-
每个规则算子组,由一组规则算子组合而成
拿到一条原始数据日志, 它最终应该属于哪张表呢?
每条日志需要与规则算子组进行匹配:
-
符合条件的进入规则算子组的,最终被规则组转换为结构化的表数据。
-
不符合的尝试下一个规则算子组。
-
都不符合的,进入unknown_table表。
规则算子
规则算子是对数据进行过滤、加工、转换的基本单元。常见的规则算子如下:
算子之间是独立的,通过组合不同的算子达到更复杂的功能,对算子进行迭代使用最终达到对任意数据进行加工的目的。
我们试图使得算子尽量满足正交性或易用性(虽然正则表达式很强大,但我们仍然开发一些简单算子例如trim算子来完成简单功能,以满足易用性),算子的开发也可以随意扩充,比如可以开发提取JSON节点值或XML节点值的算子。算子的开发也很容易,只要遵循基本接口原则,就可以开发任意自定义算子。
结构化日志的例子
下面以提取heart_beat_status表为例子进行配置说明,整个数据日志结构化过程如下:
使用通用抓取端抓取原始日志
使用Logstash 读取log4j文件作为数据输入源,输出到Kafka中(这里具体配置就不说了,可参考Logstash配置)
这里抓取端不限制,你可以使用Flume、Filebeat,甚至你自己写的端,只要输出到Kafka中就可以。
使用可视化方式进行规则算子的配置
首先配置一个输出表的规则组, 查看heartbeat_log_new这个topic中,我们直接实时的可视化操作配置。
1、读取原始数据日志
可以看到由Logstash 预先提取已经包含了log4j的基本信息,例如path、@timestamp、level等。但是数据日志的详细信息在字段log中。由于不同的数据日志输出是不一样的,因此可以看到log列数据是同的。
(需要指出的是:使用logstash预先提取其它列是可选的,其实对于Flume这样直接抓取到的就是raw data log,这对我们后面的提取没有影响)
2、提取感兴趣列
例如我们提取timestamp、log 原始信息等,可以添加一个toindex算子,提取感兴趣的字段,如下:
这里需要指出,我们考虑使用数组下标方式,是有原因的:
-
并不是所有列天生就有列名(例如flume抽取的原始数据,或者split算子处理后的数据列);
-
下表方式对可以使用数组方式指定列(类似python方式, 例如:1:3 表示1,2,3列);
因此后续操作全部基于数组下标方式访问。
特别说明一下:如下?号所在的地方,鼠标移上去就会显示一个在线帮助,告诉你这个算子怎么使用,每个算子怎么用不需要记。
执行一下,就可以看到被提取后的字段情况:
3、过滤不相关数据
在这个例子中,我们只对插入心跳包的数据感兴趣。因此添加一个filter算子,对第2列进行过滤”插入心跳包”:
执行后,不符合条件的日志行就被过滤了。
4、以切分方式进行提取
添加一个split算子,我们对“数据源”,“插入心跳包”,还有后面的“node”进行切分。
可以看到切分后,原来的第1列变新的1,2,3,4,5列了。
特别需要说明的是:提取的方式非常多,Split只是一种常见方式。我们可以substring提取,replace掉不需要的数据等,我们还也可以配置正则表示式提取。我们不积极推荐用正则表示的原因是正则表达式很容易写错,并不是最简单可视化的选择。
5、以trim方式出来数据
我们想提取4列的值,使用trim算子进行过滤掉不需要的数据。
执行后,这样新的5列就拿到干净的值。
6、选择输出列
最后我们把感兴趣的列输出,使用saveAs算子进行输出, 指定列名和类型。
执行后,这就是处理好的最终输出数据样本。
查看结构化输出结果
保存上一步配置好的规则组,运用到DBus执行算子引擎,就可以生成相应的结构化数据了。目前根据项目实际,DBus输出的数据是UMS格式。UMS是DBus开源项目(https://github.com/bridata/dbus)定义并使用的,通用的数据交换格式,是标准的JSON。其中同时包含了schema和数据信息。更多UMS介绍请参考DBus开源项目主页的介绍。
输出结果的数据格式和结构,不想使用UMS的,可经过简单的开发,实现定制化。
以下是测试案例,输出的结构化UMS数据的例子:
{
"payload": [
{
"tuple": [
"127046516736228867",
"2017-12-17 13:57:30.000",
"i",
"320171788",
"2017/12/17 13:57:30.877",
"edpdb",
"成功",
"/DBus/HeartBeat/Monitor/edpdb/TEST1/T1000"
]
},
{
"tuple": [
"127046516736228869",
"2017-12-17 13:57:30.000",
"i",
"320171790",
"2017/12/17 13:57:30.946",
"edpdb",
"成功",
"/DBus/HeartBeat/Monitor/edpdb/TEST4/ONEYI"
]
},
{
"tuple": [
"127046520930532871",
"2017-12-17 13:57:31.000",
"i",
"320171792",
"2017/12/17 13:57:31.026",
"edpdb",
"成功",
"/DBus/HeartBeat/Monitor/edpdb/TEST3/USER_REGISTER"
]
}
],
"protocol": {
"type": "data_increment_data",
"version": "1.3"
},
"schema": {
"batchId": 0,
"fields": [
{
"encoded": false,
"name": "ums_id_",
"nullable": false,
"type": "long"
},
{
"encoded": false,
"name": "ums_ts_",
"nullable": false,
"type": "datetime"
},
{
"encoded": false,
"name": "ums_op_",
"nullable": false,
"type": "string"
},
{
"encoded": false,
"name": "ums_uid_",
"nullable": false,
"type": "string"
},
{
"encoded": false,
"name": "event_time",
"nullable": false,
"type": "datetime"
},
{
"encoded": false,
"name": "datasource",
"nullable": false,
"type": "string"
},
{
"encoded": false,
"name": "heartbeat_state",
"nullable": false,
"type": "string"
},
{
"encoded": false,
"name": "heartbeat_node",
"nullable": false,
"type": "string"
}
],
"namespace": "heartbeat_log.heartbeat_log_schema.heartbeat_table.3.host1.0"
}
}
查看监控
为了便于掌握数据抽取及规则匹配等情况,我们提供了日志数据提取的可视化实时监控界面,如下图所示,可随时了解:
-
实时数据条数
-
错误条数情况(错误条数是指:执行算子时出现错误的情况,帮助发现算子与数据是否匹配,用于修改算子)
-
数据延时情况
监控中还有一张表叫做__unkown_table__ 表明所有没有被匹配上的数据条数。例如:logstash抓取的日志中有5种不同模式的数据,我们只捕获了其中3种模式,其它没有被匹配上的数据,全部在_unkown_table_计数中。
总结
DBus日志同步方案总结如下:
1、集成整合现有日志抓取工具(Flume、filebeat、logstash等),方便用户接入及整合;
2、DBUS提供了丰富的算子及可视化配置界面,供用户结构化数据日志使用。
-
通过可视化的,自由地使用各种规则算子对日志进行处理,生成结构化的数据日志;
-
每个算子配置过程都可以看到原始数据和加工后的数据;
-
算子可以随意增减,处理顺序可以随意调换,每个步骤的算子相互叠加使用;
-
用户也可以扩展开发自己需要的算子;
-
提供源到目标一对多的配置,让用户想怎么玩就怎么玩,从而可从任意多个角度挖掘数据价值;
-
监控让用户更直观地了解到数据结构化实时转换的情况,一切尽在掌控;
3、将原始数据日志转换为结构化数据,输出到kafka中提供给下游数据使用费进行使用。
最终使得日志业务数据结构化的过程,变得简单、可视化、配置集中化, 使得大家都能轻松地玩转日志数据。
本文提到的日志结构化方案计划于2018年1月发布到开源项目DBus 0.4版本中,项目地址https://github.com/bridata/dbus
系统架构和工作原理
DBUS主要分为两个部分:貼源数据采集和多租户数据分发。两个部分之间以Kafka为媒介进行衔接。无多租户资源、数据隔离需求的用户,可以直接消费源端数据采集这一级输出到kafka的数据,无需再配置多租户数据分发。
1 DBUS源端数据采集
DBUS源端数据采集大体来说分为2部分:
- 读取RDBMS增量日志的方式来 实时获取增量数据日志,并支持全量拉取;
- 基于logtash,flume,filebeat等抓取工具来实时获得数据,以可视化的方式对数据进行结构化输出;
主要模块如下:
- 日志抓取模块:从RDBMS的备库中读取增量日志,并实时同步到kafka中;
- 增量转换模块:将增量数据实时转换为UMS数据,处理schema变更,脱敏等;
- 全量抽取程序:将全量数据从RDBMS备库拉取并转换为UMS数据;
- 日志算子处理模块:将来自不同抓取端的日志数据按照算子规则进行结构化处理;
- 心跳监控模块:对于RDMS类源,定时向源端发送心跳数据,并在末端进行监控,发送预警通知;对于日志类,直接在末端监控预警。
- web管理模块:管理所有相关模块。
2 多租户数据分发
对于不同租户对不同源端数据有不同访问权限、脱敏需求的情形,需要引入Router分发模块,将源端貼源数据,根据配置好的权限、用户有权获取的源端表、不同脱敏规则等,分发到分配给租户的Topic。这一级的引入,在DBUS管理系统中,涉及到用户管理、Sink管理、资源分配、脱敏配置等。不同项目消费分配给他的topic。
主要功能:
- 无侵入方式接入多种数据源: 业务系统无需任何修改,以无侵入性读取数据库系统的日志获得增量数据实时变化。目前RDBMS支持mysql,oracle数据源(Oracle数据源请参考Oracle相关协议), 日志方面支持基于logstash,flume和filebeat的多种数据日志抽取方案。
- 海量数据实时传输: 使用基于Storm的流式计算框架,秒级延时,整体无单点保证高可用性。
- 多租户支持: 提供用户管理、资源分配、Topology管理、租户表管理等丰富的功能,可根据需求,为不同租户分配不同的源端表数据访问权限,应用不同的脱敏规则,从而实现多租户资源隔离、差异化数据安全。
-
感知源端schema变更: 当源端发生schema变更时,能自动感知schema变化,调整UMS版本号,并通过Kafka消息和邮件通知下游
-
数据实时脱敏: 可根据需求对指定列数据进行实时脱敏。脱敏策略包括:直接替换、MD5、murmur等脱敏算法,脱敏加盐,正则表达式替换等。支持用户开发jar包实现DBUS未覆盖的个性化脱敏策略。
-
统一标准化消息传输协议: 使用统一的UMS(JSON格式)消息schema格式输出便于消费,提供数据线级ums_id保证数据顺序性,输出insert,Update(before/after),Delete event数据。
-
可靠多路消息订阅分发: 使用Kafka存储和传递消息保证可靠性和便捷的多用户订阅
-
支持分区表/系列表数据汇集: 支持分区表的数据汇集到一个“逻辑表” 。也可将用户自定义的系列表数据汇集到一个“逻辑表“。例:
-
实时监控&预警: 可视化监控系统能随时查看各数据线实时流量和延时状况;当数据线发生异常时,根据配置策略自动发邮件或短信通知相关负责人
Wormhole Architecture
设计理念
- 统一 DAG 高阶分形抽象
- 构建由 Source DataSys,Kafka Topic,Spark Stream(Flink Stream),Sink DataSys 组成的物理 DAG
- 每个物理 DAG 里可以并行处理多个由 Source Namespace,Flow,Sink Namespace 组成的逻辑 DAG
- 每个 Flow 本身是典型的 Spark RDD DAG
- 统一通用流消息 UMS 协议抽象
- UMS 是 Wormhole 定义的流消息协议规范
- UMS 试图抽象统一所有结构化消息
- UMS 自身携带结构化数据 Schema 信息
- Wh4 支持用户自定义半结构化 JSON 格式
- 统一数据逻辑表命名空间 Namespace 抽象
- Namespace 唯一定位所有数据存储所有结构化逻辑表
- [Data System].[Instance].[Database].[Table].[Table Version].[Database Partition].[Table Partition]
主要特性
- 支持可视化,配置化,SQL 化开发实施流式项目
- 支持指令式动态流式处理的管理,运维,诊断和监控
- 支持统一结构化 UMS 消息和自定义半结构化 JSON 消息
- 支持处理增删改三态事件消息流
- 支持单个物理流同时并行处理多个逻辑业务流
- 支持流上 Lookup Anywhere,Pushdown Anywhere
- 支持基于业务策略的事件时间戳流式处理
- 支持 UDF 的注册管理和动态加载
- 支持多目标数据系统的并发幂等入库
- 支持多级基于增量消息的数据质量管理
- 支持基于增量消息的流式处理和批量处理
- 支持 Lambda 架构和 Kappa 架构
- 支持与三方系统无缝集成,可作为三方系统的流控引擎
- 支持私有云部署,安全权限管控和多租户资源管理