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

本文涉及的产品
对象存储 OSS,20GB 3个月
日志服务 SLS,月写入数据量 50GB 1个月
阿里云盘企业版 CDE,企业版用户数5人 500GB空间
简介: 程序构建的日志经常会以一种统计性质的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"
         }
     ]
}

加工需求

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

解决方案

初步处理

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

e_set("__topic__", "server_status,client_status,overall_type")
e_split("__topic__")

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

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

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

e_json('content',depth=1)
e_drop_fields("content")

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

__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  

处理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))
    ))

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

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

处理server_status日志

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

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

__topic__:  server_status
servers:  {"host": "1.2.3.4", "status": "green"}
host: 1.2.3.4
status: green

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

处理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")
    ))

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

__topic__:  client_status
host: 1.2.3.6
status: green

__topic__:  clients
host: 1.2.3.7
status: red

综合

综上,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")
    ))

方案优化

一个边界问题

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

假设原始日志是:

__source__:  1.2.3.4
__topic__:  
content:{
            "service": "search_service",
            "overal_status": "yellow",
            "servers": [ ],
            "clients": [ ]
}

会被分裂为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

方案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([*])")))

综合

综上,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")
    ))

方案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__")
     ))

综合

综上,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")
    ))

比较

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

进一步参考

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

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
27天前
|
存储 Oracle 关系型数据库
【赵渝强老师】MySQL InnoDB的数据文件与重做日志文件
本文介绍了MySQL InnoDB存储引擎中的数据文件和重做日志文件。数据文件包括`.ibd`和`ibdata`文件,用于存放InnoDB数据和索引。重做日志文件(redo log)确保数据的可靠性和事务的持久性,其大小和路径可由相关参数配置。文章还提供了视频讲解和示例代码。
131 11
【赵渝强老师】MySQL InnoDB的数据文件与重做日志文件
|
5月前
|
SQL 运维 监控
SLS 数据加工全面升级,集成 SPL 语法
在系统开发、运维过程中,日志是最重要的信息之一,其最大的优点是简单直接。SLS 数据加工功能旨在解决非结构化的日志数据处理,当前全面升级,集成 SPL 语言、更强的数据处理性能、更优的使用成本。
18192 141
|
27天前
|
SQL Oracle 关系型数据库
【赵渝强老师】Oracle的联机重做日志文件与数据写入过程
在Oracle数据库中,联机重做日志文件记录了数据库的变化,用于实例恢复。每个数据库有多组联机重做日志,每组建议至少有两个成员。通过SQL语句可查看日志文件信息。视频讲解和示意图进一步解释了这一过程。
|
2月前
|
数据采集 机器学习/深度学习 存储
使用 Python 清洗日志数据
使用 Python 清洗日志数据
41 2
|
4月前
|
存储 消息中间件 人工智能
AI大模型独角兽 MiniMax 基于阿里云数据库 SelectDB 版内核 Apache Doris 升级日志系统,PB 数据秒级查询响应
早期 MiniMax 基于 Grafana Loki 构建了日志系统,在资源消耗、写入性能及系统稳定性上都面临巨大的挑战。为此 MiniMax 开始寻找全新的日志系统方案,并基于阿里云数据库 SelectDB 版内核 Apache Doris 升级了日志系统,新系统已接入 MiniMax 内部所有业务线日志数据,数据规模为 PB 级, 整体可用性达到 99.9% 以上,10 亿级日志数据的检索速度可实现秒级响应。
AI大模型独角兽 MiniMax 基于阿里云数据库 SelectDB 版内核 Apache Doris 升级日志系统,PB 数据秒级查询响应
|
4月前
|
缓存 NoSQL Linux
【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据
【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据
139 1
【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据
|
3月前
|
SQL 人工智能 运维
在阿里云日志服务轻松落地您的AI模型服务——让您的数据更容易产生洞见和实现价值
您有大量的数据,数据的存储和管理消耗您大量的成本,您知道这些数据隐藏着巨大的价值,但是您总觉得还没有把数据的价值变现出来,对吗?来吧,我们用一系列的案例帮您轻松落地AI模型服务,实现数据价值的变现......
227 3
|
4月前
|
存储 监控 网络协议
在Linux中,如何使用 tcpdump 监听主机为 192.168.1.1,tcp 端⼝为 80 的数据,并将将输出结果保存输出到tcpdump.log?
在Linux中,如何使用 tcpdump 监听主机为 192.168.1.1,tcp 端⼝为 80 的数据,并将将输出结果保存输出到tcpdump.log?
|
4月前
|
数据库 Java 监控
Struts 2 日志管理化身神秘魔法师,洞察应用运行乾坤,演绎奇幻篇章!
【8月更文挑战第31天】在软件开发中,了解应用运行状况至关重要。日志管理作为 Struts 2 应用的关键组件,记录着每个动作和决策,如同监控摄像头,帮助我们迅速定位问题、分析性能和使用情况,为优化提供依据。Struts 2 支持多种日志框架(如 Log4j、Logback),便于配置日志级别、格式和输出位置。通过在 Action 类中添加日志记录,我们能在开发过程中获取详细信息,及时发现并解决问题。合理配置日志不仅有助于调试,还能分析用户行为,提升应用性能和稳定性。
59 0
|
4月前
|
开发者 前端开发 编解码
Vaadin解锁移动适配新境界:一招制胜,让你的应用征服所有屏幕!
【8月更文挑战第31天】在移动互联网时代,跨平台应用开发备受青睐。作为一款基于Java的Web应用框架,Vaadin凭借其组件化设计和强大的服务器端渲染能力,助力开发者轻松构建多设备适应的Web应用。本文探讨Vaadin与移动设备的适配策略,包括响应式布局、CSS媒体查询、TouchKit插件及服务器端优化,帮助开发者打造美观且实用的移动端体验。通过这些工具和策略的应用,可有效应对屏幕尺寸、分辨率及操作系统的多样性挑战,满足广大移动用户的使用需求。
68 0

相关产品

  • 日志服务