编写数据加工规则过程中,需根据场景选择不同的函数。

LOG DSL函数的具体用法请参见语法简介

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

默认规则中,不做处理的事件都会保留。所以KEEP通常只用于特定场景。如果需要丢弃日志,可以使用e_drope_drop传入条件,或者使用e_ife_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..."))
  • 子场景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函数更多操作请参见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,则首先删除agename字段,然后将字段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_mul函数的第二个参数只能接受数值类型,因此需要将字符串转化为数值型。
    e_set("a", 2)
    e_set("b", 5)
    
    op_mul(v("a"), v("b"))    # 非法
    op_mul(ct_int(v("a")), ct_int(v("b")))    # 合法, 返回值为10
    op_mul(v("a"), ct_int(v("b")))    # 合法, 返回值为22222
    日志事件在函数间传递,字段值都被自动转化为字符串类型,因此v("a")v("b")都是字符串类型。而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_parsetime1的字符串值类型转化为日期时间对象类型。
    • 也可以直接使用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"))
  • e_if加工结果
    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"))
  • e_switch加工结果
    status1: 200
    status2: 404
    status1_info: normal
    e_switch只要有一个条件满足,就会返回结果,不会再进行后续条件判断。