日志聚类算法 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图片…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...

免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
ThreadLocal 源码
ThreadLocal 源码 此类提供线程局部变量。这些变量不同于它们的普通对应物,因为每个访问一个线程局部变量的线程(通过其 get 或 set 方法)都有自己独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段,这些类希望将…...
电脑桌面太单调,用Python写一个桌面小宠物应用。
下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡,可以响应鼠标点击,并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...
从实验室到产业:IndexTTS 在六大核心场景的落地实践
一、内容创作:重构数字内容生产范式 在短视频创作领域,IndexTTS 的语音克隆技术彻底改变了配音流程。B 站 UP 主通过 5 秒参考音频即可克隆出郭老师音色,生成的 “各位吴彦祖们大家好” 语音相似度达 97%,单条视频播放量突破百万…...