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

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

场景1:判断字段是否存在

原始日志

a: a_vlue
b:    // 空串

LOG DSL编排

方案一:使用e_has, e_not_has

e_if(e_has("a"), e_set("has_a", true))
e_if(e_has("b"), e_set("has_b", true))
e_if(e_has("c"), e_set("has_c", true))
e_if(e_not_has("a"), e_set("not_has_a", true))
e_if(e_not_has("b"), e_set("not_has_b", true))
e_if(e_not_has("c"), e_set("not_has_c", true))

方案二:使用e_search

e_if(e_search("a: *"), e_set("has_a", true))
e_if(e_search("b: *"), e_set("has_b", true))
e_if(e_search("c: *"), e_set("has_c", true))
e_if(e_search("not a: *"), e_set("not_has_a", true))
e_if(e_search("not b: *"), e_set("not_has_b", true))
e_if(e_search("not c: *"), e_set("not_has_c", true))

加工后日志

a: a_vlue
b:    // 空串
has_a: true
has_b: true
has_c: false
not_has_a: false
not_has_b: false
not_has_c: true
  • 以上两种方案都可用于判断字段是否存在,但是方案一更直观、易理解。
  • 以上加工规则中的两个e_if可通过e_if(条件1,操作1,条件2,操作2)的形式合并为一个e_if,此处为了易于读者阅读,拆成两个。

场景2:判断字段值存在且不为空

原始日志

a: a_vlue
b:     // 空串

LOG DSL编排

方案一:使用字段取值函数v

e_if(v("a"), e_set("not_empty_a", true))
e_if(v("b"), e_set("not_empty_b", true))
e_if(v("c"), e_set("not_empty_c", true))
  • 字段取值函数v,当对应字段存在且值不为空时,其自动转换的Bool值为true,否则为false

方案二:使用e_search

# 至少一个字符
e_if(e_search('a: "?"'), e_set("not_empty_a", true))
e_if(e_search('b: "?"'), e_set("not_empty_b", true))
e_if(e_search('c: "?"'), e_set("not_empty_c", true))

# 正则
e_if(e_search('a~=".+"'), e_set("not_empty_a", true))
e_if(e_search('b~=".+"'), e_set("not_empty_b", true))
e_if(e_search('c~=".+"'), e_set("not_empty_c", true))

# 存在且不为空
e_if(e_search('a: * and not a==""'), e_set("not_empty_a", true))
e_if(e_search('b: * and not b==""'), e_set("not_empty_b", true))
e_if(e_search('c: * and not c==""'), e_set("not_empty_b", true))

加工后日志

a: a_vlue
b:     // 空串
not_empty_a: true
not_empty_b: false
not_empty_c: false
  • 以上三方案都可用于判断字段是否存在,但是方案一更简洁。
  • 以上加工规则中的两个e_if可通过e_if(条件1,操作1,条件2,操作2)的形式合并为一个e_if,此处为了易于读者阅读,拆成两个。

场景3:判断字段值存在但为空

原始日志

a: a_vlue
b:   // 空串

LOG DSL编排

方案一:使用字段取值函数v

e_if(op_and(e_has("a"), op_not(v("a"))), e_set("empty_a", true))
e_if(op_and(e_has("b"), op_not(v("b"))), e_set("empty_b", true))
e_if(op_and(e_has("c"), op_not(v("c"))), e_set("empty_c", true))

# 错误的方案
e_if(op_not(v("a")), e_set("empty_a", true))
e_if(op_not(v("b")), e_set("empty_b", true))
e_if(op_not(v("c")), e_set("empty_c", true))
  • 字段取值函数v,当对应字段存在且值不为空时,其自动转换的Bool值为true,否则为false. 但是只不存在是, 其返回None, op_not(None)时也是返回True.

方案二:使用e_search

e_if(e_search('a==""'), e_set("empty_a", true))
e_if(e_search('b==""'), e_set("empty_b", true))
e_if(e_search('c==""'), e_set("empty_c", true))

# 以下是错误调用
e_if(e_search('a:""'), e_set("empty_a", true))
e_if(e_search('b:""'), e_set("empty_b", true))

注意, 以上错误调用中, 因为:e_search是部分查询, 字段存在时, 无论是否空串的情况下, 空串a: ""永远会真.

加工后日志

a: a_vlue
b:    // 空串
empty_a: false
empty_b: true
empty_b: false
  • 以上方案中显然方案二更简洁.
  • 以上加工规则中的两个e_if可通过e_if(条件1,操作1,条件2,操作2)的形式合并为一个e_if,此处为了易于读者阅读,拆成两个。

场景4:基于字段值的逻辑查询判断

原始日志

"日志1"
http_host:  m1.abcd.com
status:  200
request_method:  GET
scheme:  https
header_length: 700
body_length: 1200

