日志服务数据加工最佳实践: 多子键为数组的复杂JSON加工

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,内容安全 1000次 1年
云备份 Cloud Backup,100GB 3个月
简介: 程序构建的日志经常会以一种统计性质的JSON格式写入, 通常其包含一个基础信息, 以及多个子健为数组的形式. 本篇如何使用日志服务数据加工处理多子键为数组的复杂JSON.

加工需求

统计类日志形式

程序构建的日志经常会以一种统计性质的JSON格式写入, 通常其包含一个基础信息, 以及多个子健为数组的形式. 例如一个服务器每隔1分钟写入一条日志, 包含当前信息状态, 以及相关服务器和客户端节点的统计状态信息.

样例:

__source__:  1.2.3.4
__topic__:  
content:{
     "service": "search_service",
     "overal_status": "yellow",
     "servers": [
         {
             "host": "1.2.3.4",
             "status": "green"
         },
         {
             "host": "1.2.3.5",
             "status": "green"
         }
     ],
     "clients": [
         {
             "host": "1.2.3.6",
             "status": "green"
         },
         {
             "host": "1.2.3.7",
             "status": "red"
         }
     ]
}
AI 代码解读

加工需求

1、对原始日志进行topic分裂,主题主要分为三个,分别是overall_type、client_status、server_status
2、对于不同的topic保存不同的信息

  • overall_type:保留server、client数量,overal_status颜色和service信息
  • client_status: 保留对应的host地址、status状态和service信息
  • server_status: 保留对应的host地址、status状态和service信息

期望输出的日志

期望样例中的一条日志会被分裂加工成5条日志:

__source__:  1.2.3.4
__topic__:  overall_type
client_count:  2
overal_status:  yellow
server_count:  2
service:  search_service


__source__:  1.2.3.4
__topic__:  client_status
host:  1.2.3.7
status:  red
service:  search_service


__source__:  1.2.3.4
__topic__:  client_status
host:  1.2.3.6
status:  green
service:  search_service


__source__:  1.2.3.4
__topic__:  server_status
host:  1.2.3.4
status:  green
service:  search_service


__source__:  1.2.3.4
__topic__:  server_status
host:  1.2.3.5
status:  green
service:  search_service
AI 代码解读

解决方案

初步处理

1、第一步将一条日志拆分成3条日志, 给主题赋予3个不同值, 在进行分裂,经过分裂后会分成除了topic不同,其他信息相同的三条日志。

e_set("__topic__", "server_status,client_status,overall_type")
e_split("__topic__")
AI 代码解读

处理后日志格式如下(在内存中):

__source__:  1.2.3.4
__topic__:  server_status                    // 另外2条是client_status和overall_type, 其他一样
content:  {
    ...如前...
}
AI 代码解读

2、第二步为基于content的JSON内容在第一层展开, 并删除content字段:

e_json('content',depth=1)
e_drop_fields("content")
AI 代码解读

处理后的日志格式如下(在内存中):

__source__:  1.2.3.4
__topic__:  overall_type                          // 另外2条是client_status和overall_type, 其他一样
clients:  [{"host": "1.2.3.6", "status": "green"}, {"host": "1.2.3.7", "status": "red"}]
overal_status:  yellow
servers:  [{"host": "1.2.3.4", "status": "green"}, {"host": "1.2.3.5", "status": "green"}]
service:  search_service  
AI 代码解读

处理overall_type日志

  1. 针对主题是overall_type的日志, 统计client_count和server_count:
e_if(e_search("__topic__==overall_type"), 
       e_compose(
                 e_set("client_count" json_select(v("clients"), "length([*])", default=0)), 
                 e_set("server_count" json_select(v("servers"), "length([*])", default=0))
    ))
AI 代码解读

处理后的日志为(仅显示修改部分):

__topic__:  overall_type
server_count:  2
client_count:  2
AI 代码解读
  1. 丢弃相关字段:
e_if(e_search("__topic__==overall_type"), e_drop_fields("clients", "servers"))
AI 代码解读

