当几百个,个性不同,习惯不同,技术背景不同的人在开发同一个app的时候,当他们在往一个日志里塞东西的时候,这个庞大复杂且没有统一规范的日志会让未来的维护和问题排查工作步履维艰,不同的部门使用不同的规范,同一个部门内不同的功能使用不同的日志习惯,几乎没有人能遍历整个日志,来定位一个跨多部门的问题,这是今天支付宝app面临的一个问题,也是我们研发LSE(logSequenceEncoding)-RNN这个模型的原因
拆解整理最混乱无序的日志并学习到规律。
这个需求描述起来是简单朴素的,我们需要一个AI,一个模型,给它喂足够多的日志,让它学会理解这是什么东西,让它看要排查的含有问题的日志,让它把问题挑出来,毕竟今天的很多AI看起来就好像在魔法坩埚里熬魔药一样简单,你只要选对坩锅,把东西哗啦啦倒进去,然后就熬制好了,但是真的这么简单么?
对大多数人来说,AI真正深入人心的节点是gpt3,在gpt3问世之后,所有人都希望用AI来解决自己所遇到的问题,于是理所当然的,当解决问题的时候,就必然会第一时间想到借用一个强大的大模型底座来实现,于是我们思考了解决日志诊断这个问题的大模型方案。
这个方案,虽然从原理上是可行的,但是其中会有一个问题:
●可用的处理文本的大模型底座,基本是NLM,也就是自然语言模型,也就是说它学习了海量数据得出的权值网络带来的优势只对自然语言文本有效,而我们要处理的日志和自然语言之间有关联的很显然只是其中某些英文单词,总体上是完全没有自然语言逻辑的
●所以如果要使用大模型底座,就必须有两块比较大的业务人力投入
所有的待排查日志必须被翻译成自然语言
业务人员需要总结出这个业务所有的日志逻辑,以及日志逻辑和代码逻辑的关系,字段所有取值的意义
看起来里面所有的东西都是成熟可用的,那么我们可以展开了么?不好意思,甲方说半个业务配合人力都没有自己想其他办法吧。
在上面的背景下,原本有人提出的将日志和诊断原因,标注出来让模型学会这些映射关系的标注方案也胎死腹中了,所以甲方的需求其实说白了就是要马跑还没草,嗯,确实有点挑战性。
在拔了不多的一些头发后,我们想到,我们自己不懂业务,又没有业务人员的参与,那岂不是说对机器来说根本没必要学什么业务逻辑,我们就把日志当作无意义文本,让模型去强行总结其中的编码规律就好,于是就有了以下方案。
总体来说,这个模型做的事情就是看足够多的线上业务日志,学会这个日志的样子,然后看到和这个日志不一样的就爆出来,它的优缺点如下
●缺点
只能给出嫌疑,说这里我看着不一样,人类你看看咋回事,不能明确的告诉你这里是什么问题
如果线上日志中存在一些平时被忽略的报错,也会被训练进去被它视为正常
●优点
没草还能跑,还要啥自行车!靠小几个技术人员解决日志排查问题,这就是最大的优点。
他是从统计学意义上挑出不符合统计学规律的少见的日志,完全不涉及业务逻辑,所以反而能够快速的在数十万行的巨大的日志中快速的挑出那些人类精力和经验边界之外的问题
方案确定了后,马不停蹄就要进行技术设计和实现,这个方案打眼看过去会遇到两大问题。
规范化编码问题,将没有统一规范的日志规范化拆解:前面说了,支付宝app是数百人多个部门在往一个日志里塞东西,那么日志必然也不是统一规范和习惯的,而我们想把日志塞进一个s2s的模型,就必须先将其规范起来,因此如何规范化编码会是第一个问题
模型架构问题,设计出经济够用且有效的模型架构:我们必须根据试验并适配上一步的编码方式设计一种效率足够高的模型架构来完成有效的自编码,这个架构会是第二个问题。
在解决规范化编码问题的时候,我们首先要解决日志分隔的问题
支付宝客户端日志如上图一个例子所示,一行日志最外层的分隔符看起来是空格,但是某些字段内的二级分隔符也用了空格,甚至三级分隔符也用了空格,而真实的情况往往比这个更复杂
在这种情况下,程序员思路往往会陷入一个误区,就是用预处理函数对每个业务的日志来做到合适的个性化的分隔,但是这会带来一个大问题,就是我们对不同业务的日志需要写独立的预处理函数,这个人力消耗是不能接受的
所以再度深入思考后,醒悟到,我们是统计学模型啊,那就不应该用逻辑学思路而是统计学思路来解决问题,所以我管你是什么分隔符,我只要都使用同一种分隔方法分开了就行了,从统计学意义上我都会学到这个日志的规律和样子,这样就达成我的目的了
接下来必须要做的一步是对字段进行压缩,有些人会说了,好好的字段,为什么要压缩呢?压缩了会不会丢失信息?模型就不精确了?
为啥要压缩呢?因为我们的模型本质上是找到日志逻辑的相似性,如果不进行压缩编码,那么两个字段,哪怕只差一个字符,也会被认为是完全不同的,这就会导致因为id,tag,url等的不同,日志抽象不出规律,一个日志文件中有1000W个字段,可能有900W个不同字段,而同样大小的自然语言文件,最多也只有1w个不同的单词,所以要想抽象出规律,必须对日志字段进行压缩编码,要稍微模糊一点去看这些字段,把微小不同的字段看成一样的,所以这种方法我命名为模糊化编码
这个方法的大致原理如上图所示,我们在拿到一个字段的时候会做两步操作
第一,提取出当前这个分隔字段内最长的有意义字符串,作为模糊化编码的前缀
第二,采用一种长度归一化的算法将长度不同但接近的字符串算出同一个数值,作为模糊化编码的后缀
这种形式模拟了一个人类看日志的过程,我先找关键的识别用的字符串在不在,再看字段的长度有没有异常。
针对完全混乱没有任何规律的日志行,还开发了数据读取的扫描器模式,每行从前往后扫描,扫描到有意义字符串后,和前后的无意义字符串聚合编码成一个新的字段,将完全无序的日志强行整合成有规律的序列
模型架构的构造经过了很多试验,总体来说要满足两点要求
要足够小,训练和调试成本要低
能够收敛到足够高的精度
在以上考虑下,加上多次的实验调整,模型的核心架构固定如下
采用两个Bi- LSTM网络作为基础结构,保证字段和前后的字段关联同样紧密,同时相比transformer要小得多但是在日志排查场景下够用
中间用一个编码器链接来降维,网络从高维降低到低维的过程中,往往能更好的压缩出数据的规律;
最后用一个残差链接直接链接输入和输出,来保证模型的有效高速收敛
在解决了上面的卡点问题后,开发模型工程架构如下
日志数据被模糊化编码后,形成字典和编码后日志数据,编码后数据再结合字典进一步编码成数字索引编码送入模型进行训练或推理,最后推理结果再反向变成可读的日志结构。