日志聚类算法 Drain 的实践与改良
在现实场景中,业务程序输出的日志往往规模庞大并且类型纷繁复杂。我们在查询和查看这些日志时,平铺的日志列表会让我们目不暇接,难以快速聚焦找到重要的日志条目。
在观测云中,我们在日志页面提供了聚类分析功能,可以快速聚合相似的日志文本,帮助你全览不同类型的日志。这个功能背后就由基于 Drain 的改良算法驱动。

快速理解 Drain 算法
根据 logparser 项目提供的 Benchmark 数据和论文原文可知,Drain 在一众日志聚类算法中准确度和性能都几乎是最好的,所以我们选择基于 Drain 算法来实现产品功能。

在这里我们尝试根据 Drain 论文来总结和梳理一下算法的主要逻辑:

1、首先对日志进行业务预处理,将一些常见的日志模式替换为占位符,比如时间、用户 ID、IP 等
2、对预处理的数据进行分词,并在 Drain 的搜索树的第一层找到对应单词数量的子节点
3、再逐个根据日志中的单词序列,在下层的前缀搜索树上找到对应的日志聚类桶
4、遍历对应前缀树指向的日志聚类桶,分别判断当前日志跟对应日志类的相似度是否达到阈值
- 相似度算法:两条日志从左往右,一个一个单词看相不相同,统计相同单词的数量,除以日志长度就得到相似度
5、如果相似则将当前的日志加入该类别,不相似则创建新的类别并加入到前缀树中
我们回顾一下上述的处理流程:
1、占位预处理的目标是为了将日志中最常见的变量替换为相同的占位符,来提升相同单词的比例,以提升最终文本的相似度
2、Drain 第一层的按照单词数区分的子树和后续根据前缀树拆分的子树目标都是缩小相似度计算的开销,我们只用跟最可能相似的日志组来对比计算
相信到这里,你已经基本理解了 Drain 算法的基本设计思路。这个算法思路总体并不复杂,固定深度的查找树、简单的相似度匹配算法都让算法的运行非常高效。
改良 1:占位处理后置,并提升处理效率
我们在最初的使用中按照论文的描述给日志增加了一些常见的日志模式占位符,比如:
- 时间:
[0-9]{1,}:[0-9]{1,}:[0-9]{1,}.?[0-9]{1,}? - IPv4:
\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3} - Hex:
0x[a-f0-9A-F]+ - 数字:
-?[0-9]{1,}.?[0-9]* - ID:
[0-9a-f]{4,}
众所周知,时间在日志中输出的格式是可能有非常多种类型的,我这里只添加了最简单的一种,还有 IPv6 因为正则表达式的规则过于复杂也还没加。
在初步添加上述少量的占位符匹配之后,我们就发现 Drain 算法的匹配效率显著下降。
在我们内部一个 1w 行的测试数据集上,完成训练的时间由 80ms 增长到 2.2s,匹配效率降低约 30 倍,通过 CPU Profiler 调用树可以观察到性能的主要开销都在正则表达式的替换上。