处理server_status日志

  1. 针对主题是server_status的日志, 进行进一步分裂.
e_if(e_search("__topic__==server_status"), 
       e_compose(
                 e_split("servers"), 
                 e_json("servers", depth=1)
    ))
AI 代码解读

处理后的日志为2条如下(仅显示修改部分):

__topic__:  server_status
servers:  {"host": "1.2.3.4", "status": "green"}
host: 1.2.3.4
status: green
AI 代码解读

__topic__:  server_status
servers:  {"host": "1.2.3.5", "status": "green"}
host: 1.2.3.5
status: green
AI 代码解读
  1. 保留相关字段:
e_if(e_search("__topic__==overall_type"), e_drop_fields("servers"))
AI 代码解读

处理client_status日志

  1. 同理, 针对主题是client_status的日志, 进行进一步分裂, 在删除多余字段.
e_if(e_search("__topic__==client_status"), 
       e_compose(
                 e_split("clients"), 
                 e_json("clients", depth=1),
                 e_drop_fields("clients")
    ))
AI 代码解读

处理后的日志为2条如下(仅显示修改部分):

__topic__:  client_status
host: 1.2.3.6
status: green
AI 代码解读

__topic__:  clients
host: 1.2.3.7
status: red
AI 代码解读

综合

综上,LOG DSL规则是


# 总体分裂
e_set("__topic__", "server_status,client_status,overall_type")
e_split("__topic__")
e_json('content',depth=1)
e_drop_fields("content")

# 处理overall_type日志
e_if(e_search("__topic__==overall_type"), 
       e_compose(
                 e_set("client_count" json_select(v("clients"), "length([*])", default=0)), 
                 e_set("server_count" json_select(v("servers"), "length([*])", default=0))
    ))

# 处理server_status日志
e_if(e_search("__topic__==server_status"), 
       e_compose(
                 e_split("servers"), 
                 e_json("servers", depth=1)
    ))
e_if(e_search("__topic__==overall_type"), e_drop_fields("servers"))


# 处理client_status日志
e_if(e_search("__topic__==client_status"), 
       e_compose(
                 e_split("clients"), 
                 e_json("clients", depth=1),
                 e_drop_fields("clients")
    ))
AI 代码解读

方案优化

一个边界问题

注意到以上方案对于content.serverscontent.servers是空时的处理有一些问题,

假设原始日志是:

__source__:  1.2.3.4
__topic__:  
content:{
            "service": "search_service",
            "overal_status": "yellow",
            "servers": [ ],
            "clients": [ ]
}
AI 代码解读

会被分裂为3条日志, 其中主题是client_status和server_status的日志内容是空的.

__source__:  1.2.3.4
__topic__:  overall_type
client_count:  0
overal_status:  yellow
server_count:  0
service:  search_service


__source__:  1.2.3.4
__topic__:  client_status
service:  search_service
__source__:  1.2.3.4


__topic__:  server_status
host:  1.2.3.4
status:  green
service:  search_service
AI 代码解读

方案1

这里可以在初始分裂后, 处理server_statusclient_status日志前分别判断并丢弃空的相关事件:

# 处理server_status: 空的丢弃(非空保留)
e_keep(op_and(e_search("__topic__==server_status"), json_select(v("servers"), "length([*])")))

# 处理client_status: 空的丢弃(非空保留)
e_keep(op_and(e_search("__topic__==client_status"), json_select(v("clients"), "length([*])")))
AI 代码解读

综合

综上,LOG DSL规则是


# 总体分裂
e_set("__topic__", "server_status,client_status,overall_type")
e_split("__topic__")
e_json('content',depth=1)
e_drop_fields("content")

# 处理overall_type日志
e_if(e_search("__topic__==overall_type"), 
       e_compose(
                 e_set("client_count" json_select(v("clients"), "length([*])", default=0)), 
                 e_set("server_count" json_select(v("servers"), "length([*])", default=0))
    ))

