Tous les produits
Search
Centre de documentation

Simple Log Service:Transformation de données JSON complexes

Dernière mise à jour :Aug 18, 2026

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

    1. Divisez le journal brut en trois journaux distincts selon le champ topic : overall_type, client_status et server_status.

    2. 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

    1. 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...
      }
    2. Développez le contenu JSON de premier niveau du champ content, puis supprimez le champ content.

      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
    3. Pour le journal dont le topic est overall_type, calculez les valeurs de client_count et de 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))
        ))

      Le journal traité est le suivant :

      __topic__:  overall_type
      server_count:  2
      client_count:  2
    4. Supprimez les champs inutiles.

      e_if(e_search("__topic__==overall_type"), e_drop_fields("clients", "servers"))
    5. 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
    6. Champs pertinents à conserver :

      e_if(e_search("__topic__==overall_type"), e_drop_fields("servers"))
    7. Divisez davantage le journal dont le topic est client_status, puis supprimez le champ clients.

      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
    8. 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_status et client_status sont 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

    1. Divisez et développez le journal en fonction de users dans le champ content.

      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
    2. 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
    3. 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
    4. 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.