日志服务数据加工最佳实践: 函数调用

日志服务数据加工最佳实践: 函数调用

编写数据加工规则过程中,需根据场景选择不同的函数。LOG DSL函数的具体用法可参考数据加工语法参考

场景1: 理解e_keep/KEEP的应用场景

默认规则中, 不做处理的事件都是保留. 所以KEEP通常只用于特定场景. 如果需要丢弃日志, 可以使用e_drop/e_drop传入条件或者使用e_if/e_if_elseDROP搭配使用:

如下比较区别:

e_keep(e_search(...) )    # 满足保留, 不满足丢弃
e_drop(e_search(...) )    # 满足丢弃, 不满足保留
e_if(e_search("..."), KEEP)    # 没有意义的代码, 满足后KEEP
e_if_else(e_search("..."), KEEP, DROP)    # 有意义
e_if(e_search("not ..."), DROP)        # 有意义

场景2:尽可能使用函数自身提供的功能

子场景1:当原字段不存在或者为空时,为字段赋值

  • 最佳实践
e_set("result", ".....value...", mode="fill")
  • 非最佳实践
e_if(op_not(v("result")), e_set("result", ".....value..."))
  • 字段的提取与覆盖模式,参考字段提取与覆盖模式
    提取与覆盖模式包含:

    • fill – 当原字段不存在或者值为空时
    • add –当原字段不存在时设置
    • overwrite – 总是设置
    • fill/add/overwrite-auto – 当新值非空时才fill/add/overwrite

子场景2:使用GROK函数简化正则表达式

  • 加工逻辑
    提取content字段中的IP地址


  • 原日志格式
content:"ip address: 192.168.1.1"
  • 提取目标: 192.168.1.1

  • 最佳实践
e_regex("content", grok(r"\w+: (%{IP})"), "addr")
# 或者
e_regex("content", grok(r"\w+: (%{IP:addr})"))
  • 非最佳实践
    如下代码相对复杂一些:
e_regex("content", grok(r"\w+: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"), "addr")

进一步参考Grok函数说明

子场景3:给多个字段赋值

  • 最佳实践
e_set("k1", "v1", "k2", "v2", "k3", "v3", ....)
  • 相对冗余的规则
e_set("k1", "v1")
e_set("k2", "v2")
e_set("k3", "v3")
...

场景3:使用e_compose减少重复判断

  • 加工逻辑
    如果content是123,则首先执行删除age和name字段,然后重命名content为ctx


  • 原日志格式
content:123
age:23
name:twiss
  • 加工后的日志格式
  ctx: 123
  • 最佳实践
    一般情况下推荐使用e_compose组合相同逻辑下的操作.
e_if(e_search("content==123"), 
     e_compose(e_drop_fields("age|name"), e_rename("content", "ctx")))
  • 非最佳实践
    如下代码会多次判断, 一定程度上效率低一些.
e_if(e_search("content==123"), e_drop_fields("age|name"))
e_if(e_search("content==123"), e_rename("content", "ctx"))

进一步参考事件判断

场景4:注意表达式函数的参数类型

日志事件的字段和值在函数之间传递的过程中,始终都是字符串形式。非字符串类型的数据会被自动转化为字符串类型。因此在调用函数时,要注意各个函数能接收的参数类型。具体每个函数接收的参数类型可参考数据加工语法参考中对各个函数的解释。

样例1:
op_add既可以接收字符串类型,也可以接受数值类型,因此不需要做参数类型转换

e_set("a", 1)
e_set("b", 2)

op_add(v("a"), v("b"))    # 合法,返回值为"12"
op_add(ct_int(v("a")), ct_int(v("b")))    # 合法, 返回值为3

样例2:
op_sum, op_mul等函数只能接受数值类型,因此需要做数据类型转换,将字符串转化为数值型

e_set("a", 1)
e_set("b", 2)

op_sum(v("a"), v("b"))    # 非法
op_sum(ct_int(v("a")), ct_int(v("b")))    # 合法, 返回值为3

op_mul(v("a"), v("b"))    # 非法
op_mul(ct_int(v("a")), ct_int(v("b")))    # 合法, 返回值为2

日志事件在函数间传递,字段值都被自动转化为字符串类型,因此v("a"), v("b")都是字符串类型。而op_sum, op_mul等函数只能接受数值类型,可通过ct_int将字符串转化为整型,再传递给这类函数。

样例3:

"""
加工逻辑: 将time1表示的日期时间转化为Unix时间戳
"""
e_set("time1", "2019-06-03 2:41:26")

e_set("time2", dt_totimestap(v("time1"))) # 非法

e_set("time2", dt_totimestap(dt_parse(v("time1")))) # 合法
e_set("time2", dt_parsetimestamp(v("time1"))) # 合法
  • dt_totimestap接收的参数类型为日期时间对象, 不是字符串。因此需要调用dt_parse将time1的字符串值类型转化为日期时间对象类型。
  • 也可直接使用dt_parsetimestamp函数,它既可接收日期时间对象,也可接收字符串。

场景5:注意表达式函数的异常处理情况

许多表达式函数对于输入的参数有一定的要求, 如果不满足会报错. 也有一些会返回默认值容错, 需要特别注意, 这些默认值传递给后续的函数时可能进一步会报错. 例如:

e_set("data_len": op_len(v("data")))                     # 错误调用
e_set("data_len": op_len(v("data", default="")))  # 正确调用

如果字段data不存在时v("data")会返回None, 那么第一个调用会报错. 第二个通过默认值default给与一个合法的默认值, 让表达式可以不会报错.

场景6:理解e_if与e_switch的区别

  • e_if语法
e_if(条件1, 操作1, 条件2, 操作2, 条件3, 操作3, ....)

条件与操作的配对组合, 依次根据条件判断, 满足条件的进行相应操作, 不满足条件的不进行对应操作, 进行下一个条件判断.

  • e_switch语法
e_switch(条件1, 操作1, 条件2, 操作2, 条件3, 操作3, ...., default=None)

条件与操作的配对组合, 依次根据条件判断, 满足条件的进行相应操作, 然后直接返回操作的结果, 不满足条件的不进行对应操作, 进行下一个条件判断. 如果没有任何条件满足, 并且配置了default参数的话, 执行default配置的操作并返回

  • 样例

原始日志

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_if会进行所有条件的判断,满足的则进行对应操作,不满足不进行对应操作。


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

e_switch只要有一个条件满足,就会返回结果,不会再进行后续条件判断。

进一步参考

欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
日志服务数据加工最佳实践: 函数调用

上一篇:开源DSMC模拟软件-SPARTA


下一篇:日志服务数据加工最佳实践: 事件判断