# 新加: 预处理server_status: 空的丢弃(非空保留) 
e_keep(op_and(e_search("__topic__==server_status"), json_select(v("servers"), "length([*])")))

# 处理server_status日志
e_if(e_search("__topic__==server_status"), 
       e_compose(
                 e_split("servers"), 
                 e_json("servers", depth=1)
    ))
e_if(e_search("__topic__==overall_type"), e_drop_fields("servers"))


# 新加: 预处理client_status: 空的丢弃(非空保留) 
e_keep(op_and(e_search("__topic__==client_status"), json_select(v("clients"), "length([*])")))

# 处理client_status日志
e_if(e_search("__topic__==client_status"), 
       e_compose(
                 e_split("clients"), 
                 e_json("clients", depth=1),
                 e_drop_fields("clients")
    ))
AI 代码解读

方案2

在初始分裂时进行判断, 如果对应数据是空的就不分裂出更多事件:

# 初始主题
e_set("__topic__", "server_status")

# 如果content.servers非空, 则从server_status分裂出1条日志
e_if(json_select(v("content"), "length(servers[*])"),
     e_compse(
           e_set("__topic__", "server_status,overall_type"),
           e_split("__topic__")
     ))

# 如果content.clients非空, 则从overall_type再分裂出1条日志
e_if(op_and(e_search("__topic__==overall_type"), json_select(v("content"), "length(clients[*])")),
     e_compse(
           e_set("__topic__", "client_status,overall_type"),
           e_split("__topic__")
     ))
AI 代码解读

综合

综上,LOG DSL规则是


# 总体分裂
e_set("__topic__", "server_status")

# 如果content.servers非空, 则从server_status分裂出1条日志
e_if(json_select(v("content"), "length(servers[*])"),
     e_compse(
           e_set("__topic__", "server_status,overall_type"),
        e_split("__topic__")
     ))

# 如果content.clients非空, 则从server_status分裂出1条日志
e_if(op_and(e_search("__topic__==overall_type"), json_select(v("content"), "length(clients[*])")),
     e_compse(
           e_set("__topic__", "client_status,overall_type"),
        e_split("__topic__")
     ))

# 处理overall_type日志
e_if(e_search("__topic__==overall_type"), 
       e_compose(
                 e_set("client_count" json_select(v("clients"), "length([*])", default=0)), 
                 e_set("server_count" json_select(v("servers"), "length([*])", default=0))
    ))

# 处理server_status日志
e_if(e_search("__topic__==server_status"), 
       e_compose(
                 e_split("servers"), 
                 e_json("servers", depth=1)
    ))
e_if(e_search("__topic__==overall_type"), e_drop_fields("servers"))


# 处理client_status日志
e_if(e_search("__topic__==client_status"), 
       e_compose(
                 e_split("clients"), 
                 e_json("clients", depth=1),
                 e_drop_fields("clients")
    ))
AI 代码解读

比较

方案1会在分裂出日志后再删除, 逻辑上有些多余, 但规则简单易维护. 默认推荐.
方案2会在分裂前进行判断, 处理效率会高一些, 但规则略微冗余, 仅在特定场景(例如初始分裂可能导致大量额外事件产生)时推荐.

进一步参考

欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
image

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
打赏
0
0
0
0
3963
分享
相关文章
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——封装统一返回的数据结构
本文介绍了在Spring Boot中封装统一返回的数据结构的方法。通过定义一个泛型类`JsonResult<T>`,包含数据、状态码和提示信息三个属性,满足不同场景下的JSON返回需求。例如,无数据返回时可设置默认状态码"0"和消息"操作成功!",有数据返回时也可自定义状态码和消息。同时,文章展示了如何在Controller中使用该结构,通过具体示例(如用户信息、列表和Map)说明其灵活性与便捷性。最后总结了Spring Boot中JSON数据返回的配置与实际项目中的应用技巧。
61 0
|
10天前
|
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——使用 fastJson 处理 null
本文介绍如何使用 fastJson 处理 null 值。与 Jackson 不同,fastJson 需要通过继承 `WebMvcConfigurationSupport` 类并覆盖 `configureMessageConverters` 方法来配置 null 值的处理方式。例如,可将 String 类型的 null 转为 "",Number 类型的 null 转为 0,避免循环引用等。代码示例展示了具体实现步骤,包括引入相关依赖、设置序列化特性及解决中文乱码问题。
34 0
|
10天前
|
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——Spring Boot 默认对Json的处理
本文介绍了在Spring Boot中返回Json数据的方法及数据封装技巧。通过使用`@RestController`注解,可以轻松实现接口返回Json格式的数据,默认使用的Json解析框架是Jackson。文章详细讲解了如何处理不同数据类型(如类对象、List、Map)的Json转换,并提供了自定义配置以应对null值问题。此外,还对比了Jackson与阿里巴巴FastJson的特点,以及如何在项目中引入和配置FastJson,解决null值转换和中文乱码等问题。
38 0
如何在Python中高效实现CSV到JSON的数据转换
在实际项目中,数据格式转换是常见问题,尤其从CSV到JSON的转换。本文深入探讨了多种转换方法,涵盖Python基础实现、数据预处理、错误处理、性能优化及调试验证技巧。通过分块处理、并行处理等手段提升大文件转换效率,并介绍如何封装为命令行工具或Web API,实现自动化批量处理。关键点包括基础实现、数据清洗、异常捕获、性能优化和单元测试,确保转换流程稳定高效。
161 83
JSON数据解析实战:从嵌套结构到结构化表格
在信息爆炸的时代,从杂乱数据中提取精准知识图谱是数据侦探的挑战。本文以Google Scholar为例,解析嵌套JSON数据,提取文献信息并转换为结构化表格,通过Graphviz制作技术关系图谱,揭示文献间的隐秘联系。代码涵盖代理IP、请求头设置、JSON解析及可视化,提供完整实战案例。
JSON数据解析实战:从嵌套结构到结构化表格
python语言采集淘宝商品详情数据,json数据示例返回
通过淘宝开放平台的API接口,开发者可以轻松获取商品详情数据,并利用这些数据进行商品分析、价格监控、库存管理等操作。本文提供的示例代码和JSON数据解析方法,可以帮助您快速上手淘宝商品数据的采集与处理。
淘宝商品详情API接口概述与JSON数据示例
淘宝商品详情API是淘宝开放平台提供的核心接口之一,为开发者提供了获取商品深度信息的能力。以下是技术细节和示例:
处理从API返回的JSON数据时返回Unicode编码字符串怎么处理
在处理API返回的JSON数据时,遇到类似`\u7f51\u7edc\u8fde\u63a5\u9519\u8bef`的Unicode编码字符串,可使用JavaScript内置方法转换为可读文字。主要方法包括:1. 使用`JSON.parse`自动解析;2. 使用`decodeURIComponent`和`escape`组合解码;3. 在API调用中直接处理响应数据。这些方法能有效处理多语言内容,确保正确显示非ASCII字符。
|
3月前
|
解析电商商品详情API接口系列,json数据示例参考
电商商品详情API接口是电商平台的重要组成部分,提供了商品的详细信息,支持用户进行商品浏览和购买决策。通过合理的API设计和优化,可以提升系统性能和用户体验。希望本文的解析和示例能够为开发者提供参考,帮助构建高效、可靠的电商系统。
73 12
淘宝商品评论数据API接口详解及JSON示例返回
淘宝商品评论数据API接口是淘宝开放平台提供的一项服务,旨在帮助开发者通过编程方式获取淘宝商品的评论数据。这些数据包括评论内容、评论时间、评论者信息、评分等,对于电商分析、用户行为研究、竞品分析等领域都具有极高的价值。

云存储

+关注

相关产品

  • 日志服务
  • AI助理

    你好,我是AI助理

    可以解答问题、推荐解决方案等