多行日志合并处理的内外存方法

上一讲中,咱们介绍了如何用SPL将一行日志结构化为一条记录,今天则要说一下多行日志对应一条记录的状况,咱们称之为不定行日志。node

 

事实上,集算器本身的输出日志就是这种不定行日志,咱们来看一下集算器节点机下的一个日志文件rqlog. log,一样摘录两段日志:函数

[2018-05-14 09:20:20]性能

DEBUG: 临时文件过时时间为:12小时。fetch

 

[2018-05-14 09:20:20]线程

DEBUG: Temporary file directory is:日志

D:\temp\esProc\nodes\127.0.0.1_8281\temp.blog

Files in temporary directory will be deleted on every 12 hours.内存

简单分析一下这两段日志,每一段第一行都是方括号中的时间,紧接着第二行是用冒号分隔的日志类型和日志内容,随后各行都是日志内容的补充,直至遇到下一个方括号中的时间行。由此能够肯定,结构化后的字段应该有三个:日志时间日志类型日志内容get

对于这种不定行的日志,最直接的结构化思路就是逐行加载,并根据条件拼接成相应的字段。但这里有个麻烦,因为每条记录对应的行数不肯定,因此须要另外经过条件判断来识别每条记录的起始行。另外一个问题就是当使用游标分析比较大的文件时,因为每次fetch的行数不可能老是恰好断在一条记录的末尾,因此在fetch的块与块之间还要考虑衔接问题。table

因此,对于这种类型的日志,最理想的方法,是应该能把每一块日志与其它块自动分开。这样,就能够直接将每一块日志解析到一条记录,而不须要考虑读取的行应该属于上一条记录仍是下一条记录。

SPL中就有这样的分组方式,形如A.group@i(exp),只不过其中的分组表达式exp不能返回枚举值,而应该返回布尔值。而i选项则表示根据相邻行计算exp,若是返回true就表示创建一个新组,其后面连续返回false的行都归于这个组。

有了这样的分组方式,咱们只要写出能解析记录第一行的exp就能够了。本例中,记录第一行是以方括号“[”开始的一个日期时间,若是可以肯定后面的内容中不会有以“[”开始的行,那么分组表达式只需判断每一行的第一个字符是否为‘[’就能够了。

下面是实现的脚本文件convertlog.dfx:

 

A

B

C

D

1

=create(日志时间, 日志类型, 日志内容 )

=now()

   

2

=file@s(fileName:"UTF-8")

=A2.read@n()

=B2.select(~!="")

=C2.group@i(pos(~,"[")==1)

3

=A2.name()

=filename@d(A3)

=filename@n(A3)

 

4

=outFile=B3+"\\"+C3+".btx"

=file(outFile)

>movefile(B4)

 

5

       

6

for D2

=0

   

7

 

for A6

>B6=B6+1

 

8

   

if B6==1

=mid(B7,2,19)

9

     

>A5=datetime(D8,"yyyy-MM-dd   HHss")

10

     

next

11

   

if B6==2

>B5=substr@l(B7,":")

12

     

>C5=C5|substr(B7,":")

13

     

next

14

   

>C5=C5|B7

 

15

 

>A1.record([A5:C5])

>C5=[]

 

16

 

if A1.len()>99999

 

17

   

>B4.export@ab(A1)

18

   

>D4=D4+A1.len()

>output("转换完成:"/D4)

19

   

>A1.reset()

 

20

>D4=D4+A1.len()

>B4.export@ab(A1)

>output("转换完成:"/D4)

 

21

=now()

=interval@ms(B1,A21)

Return

 

22

>output("转换为 "+outFile+" 成功。耗时 "/B21+" 毫秒。")

表(1)

脚本仍然是一次性将文件所有读入,只是在分析过程当中,每凑够十万行就将其追加到输出文件。下面是重点代码的解析:

1)         C2过滤空行,日志的块与块之间可能有不少空行,此处用select函数选出非空行。

2)         D2是本例的重点,按照每一行是否由‘[’开头来进行分组,分组后的结果是每一块对应一个序列,再由这些块序列组成一个大的序列。

3)         第5行空了出来,实际上是预留A5到C5来容纳解析后的3个字段。

4)         因为D2返回的序列已是按照记录对应的块作好分组了,因此只需在A6中对D2序列循环便可。

