Cette rubrique explique comment utiliser la fonctionnalité de transformation des données de Simple Log Service (SLS) pour transformer des données JSON complexes.
Transformation de données JSON complexes comportant plusieurs sous-clés sous forme de tableaux
Les programmes génèrent souvent des journaux au format JSON statistique. Ces journaux incluent généralement des informations de base et plusieurs sous-clés sous forme de tableaux. Par exemple, un serveur écrit un journal chaque minute. Ce journal contient l'état actuel ainsi que des informations statistiques sur les nœuds serveur et client associés.
-
Exemple de journal
__source__: 192.0.2.1 __topic__: content:{ "service": "search_service", "overal_status": "yellow", "servers": [ { "host": "192.0.2.1", "status": "green" }, { "host": "192.0.2.2", "status": "green" } ], "clients": [ { "host": "192.0.2.3", "status": "green" }, { "host": "192.0.2.4", "status": "red" } ] } -
Exigences en matière de transformation des données
Divisez le journal brut en trois journaux distincts selon le champ
topic:overall_type,client_statusetserver_status.-
Stockez des informations différentes selon les valeurs de
topic.overall_type: conservez le nombre de serveurs, le nombre de clients, la couleur de overal_status et les informations sur le service.client_status: conservez l'adresse hôte, l'état et les informations sur le service.server_status: conservez l'adresse hôte, l'état et les informations sur le service.
-
Résultat attendu
__source__: 192.0.2.1 __topic__: overall_type client_count: 2 overal_status: yellow server_count: 2 service: search_service __source__: 192.0.2.1 __topic__: client_status host: 192.0.2.4 status: red service: search_service __source__: 192.0.2.1 __topic__: client_status host: 192.0.2.3 status: green service: search_service __source__: 192.0.2.1 __topic__: server_status host: 192.0.2.1 status: green service: search_service __source__: 192.0.2.1 __topic__: server_status host: 192.0.2.2 status: green service: search_service -
Solution
-
Divisez le journal en trois journaux distincts en attribuant trois valeurs différentes au champ topic. Après la division, vous obtenez trois journaux identiques, à l'exception du champ
topic.e_set("__topic__", "server_status,client_status,overall_type") e_split("__topic__")Le format du journal après traitement est le suivant :
__source__: 192.0.2.1 __topic__: server_status // The other two logs have `client_status` and `overall_type` as topics. The rest of the fields are the same. content: { ...As before... } -
Développez le contenu JSON de premier niveau du champ
content, puis supprimez le champcontent.e_json('content',depth=1) e_drop_fields("content")Le format du journal après traitement est le suivant :
__source__: 192.0.2.1 __topic__: overall_type // The other two logs have `client_status` and `overall_type` as topics. The rest of the fields are the same. clients: [{"host": "192.0.2.3", "status": "green"}, {"host": "192.0.2.4", "status": "red"}] overal_status: yellow servers: [{"host": "192.0.2.1", "status": "green"}, {"host": "192.0.2.2", "status": "green"}] service: search_service -
Pour le journal dont le topic est
overall_type, calculez les valeurs declient_countet deserver_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)) ))Le journal traité est le suivant :
__topic__: overall_type server_count: 2 client_count: 2 -
Supprimez les champs inutiles.
e_if(e_search("__topic__==overall_type"), e_drop_fields("clients", "servers")) -
Divisez davantage le journal dont le topic est
server_status.e_if(e_search("__topic__==server_status"), e_compose( e_split("servers"), e_json("servers", depth=1) ))Le journal est divisé en deux journaux :
__topic__: server_status servers: {"host": "192.0.2.1", "status": "green"} host: 192.0.2.1 status: green__topic__: server_status servers: {"host": "192.0.2.2", "status": "green"} host: 192.0.2.2 status: green -
Champs pertinents à conserver :
e_if(e_search("__topic__==overall_type"), e_drop_fields("servers")) -
Divisez davantage le journal dont le topic est
client_status, puis supprimez le champclients.e_if(e_search("__topic__==client_status"), e_compose( e_split("clients"), e_json("clients", depth=1), e_drop_fields("clients") ))Le journal est divisé en deux journaux :
__topic__: client_status host: 192.0.2.3 status: green__topic__: clients host: 192.0.2.4 status: red -
Voici les règles complètes du langage spécifique au domaine (DSL) LOG :
# Split the log. e_set("__topic__", "server_status,client_status,overall_type") e_split("__topic__") e_json('content',depth=1) e_drop_fields("content") # Process the overall_type log. 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)) )) # Process the server_status log. 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")) # Process the client_status log. e_if(e_search("__topic__==client_status"), e_compose( e_split("clients"), e_json("clients", depth=1), e_drop_fields("clients") ))
-
Optimisation de la solution
La solution précédente pose problème lorsque content.servers et content.servers sont vides. Prenons l'exemple du journal brut suivant :
__source__: 192.0.2.1
__topic__:
content:{
"service": "search_service",
"overal_status": "yellow",
"servers": [ ],
"clients": [ ]
}
Si vous utilisez la solution précédente pour diviser ce journal brut en trois journaux, les journaux ayant pour topics client_status et server_status seront vides.
__source__: 192.0.2.1
__topic__: overall_type
client_count: 0
overal_status: yellow
server_count: 0
service: search_service
__source__: 192.0.2.1
__topic__: client_status
service: search_service
__source__: 192.0.2.1
__topic__: server_status
host: 192.0.2.1
status: green
service: search_service
-
Solution 1
Après la division initiale, vérifiez si les journaux ayant pour topics
server_statusetclient_statussont vides. Si c'est le cas, ignorez-les.# For server_status: discard if empty, retain if not. e_keep(op_and(e_search("__topic__==server_status"), json_select(v("servers"), "length([*])"))) # For client_status: discard if empty, retain if not. e_keep(op_and(e_search("__topic__==client_status"), json_select(v("clients"), "length([*])")))Voici les règles complètes du DSL LOG :
# Split the log. e_set("__topic__", "server_status,client_status,overall_type") e_split("__topic__") e_json('content',depth=1) e_drop_fields("content") # Process the overall_type log. 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)) )) # New: Pre-process server_status: discard if empty, retain if not. e_keep(op_and(e_search("__topic__==server_status"), json_select(v("servers"), "length([*])"))) # Process the server_status log. 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")) # New: Pre-process client_status: discard if empty, retain if not. e_keep(op_and(e_search("__topic__==client_status"), json_select(v("clients"), "length([*])"))) # Process the client_status log. e_if(e_search("__topic__==client_status"), e_compose( e_split("clients"), e_json("clients", depth=1), e_drop_fields("clients") )) -
Solution 2
Vérifiez si un champ est vide avant de diviser le journal. Si le champ n'est pas vide, divisez le journal en fonction de ce champ.
# Set the initial topic. e_set("__topic__", "server_status") # If the content.servers field is not empty, split the log to create a log with the topic server_status. e_if(json_select(v("content"), "length(servers[*])"), e_compose( e_set("__topic__", "server_status,overall_type"), e_split("__topic__") )) # If the content.clients field is not empty, further split the log to create a log with the topic client_status. e_if(op_and(e_search("__topic__==overall_type"), json_select(v("content"), "length(clients[*])")), e_compose( e_set("__topic__", "client_status,overall_type"), e_split("__topic__") ))Voici les règles complètes du DSL LOG :
# Split the log. e_set("__topic__", "server_status") # If the content.servers field is not empty, split the log to create a log with the topic server_status. e_if(json_select(v("content"), "length(servers[*])"), e_compose( e_set("__topic__", "server_status,overall_type"), e_split("__topic__") )) # If the content.clients field is not empty, further split the log to create a log with the topic client_status. e_if(op_and(e_search("__topic__==overall_type"), json_select(v("content"), "length(clients[*])")), e_compose( e_set("__topic__", "client_status,overall_type"), e_split("__topic__") )) # Process the overall_type log. 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)) )) # Process the server_status log. 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")) # Process the client_status log. e_if(e_search("__topic__==client_status"), e_compose( e_split("clients"), e_json("clients", depth=1), e_drop_fields("clients") ))
Comparaison des solutions
La solution 1 est logiquement redondante, car elle crée des journaux vides à partir du journal brut pour ensuite les supprimer. Toutefois, les règles sont simples et faciles à maintenir. Cette solution est recommandée par défaut.
La solution 2 est plus efficace, car elle vérifie la présence de champs vides avant la division. Cependant, les règles sont légèrement plus complexes. Cette solution est recommandée uniquement dans des scénarios spécifiques, par exemple lorsque la division initiale risque de générer de nombreux événements supplémentaires.
Transformation de données JSON complexes avec des objets de tableau imbriqués sur plusieurs niveaux
Cet exemple montre comment traiter un objet complexe contenant des tableaux imbriqués sur plusieurs niveaux. L'objectif est de diviser chaque événement de connexion du tableau login_histories pour chaque objet du tableau users en un événement de connexion distinct.
-
Journal brut
__source__: 192.0.2.1 __topic__: content:{ "users": [ { "name": "user1", "login_histories": [ { "date": "2019-10-10 0:0:0", "login_ip": "192.0.2.6" }, { "date": "2019-10-10 1:0:0", "login_ip": "192.0.2.6" }, { ...More logon information... } ] }, { "name": "user2", "login_histories": [ { "date": "2019-10-11 0:0:0", "login_ip": "192.0.2.7" }, { "date": "2019-10-11 1:0:0", "login_ip": "192.0.2.9" }, { ...More logon information... } ] }, { ...More users... } ] } -
Journaux attendus après division
__source__: 192.0.2.1 name: user1 date: 2019-10-11 1:0:0 login_ip: 192.0.2.6 __source__: 192.0.2.1 name: user1 date: 2019-10-11 0:0:0 login_ip: 192.0.2.6 __source__: 192.0.2.1 name: user2 date: 2019-10-11 0:0:0 login_ip: 192.0.2.7 __source__: 192.0.2.1 name: user2 date: 2019-10-11 1:0:0 login_ip: 192.0.2.9 ...More logs... -
Solution
-
Divisez et développez le journal en fonction de
usersdans le champcontent.e_split("content", jmes='users[*]', output='item') e_json("item",depth=1)Les journaux traités sont les suivants :
__source__: 192.0.2.1 __topic__: content:{...Same as that in the raw log...} item: {"name": "user1", "login_histories": [{"date": "2019-10-10 0:0:0", "login_ip": "192.0.2.6"}, {"date": "2019-10-10 1:0:0", "login_ip": "192.0.2.6"}]} login_histories: [{"date": "2019-10-10 0:0:0", "login_ip": "192.0.2.6"}, {"date": "2019-10-10 1:0:0", "login_ip": "192.0.2.6"}] name: user1 __source__: 192.0.2.1 __topic__: content:{...Same as that in the raw log...} item: {"name": "user2", "login_histories": [{"date": "2019-10-11 0:0:0", "login_ip": "192.0.2.7"}, {"date": "2019-10-11 1:0:0", "login_ip": "192.0.2.9"}]} login_histories: [{"date": "2019-10-11 0:0:0", "login_ip": "192.0.2.7"}, {"date": "2019-10-11 1:0:0", "login_ip": "192.0.2.9"}] name: user2 -
Ensuite, divisez puis développez les données en fonction de
login_histories.e_split("login_histories") e_json("login_histories", depth=1)Les journaux traités sont les suivants :
__source__: 192.0.2.1 __topic__: content: {...Same as that in the raw log...} date: 2019-10-11 0:0:0 item: {"name": "user2", "login_histories": [{"date": "2019-10-11 0:0:0", "login_ip": "192.0.2.7"}, {"date": "2019-10-11 1:0:0", "login_ip": "192.0.2.9"}]} login_histories: {"date": "2019-10-11 0:0:0", "login_ip": "192.0.2.7"} login_ip: 192.0.2.7 name: user2 __source__: 192.0.2.1 __topic__: content: {...Same as that in the raw log...} date: 2019-10-11 1:0:0 item: {"name": "user2", "login_histories": [{"date": "2019-10-11 0:0:0", "login_ip": "192.0.2.7"}, {"date": "2019-10-11 1:0:0", "login_ip": "192.0.2.9"}]} login_histories: {"date": "2019-10-11 1:0:0", "login_ip": "192.0.2.9"} login_ip: 192.0.2.9 name: user2 __source__: 192.0.2.1 __topic__: content: {...Same as that in the raw log...} date: 2019-10-10 1:0:0 item: {"name": "user1", "login_histories": [{"date": "2019-10-10 0:0:0", "login_ip": "192.0.2.6"}, {"date": "2019-10-10 1:0:0", "login_ip": "192.0.2.6"}]} login_histories: {"date": "2019-10-10 1:0:0", "login_ip": "192.0.2.6"} login_ip: 192.0.2.6 name: user1 __source__: 192.0.2.1 __topic__: content: {...Same as that in the raw log...} date: 2019-10-10 0:0:0 item: {"name": "user1", "login_histories": [{"date": "2019-10-10 0:0:0", "login_ip": "192.0.2.6"}, {"date": "2019-10-10 1:0:0", "login_ip": "192.0.2.6"}]} login_histories: {"date": "2019-10-10 0:0:0", "login_ip": "192.0.2.6"} login_ip: 192.0.2.6 name: user1 -
Enfin, supprimez les champs non pertinents.
e_drop_fields("content", "item", "login_histories")Les journaux traités sont les suivants :
__source__: 192.0.2.1 __topic__: name: user1 date: 2019-10-11 1:0:0 login_ip: 192.0.2.6 __source__: 192.0.2.1 __topic__: name: user1 date: 2019-10-11 0:0:0 login_ip: 192.0.2.6 __source__: 192.0.2.1 __topic__: name: user2 date: 2019-10-11 0:0:0 login_ip: 192.0.2.7 __source__: 192.0.2.1 __topic__: name: user2 date: 2019-10-11 1:0:0 login_ip: 192.0.2.9 -
Voici les règles complètes du DSL LOG :
e_split("content", jmes='users[*]', output='item') e_json("item",depth=1) e_split("login_histories") e_json("login_histories", depth=1) e_drop_fields("content", "item", "login_histories")
-
Résumé : pour des exigences similaires, commencez par diviser le journal, puis développez les données et enfin supprimez les champs non pertinents.