那么不进行占位处理可以吗?不可以,这些变量会严重影响日志相似度的判定。
那么可以换更高效的正则表达式库吗?我们尝试了一些常见的优化方案,但提升的幅度没有这么显著。
我们使用多个正则表达式对全部日志的每个单词都进行了一遍正则的匹配,这个的开销自然是不低的。那这个完整的匹配是必须的吗?哪些路径的正则匹配是必须的?
前面我们已经强调过,我们期望的通过占位处理之后不影响日志相似度的判断。那么我们其实只需要在日志相似度运算的时候对必要的单词来做这个正则匹配就好了。
假设当前日志组的模板变量是:Just A Log Template <Number> ,现在我们有一个日志 Just A Log Template 123 需要跟这个模板对比相似度,我们在逐个单词进行对比相似度时,只需要判断日志的最后一个单词是否匹配中 <Number> 占位符对应的正则表达式,我们不用判断这个单词是否能匹配中其他的占位符,也不用关注其他的单词是什么情况,更不必要提前对这个日志进行完整占位处理。
Drain 本身通过多层的分桶降低了我们日志需要判断的聚类组的数量,另外我们根据日志组的模板变量确定了需要进行哪种占位符的判断,所以实际的匹配开销经过这两重剪枝就大大降低了。在刚才提到的数据集下,我们把占位符的匹配后置,完成训练的时间就降低到只有 120ms 左右了。
改良 2:提升占位处理通用性
在通过剪枝解决了匹配效率问题之后我们还关注到当前的占位处理的逻辑其实不太通用,IPv4、IPv6、超多种时间格式、用户自行输出的结构体、JSON 文本等等其实结构非常复杂,根据不同的使用习惯不同也难以罗列完整,维护也比较复杂。
我们最终的方案是尝试将常见的符号比如 :.," 等前后添加空格,在分词时就可以把这些符号拆分成单独的单词,这样比如一个 IPv4 的地址会被拆分为 <Number>:<Number>:<Number>:<Number> ,一个 IPv6 地址会被拆分为 <ID> 和 : 子元素,时间格式会被拆分为数个 <Number> 和 : 。
按这种逻辑,我们最终只维护了三个基本的占位元素,分别是:
- 数字:
[-+]?[0-9]+ - Hex:
0x[a-f0-9A-F]+ - ID:
[a-f0-9A-F]{4,}
这样除了维护更简单,常见的占位格式都不用维护,而且对 JSON 文本的支持也更友好了,可以将其中的 KV 都完全拆分。
改良 3:支持变长日志聚类
回忆一下 Drain 的处理流程,我们在知道日志长度的时候就直接划分子树了。那么显而易见的,Drain 对变长的日志支持效果都不会太好。
举个实际点的例子,两个包含 UA 的请求日志:
1、[28/Aug/2022:07:01:36 +0800] "GET / HTTP/1.1" 200 "Mozilla/5.0 (Linux; Android 12; PEEM00 Build/RKQ1.211119.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/97.0.4692.98 Mobile Safari/537.36"
2、[28/Aug/2022:07:50:18 +0800] "GET / HTTP/1.1" 200 "Mozilla/5.0 (Linux; Android 9; MI 8 Build/PQ3A.190801.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Mobile Safari/537.36"
这两条日志的长度是不一样的,一个的手机型号是 PEEM00 一个的手机型号是 MI 8 。所以虽然两个日志非常相像,但还是不会被聚合到同一个类别中。
这合理吗?显然是不合理的。
所以我们给 Drain 额外增加了一个相邻长度聚类组的相似度判断逻辑。同时要注意到,之前我们的相似度判断算法是逐个单词匹配的,当这里单词数不一样的时候,中间可能会错位,导致相似度计算可能会很严重偏低,我们原有的算法已经不太适用这种情况了。
常见的文本相似度算法都不限定两个文本单词长度必须相等,比如计算欧几里得距离、曼哈顿距离、明可夫斯基距离、余弦相似度等。这里我们最终选用了 minhash 算法,我们可以提前对每个日志组计算好特征向量,在判断匹配时给日志行计算雅卡尔距离时可以更高效。
同时要注意的是,如果你的数据集是请求日志这样的数据,单词的长度可能都差不多,并且不同的日志组本身的相似度其实已经很高了。这样的数据集下,你不能只优先匹配相邻长度的日志组,应该优先把前后一定长度范围的全部日志组放在一个大池子里来找到最优解。
总结
经过我们对 Drain 的深入剖析和改良,新算法的流程是这样的:
1、直接对日志文本进行分词,并在 Drain 的搜索树的第一层找到对应单词数量的子节点
2、再逐个根据日志中的单词序列,在下层的前缀搜索树上找到对应的日志聚类桶
3、遍历对应前缀树指向的日志聚类桶,分别判断当前日志跟对应日志类的相似度是否达到阈值
- 相似度算法:两条日志从左往右依次对比,如果类别模板中对应位置是占位符,则使用占位符对应的正则表达式匹配原始文本,跟原有算法一样计算相似度
4、如果相似则将当前的日志加入该类别,不相似则拿出来前后一定范围长度的全部日志类别,对比这些日志于当前的日志的 minhash 的笛卡尔距离,判断相似度是否达到阈值
5、如果前面两种相似度判断都失败,则创建新的日志分类,此时注意需要对现有的日志进行完整的占位符替换,最后将创建的新的日志类别加入前缀搜索树中
经过如上的处理流程,该算法对于定长、变长、JSON 等格式的日志聚合表现相比于原版 Drain 都更加优异,同时也能保持极高的匹配效率。
相关文章:
日志聚类算法 Drain 的实践与改良
在现实场景中,业务程序输出的日志往往规模庞大并且类型纷繁复杂。我们在查询和查看这些日志时,平铺的日志列表会让我们目不暇接,难以快速聚焦找到重要的日志条目。 在观测云中,我们在日志页面提供了聚类分析功能,可以…...
如何让用户在网页中填写PDF表格?
在网页中让用户直接填写PDF表格,可以大大简化填写、打印、扫描和提交表单的流程。通过使用复选框、按钮和列表等交互元素,PDF表格不仅让填写过程更高效,还能方便地在电脑或移动设备上访问和提交数据。 以下是在浏览器中显示可填写PDF表单的四…...
GXUOJ-算法-补题:22级《算法设计与分析》第一次课堂练习
2.最大子数组和 问题描述 代码解答 #include<bits/stdc.h> using namespace std; const int N1005; int sum,n,a[N]; int res-1;int result(){for(int i0;i<n;i){if(sum<0) suma[i];else{suma[i];resmax(res,sum);}}return res; } int main(){cin>>n;for(i…...
源代码编译安装X11及相关库、vim,配置vim(3)
一、vim插件安装 首先安装插件管理器Vundle ()。参照官网流程即可。vim的插件管理器有多个,只用Vundle就够了。然后~/.vimrc里写上要安装的插件: filetype offset rtp~/.vim/bundle/Vundle.vim call vundle#begin() Plugin VundleVim/Vundle.vim Plugin powerline…...
uniapp 微信小程序 自定义日历组件
效果图 功能:可以记录当天是否有某些任务或者某些记录 具体使用: 子组件代码 <template><view class"Accumulate"><view class"bx"><view class"bxx"><view class"plank"><…...
EdgeX规则引擎eKuiper
EdgeX 规则引擎eKuiper 一、架构设计 LF Edge eKuiper 是物联网数据分析和流式计算引擎。它是一个通用的边缘计算服务或中间件,为资源有限的边缘网关或设备而设计。 eKuiper 采用 Go 语言编写,其架构如下图所示: eKuiper 是 Golang 实现的轻量级物联网边缘分析、流式处理开源…...
react 优化方案
更详细的 React 优化方案可以分为性能优化、代码结构优化、开发效率提升等多个方面,结合实际项目需求,逐步应用这些优化策略。 一、性能优化 1. 避免不必要的重新渲染 React.memo: 缓存组件,防止组件在父组件重新渲染时无意义的重新渲染。 const ChildComponent = Reac…...
【Linux】sed编辑器
一、基本介绍 sed编辑器也叫流编辑器(stream editor),它是根据事先设计好得一组规则编辑数据流。 交互式文本编辑器(如Vim)中,可以用键盘命令交互式地插入、删除或替换文本数据。 sed编辑器是根据命令处理…...
(leetcode算法题)137. 只出现一次的数字 II
处理这种数据集中只有一个数出现的频次为1,其他数出现的频次均为k的题目 往往都是使用位运算的进行求解 假设 target在数据集中只出现了1次,其他数据n1, ... nj都出现了 k 次, 考虑数据集中所有数据的第 i 位的取值,那么将会有…...
在大数据环境下高效运用NoSQL与关系型数据库的结合策略
在大数据环境下,高效运用NoSQL与关系型数据库结合策略涉及到理解两者各自的优劣势,以及如何有效地整合它们。以下是一些代码示例和实际案例,以帮助你了解这种结合策略。 背景介绍 NoSQL数据库通常用于处理大量非结构化或半结构化的数据&…...
C语言——分支与循环语句
目录 一.分支语句 1.if语句 2.悬空else问题 3.switch语句 default子句 二.循环语句 1.while循环 whle循环流程图: break与continue 2.for循环 2.2for与while循环 2.3关于for循环的一道笔试题 3.do while 循环 三.猜数字游戏实现 四.goto语句 补充 …...
下载b站高清视频
需要使用的edge上的一个扩展插件,所以选择使用edge浏览器。 1、在edge浏览器上下载 强力视频下载合并 扩展插件 2、在edge上打开b站,登录自己账号(登录后才能下载到高清!!)。打开一个视频,选择自…...
常见 JVM垃圾回收器、内存分配策略、JVM调优
垃圾收集( Garbage Collection ,下文简称 GC),垃圾收集的历史远远比 Java久远。经过半个世纪的发展,今天的内存动态分配与内存回收技术已经相当成熟,一切看起来都进入了“自动化”时代,那为什么…...
【HarmonyOS应用开发——ArkTS语言】欢迎界面(启动加载页)的实现【合集】
目录 😋环境配置:华为HarmonyOS开发者 📺演示效果: 📖实验步骤及方法: 一、在media文件夹中添加想要使用的图片素材 二、在entry/src/main/ets/page目录下创建Welcome.ets文件 1. 整体结构与组件声…...
【MySQL】:Linux 环境下 MySQL 使用全攻略
📃个人主页:island1314 🔥个人专栏:MySQL学习 ⛺️ 欢迎关注:👍点赞 👂🏽留言 😍收藏 💞 💞 💞 1. 背景 🚀 世界上主…...
Linux驱动开发 gpio_get_value读取输出io的电平返回值一直为0的问题
当时gpio子系统进行读取时返回必定是0 因此,首先必须使用platform驱动来管理gpio和pinctrl子系统,然后如果按照正点原子所教的设备树引脚设置为0x10B0则会导致读取到的电平值为0。 解决方法: 将设备树中的引脚设置为 pinctrl_gpioled: gpio…...
【数据结构】栈与队列(FIFO)
在阅读该篇文章之前,可以先了解一下堆栈寄存器和栈帧的运作原理:<【操作系统】堆栈寄存器sp详解以及栈帧>。 栈(FILO) 特性: 栈区的存储遵循着先进后出的原则。 例子: 枪的弹夹,最先装进去的子弹最后射出来,最后装入的子弹…...
vue.js -ref和$refs获取dom和组件
在Vue.js中,ref和$refs是两个常用的属性,用于访问DOM元素和组件实例。下面分别详细解析这两个属性,并提供代码实例。 ref属性 ref属性用于给DOM元素或组件指定一个唯一的引用标识,在Vue实例中可以通过这个标识来访问对应的DOM元素…...
unity学习5:创建一个自己的3D项目
目录 1 在unity里创建1个3D项目 1.1 关于选择universal 3d,built-in render pipeline的区别 1.2 创建1个universal 3d项目 2 打开3D项目 2.1 准备操作面板:操作界面 layout,可以随意更换 2.2 先收集资源:打开 window的 AssetStore 下载…...
IEEE PDF eXpress遇到Font TimesNewRomanPSMT is not embedded的解决方案
IEEE PDF eXpress遇到Font TimesNewRomanPSMT is not embedded的解决方案 问题描述 在IEEE PDF eXpress上上传论文后,出现Font XXX is not embedded的问题。 该问题是指你所插入的图片等,没有将对应的字体嵌入进去。 解决方案 以下以Origin Lab图片…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
