您可以通过日志服务数据加工函数清洗您所采集的海量日志数据,实现数据格式标准化。本文介绍调用函数清洗数据的常见场景和相关操作。
场景1:过滤日志(e_keep函数和e_drop函数)
您可以使用e_drop函数或e_keep函数过滤日志,也可以使用e_if函数与DROP参数、e_if_else函数与DROP参数过滤日志。
常用规则如下所示:
e_keep(e_search(...) )
:满足条件时保留,不满足条件时丢弃。e_drop(e_search(...) )
:满足条件时丢弃,不满足条件时保留。e_if_else(e_search("..."), KEEP, DROP)
:满足条件时保留,不满足条件时丢弃。e_if(e_search("not ..."), DROP)
:满足条件时丢弃,不满足条件时保留。e_if(e_search("..."), KEEP)
:无意义的加工规则。
示例如下所示:
原始日志
#日志1 __source__: 192.168.0.1 __tag__:__client_ip__: 192.168.0.2 __tag__:__receive_time__: 1597214851 __topic__: app class: test_case id: 7992 test_string: <function test1 at 0x1027401e0> #日志2 __source__: 192.168.0.1 class: produce_case id: 7990 test_string: <function test1 at 0x1020401e0>
加工规则
丢弃没有__topic__字段和__tag__:__receive_time__字段的日志。
e_if(e_not_has("__topic__"),e_drop()) e_if(e_not_has("__tag__:__receive_time__"),e_drop())
加工结果
__source__: 192.168.0.1 __tag__:__client_ip__: 192.168.0.2 __tag__:__receive_time__: 1597214851 __topic__: app class: test_case id: 7992 test_string: <function test1 at 0x1027401e0>
场景2:为日志空缺字段赋值(e_set函数)
您可以使用e_set函数为日志空缺字段赋值。
子场景1:原字段不存在或者为空时,为字段赋值。
e_set("result", "......value......", mode="fill")
mode参数取值请参见字段提取检查与覆盖模式。
示例如下所示:
原始日志
name:
加工规则
e_set("name", "aspara2.0", mode="fill")
加工结果
name: aspara2.0
子场景2:使用GROK函数简化正则表达式,提取字段内容。
示例如下所示:
原始日志
content:"ip address: 192.168.1.1"
加工规则
使用GROK函数捕获提取content字段中的IP地址。
e_regex("content", grok(r"(%{IP})"),"addr")
加工结果
addr: 192.168.1.1 content:"ip address: 192.168.1.1"
子场景3:为多个字段赋值。
e_set("k1", "v1", "k2", "v2", "k3", "v3", ......)
示例如下所示:
原始日志
__source__: 192.168.0.1 __topic__: __tag__: __receive_time__: id: 7990 test_string: <function test1 at 0x1020401e0>
加工规则
为__topic__字段、__tag__字段和__receive_time__字段赋值。
e_set("__topic__","app", "__tag__","stu","__receive_time__","1597214851")
加工结果
__source__: 192.168.0.1 __topic__: app __tag__: stu __receive_time__: 1597214851 id: 7990 test_string: <function test1 at 0x1020401e0>
场景3:通过判断,删除和重命名字段(e_search函数、e_rename函数和e_compose函数)
一般情况下,推荐您使用e_compose函数进行重复判断和操作。
示例如下所示:
原始日志
content:123 age:23 name:twiss
加工规则
首先判断content字段值是否为123,如果是,则删除age和name字段,再将content字段重命名为ctx。
e_if(e_search("content==123"),e_compose(e_drop_fields("age|name"), e_rename("content", "ctx")))
加工结果
ctx: 123
场景4:转换日志参数类型(v函数、cn_int函数和dt_totimestamp函数)
日志的字段和字段值在加工过程中,始终都是字符串形式,非字符串类型的数据会被自动转化为字符串类型。因此在调用函数时,要注意各个函数能接收的参数类型。更多信息,请参见语法简介。
子场景1:调用op_add函数进行字符拼接和数据相加。
op_add函数既可以接收字符串类型,也可以接受数值类型,因此不需要做参数类型转换。
示例如下所示:
原始日志
a : 1 b : 2
加工规则
e_set("d",op_add(v("a"), v("b"))) e_set("e",op_add(ct_int(v("a")), ct_int(v("b"))))
加工结果
a:1 b:2 d:12 e:3
子场景2:运用字段操作函数和ct_int函数进行类型转换并调用op_mul函数进行数据相乘。
示例如下所示:
原始日志
a:2 b:5
加工规则
因为v("a")和v("b")都是字符串类型,而op_mul函数的第二个参数只能接收数值类型,所以您需要通过ct_int函数将字符串转化为整型,再传递给op_mul函数。
e_set("c",op_mul(ct_int(v("a")), ct_int(v("b")))) e_set("d",op_mul(v("a"), ct_int(v("b"))))
加工结果
a: 2 b: 5 c: 10 d: 22222
子场景3:调用dt_parse函数和dt_parsetimestamp函数将字符串或日期时间转换为标准时间。
dt_totimestamp函数接收的参数类型为日期时间对象,不是字符串。因此需要调用dt_parse函数将time1的字符串值类型转化为日期时间对象类型。您也可以直接使用dt_parsetimestamp函数,它既能接收日期时间对象,也能接收字符串。更多信息,请参见日期时间函数。
示例如下所示:
原始日志
time1: 2020-09-17 9:00:00
加工规则
将time1表示的日期时间转化为Unix时间戳。
e_set("time1", "2019-06-03 2:41:26") e_set("time2", dt_totimestamp(dt_parse(v("time1")))) 或 e_set("time2", dt_parsetimestamp(v("time1")))
加工结果
time1: 2019-06-03 2:41:26 time2: 1559529686
场景5:为日志不存在的字段填充默认值(default传参)
部分SLS DSL表达式函数对输入的参数有一定要求,如果不满足,数据加工窗口会报错或返回默认值。当日志中存在必要而残缺字段时,您可以在op_len函数中填充默认值。
传递默认值给后续的函数时可能会进一步报错,因而需要及时处理函数返回的异常。
原始日志
data_len: 1024
加工规则
e_set("data_len", op_len(v("data", default="")))
加工结果
data: 0 data_len: 0
场景6:判断日志并增加字段(e_if函数与e_switch函数)
推荐使用e_if函数或e_switch函数进行日志判断。更多信息,请参见流程控制函数。
e_if函数
e_if(条件1, 操作1, 条件2, 操作2, 条件3, 操作3, ....)
e_switch函数
e_switch函数是条件与操作的配对组合。依次根据条件进行判断,满足条件的进行对应操作,然后直接返回操作结果。不满足条件的不进行对应操作,直接进行下一个条件判断。如果没有满足任一条件并且配置了default参数,则执行default配置的操作并返回。
e_switch(条件1, 操作1, 条件2, 操作2, 条件3, 操作3, ...., default=None)
示例如下所示:
原始日志
status1: 200 status2: 404
e_if函数
加工规则
e_if(e_match("status1", "200"), e_set("status1_info", "normal"), e_match("status2", "404"), e_set("status2_info", "error"))
加工结果
status1: 200 status2: 404 status1_info: normal status2_info: error
e_switch函数
加工规则
e_switch(e_match("status1", "200"), e_set("status1_info", "normal"), e_match("status2", "404"), e_set("status2_info", "error"))
加工结果
只要有一个条件满足,就返回结果,不再进行后续条件判断。
status1: 200 status2: 404 status1_info: normal
场景7:数据转化纳秒级的Unix时间戳
部分场景需要日志服务的数据加工能够满足纳秒级精度时间戳的需求,当原始日志中存在Unix时间格式字段,您可以使用字段操作函数,将其解析成纳秒精度的日志时间。
原始日志
{ "__source__": "1.2.3.4", "__time__": 1704983810, "__topic__": "test", "log_time_nano":"1705043680630940602" }
加工规则
e_set( "__time__", op_div_floor(ct_int(v("log_time_nano")), 1000000000), ) e_set( "__time_ns_part__", op_mod(ct_int(v("log_time_nano")), 1000000000), )
加工结果
{ "__source__": "1.2.3.4", "__time__": 1705043680, "__time_ns_part__": 630940602, "__topic__": "test", "log_time_nano":"1705043680630940602" }
场景8:数据转化微秒级标准 ISO8601 时间戳
部分场景需要日志服务的数据加工满足高精度时间戳的需求,当原始日志中存在标准 ISO8601时间格式的字段,您可以使用字段操作函数,将其解析成微秒精度的日志时间。
原始日志
{ "__source__": "1.2.3.4", "__time__": 1704983810, "__topic__": "test", "log_time":"2024-01-11 23:10:43.992847200" }
加工规则
e_set( "__time__", dt_parsetimestamp(v("log_time"), tz="Asia/Shanghai"), mode="overwrite", ) e_set("tmp_ms", dt_prop(v("log_time"), "microsecond")) e_set( "__time_ns_part__", op_mul(ct_int(v("tmp_ms")), 1000), )
加工结果
{ "__source__": "1.2.3.4", "__time__": 1704985843, "__time_ns_part__": 992847000, "__topic__": "test", "log_time": "2024-01-11 23:10:43.992847200", "tmp_ms": "992847" }