"日志2"
http_host:  m2.abcd.com
status:  200
request_method:  POST
scheme:  https
header_length: 100
body_length: 800

"日志3"
http_host:  m3.abcd.com
status:  200
request_method:  GET
scheme:  http
header_length: 700
body_length: 800

"日志4"
http_host:  m4.abcd.com
status:  404
request_method:  GET
scheme:  https
header_length: 100
body_length: 300

加工需求1

  • 为所有status字段值为200的日志事件, 添加一个字段type,其值为normal

LOG DSL编排

e_if(e_match("status", "200"), e_set("type", "normal))
或者
e_if(e_search("status==200"), e_set("type", "normal"))
  • 在此相对简单的场景下,以上两种方式都可以,并无太大差别。
  • 一般情况下status: 200也可以, 只是==更精准一些.

加工后的日志

"日志1"
type: normal
http_host:  m1.abcd.com
status:  200
request_method:  GET
scheme:  https
header_length: 700
body_length: 1200

"日志2"
type: normal
http_host:  m2.abcd.com
status:  200
request_method:  POST
scheme:  https
header_length: 100
body_length: 800

"日志3"
type: normal
http_host:  m3.abcd.com
status:  200
request_method:  GET
scheme:  http
header_length: 700
body_length: 800

"日志4"
http_host:  m4.abcd.com
status:  404
request_method:  GET
scheme:  https
header_length: 100
body_length: 300

加工需求2

  • 为所有status字段值为200 并且 request_method字段值为GET 并且 scheme字段值为https 的日志事件, 添加一个字段type,其值为normal

LOG DSL编排

e_if(e_search("status==200 and request_method==GET and scheme==https"), e_set("type", "normal"))
或者
e_if(e_match_all("status", "200", "request_method","GET", "scheme", "https"), e_set("type", "normal"))
  • 在此场景下,需要同时满足多个字段的匹配条件,可以使用e_searche_match_all
  • e_search用法相对更简洁一些。
  • 一般情况下e_search==也可以换成:status: 200也可以, 只是==更精准一些.

加工后的日志

"日志1"
type: normal
http_host:  m1.abcd.com
status:  200
request_method:  GET
scheme:  https
header_length: 700
body_length: 1200

"日志2"
http_host:  m2.abcd.com
status:  200
request_method:  POST
scheme:  https
header_length: 100
body_length: 800

"日志3"
http_host:  m3.abcd.com
status:  200
request_method:  GET
scheme:  http
header_length: 700
body_length: 800

"日志4"
http_host:  m4.abcd.com
status:  404
request_method:  GET
scheme:  https
header_length: 100
body_length: 300

加工需求3

  • 为所有status字段值为200 或者 request_method字段值为GET 或者 scheme字段值为https 的日志事件, 添加一个字段type,其值为normal

LOG DSL编排

e_if(e_search("status==200 or request_method==GET or scheme==https"), e_set("type", "normal"))
或者
e_if(e_match_any("status", "200", "request_method","GET", "scheme", "https"), e_set("type", "normal"))
  • 在此场景下,需要满足多个字段的匹配条件中的其中一个,可以使用e_searche_match_any
  • e_search用法相对更简洁一些。

加工后的日志

"日志1"
type: normal
http_host:  m1.abcd.com
status:  200
request_method:  GET
scheme:  https
header_length: 700
body_length: 100

"日志2"
type: normal
http_host:  m2.abcd.com
status:  200
request_method:  POST
scheme:  https
header_length: 100
body_length: 800

"日志3"
type: normal
http_host:  m3.abcd.com
status:  200
request_method:  GET
scheme:  http
header_length: 700
body_length: 800

"日志4"
type: normal
http_host:  m4.abcd.com
status:  404
request_method:  GET
scheme:  https
header_length: 100
body_length: 1300

加工需求4

  • 为所有status字段值为200 并且 request_method字段值为GET 并且 header_lengthbody_length的字段值之和小于等于1000的日志事件, 添加一个字段type,其值为normal

LOG DSL编排

e_if(op_and(e_search("status: 200 and request_method: GET"), op_le(op_sum(v("header_length"), v("body_length")), 1000)), e_set("type", "normal"))
  • 在更复杂的逻辑场景下,可通过e_search和其他表达式函数的组合来完成。

加工后的日志

"日志1"
type: normal
http_host:  m1.abcd.com
status:  200
request_method:  GET
scheme:  https
header_length: 700
body_length: 100

"日志2"
http_host:  m2.abcd.com
status:  200
request_method:  POST
scheme:  https
header_length: 100
body_length: 800

"日志3"
http_host:  m3.abcd.com
status:  200
request_method:  GET
scheme:  http
header_length: 700
body_length: 800

"日志4"
http_host:  m4.abcd.com
status:  404
request_method:  GET
scheme:  https
header_length: 100
body_length: 1300

进一步参考

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

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


下一篇:Python for Data Science - Web scraping