5)         B7到C14将每一块日志结构化到各个字段。

6)         A20到C20将最后一段不够十万行的数据,写到文件。

 

下面是当前代码的执行结果:

                                              undefined

图(1)

 

上面这个例子介绍了如何用分组来结构化不定行的日志。不过,分析前仍然导入了整个文件,占用的内存较大。为了减小内存占用,咱们可使用游标来处理。和上面这个例子很是相似,从文件获得游标后,紧接着的是和序列几乎同样的过滤、分组方法。细微的区别是因为游标中返回的分组是序表,而再也不是序列,所以须要用字段名‘_1’来引用各行的值。另外,使用游标后,每次fetch获得的是一个序表的序列,所以相对于全文导入,要多一层循环处理。

下面是实现的脚本文件convertLogCursor.dfx:

 

A

B

C

D

E

1

=create(日志时间, 日志类型, 日志内容 )

=now()

     

2

=file@s(fileName:"UTF-8")

=A2.cursor@s()

=B2.select(_1!="")

=C2.group@i(pos(_1,"[")==1)

 

3

=A2.name()

=filename@d(A3)

=filename@n(A3)

   

4

=outFile=B3+"\\"+C3+".btx"

=file(outFile)

>movefile(B4)

   

5

         

6

for C2,10000

for A6

=0

   

7

   

for B6

>C6=C6+1

 

8

     

if C6==1

=mid(C7._1,2,19)

9

       

>A5=datetime(E8,"yyyy-MM-dd   HHss")

10

       

next

11

     

if C6==2

>B5=substr@l(C7._1,":")

12

       

>C5=C5|substr(C7._1,":")

13

       

next

14

     

>C5=C5|C7._1

 

15

   

>A1.record([A5:C5])

>C5=[]

 

16

   

if A1.len()>99999

 

17

     

>B4.export@ab(A1)

 

18

     

>D4=D4+A1.len()

>output("转换完成:"/D4)

19

     

>A1.reset()

 

20

>D4=D4+A1.len()

>B4.export@ab(A1)

>output("转换完成:"/D4)

 

21

=now()

=interval@ms(B1,A21)

   

22

>output("转换为 "+outFile+" 成功。耗时 "/B21+" 毫秒。")

表(2)

能够看到,除了用游标时,表达式中须要使用缺省字段名‘_1’,以及多了一层循环取数,这个脚本和前面所有读入文件的方式彻底一致。执行后能够看到结果也跟convertLog.dfx彻底同样。

         看到这里,细心的读者可能会想到,是否是也能够像上一讲同样,使用多路游标来提高性能?答案是确定的,不过须要调整一下方法。若是只是简单使用cursor@m返回多路游标的话,因为线程游标是由SPL自动建立的,每一个游标会被自动分配一段属于本身的数据,这就有可能会将原本属于同一条记录的数据块,分配给了两个线程,从而形成线程内部难以处理数据缺失的部分。若是不介意部分数据不完整,那么能够稍稍调整一下脚本,直接跳过残缺的记录。而若是对数据的完整性要求很严格的话,就须要将不完整的记录头和记录尾返回给主程序,让主程序来集中处理了。但要注意的是多路游标的各线程游标是自动建立的,线程之间的顺序很差肯定,因此主程序拿到这些不完整的记录头和记录尾,也会由于次序无法肯定而没法正确衔接。所以,这时就须要改用 fork to(n)语句来主动建立顺序线程了。fork语句所在的格子值就是当前线程的序号,而后在代码段中建立局部游标,并加载相应的数据段就能够了。 不过,不管如何都会麻烦一些,因不是很常见,这里也就再也不举例了,有兴趣的同窗能够将它做为一个练习题。

最后总结一下,不定行日志的处理关键是肯定几行日志能够“凑”成一条记录。而利用集算器SPL中group函数的@i选项,就能够定义日志块的分隔条件,从而简洁方便地“庖丁解牛”了。

更多精彩内容,详情请阅读原文

相关文章
相关标签/搜索