Logstash pour les devs - 13 - Filtres KV, JSON et XML : parser les formats structures

Extraire des champs depuis des paires clé-valeur, du JSON inline et du XML avec les filtres kv, json et xml.

  1. 01 Logstash pour les devs - 00 - Pourquoi Logstash existe encore en 2026
  2. 02 Logstash pour les devs - 01 - L'Elastic Stack de A a Z
  3. 03 Logstash pour les devs - 02 - Installer Logstash avec Docker en 5 minutes
  4. 04 Logstash pour les devs - 03 - Anatomie d'un pipeline Logstash
  5. 05 Logstash pour les devs - 04 - Inputs stdin et file : lire des donnees locales
  6. 06 Logstash pour les devs - 05 - Input Beats : recevoir des logs de Filebeat
  7. 07 Logstash pour les devs - 06 - Inputs HTTP, TCP et UDP : recevoir des donnees réseau
  8. 08 Logstash pour les devs - 07 - Inputs Kafka et JDBC : sources avancees
  9. 09 Logstash pour les devs - 08 - Les codecs : decoder et encoder les donnees
  10. 10 Logstash pour les devs - 09 - Le filtre Grok : parser n'importe quel log
  11. 11 Logstash pour les devs - 10 - Le filtre Dissect : parser sans regex
  12. 12 Logstash pour les devs - 11 - Le filtre Mutate : transformer les champs
  13. 13 Logstash pour les devs - 12 - Filtres Date et GeoIP : temps et geolocalisation
  14. 14 Logstash pour les devs - 13 - Filtres KV, JSON et XML : parser les formats structures
  15. 15 Logstash pour les devs - 14 - Le filtre Ruby : quand les autres ne suffisent pas
  16. 16 Logstash pour les devs - 15 - Filtres Aggregate et Metrics : correler les événements
  17. 17 Logstash pour les devs - 16 - Conditionnels et contrôle de flux
  18. 18 Logstash pour les devs - 17 - Output Elasticsearch : envoyer les donnees
  19. 19 Logstash pour les devs - 18 - Outputs file, stdout et les autres
  20. 20 Logstash pour les devs - 19 - Gerer le multiline : stack traces et logs multi-lignes
  21. 21 Logstash pour les devs - 20 - Pipelines multiples et pipeline-to-pipeline
  22. 22 Logstash pour les devs - 21 - Performance et tuning Logstash
  23. 23 Logstash pour les devs - 22 - Monitoring Logstash : metriques et alertes
  24. 24 Logstash pour les devs - 23 - Dead Letter Queue : ne plus perdre d'événements
  25. 25 Logstash pour les devs - 24 - Sécurité Logstash : SSL, auth et secrets
  26. 26 Logstash pour les devs - 25 - Debugger un pipeline Logstash
  27. 27 Logstash pour les devs - 26 - Tester ses pipelines avant la prod
  28. 28 Logstash pour les devs - 27 - Cas pratique : centraliser des logs applicatifs
  29. 29 Logstash pour les devs - 28 - Cas pratique : ETL avec Logstash et PostgreSQL
  30. 30 Logstash pour les devs - 29 - Cas pratique : enrichir des donnees en temps réel
  31. 31 Logstash pour les devs - 30 - Logstash en production : architecture et bonnes pratiques
  32. 32 Logstash pour les devs - 31 - Glossaire Logstash de A a Z

13 - Filtres KV, JSON et XML : parser les formats structures

Ce que tu vas apprendre

  • Parser des paires clé=valeur avec le filtre KV
  • Extraire du JSON inline (un champ qui contient du JSON dans un string)
  • Parser du XML et naviguer avec XPath
  • Quand utiliser un filtre vs un codec

Prerequisites


Le filtre KV : paires clé-valeur

Les logs applicatifs utilisent souvent un format clé=valeur. Pas du JSON, pas du CSV, juste des paires separees par des espaces :

ts=2026-03-31T14:23:01Z level=ERROR service=api-users method=POST path=/users status=500 duration=5023 msg="Connection refused"

Le filtre KV parse ca sans regex.

Configuration de base

filter {
  kv {
    source => "message"
    field_split => " "
    value_split => "="
  }
}

Résultat :

json{
  "ts": "2026-03-31T14:23:01Z",
  "level": "ERROR",
  "service": "api-users",
  "method": "POST",
  "path": "/users",
  "status": "500",
  "duration": "5023",
  "msg": "Connection refused"
}

KV a détecté le separateur = entre les clés et les valeurs, et les espaces entre les paires. Les valeurs entre guillemets ("Connection refused") sont extraites sans les guillemets.

Paramètres utiles

filter {
  kv {
    source => "message"
    field_split => " "
    value_split => "="

    # Ne garder que certaines cles
    include_keys => ["level", "service", "status", "duration", "msg"]

    # Ou exclure des cles
    # exclude_keys => ["ts"]

    # Prefixer les champs extraits
    prefix => "app_"

    # Stocker dans un sous-objet
    target => "parsed"

    # Supprimer les guillemets des valeurs
    trim_value => "\""
    trim_key => " "
  }
}

Avec target => "parsed", les champs vont dans [parsed][level], [parsed][service], etc. Ca évité de polluer la racine de l'événement.

Parser des query strings

KV est parfait pour les query strings d'URL :

# Ligne de log avec une URL
GET /search?q=logstash&page=2&lang=fr HTTP/1.1

Apres extraction de la query string par Grok :

filter {
  grok {
    match => { "message" => "%{WORD:method} %{URIPATH:path}\?%{DATA:query_string} HTTP/%{NUMBER}" }
  }

  kv {
    source => "query_string"
    field_split => "&"
    value_split => "="
    target => "params"
  }
}

Résultat : params.q = "logstash", params.page = "2", params.lang = "fr".

Le filtre JSON

Codec json vs filtre json

On a vu le codec json dans l'article 08. Le codec agit a l'entree, il parse les donnees brutes. Le filtre JSON agit au milieu du pipeline, il parse un champ qui contient du JSON dans un string.

Le cas classique : un log texte qui contient un champ JSON embarque.

2026-03-31 14:23:01 INFO [api] Request completed {"user_id":42,"action":"login","ip":"192.168.1.10","duration_ms":156}

Grok extrait la partie JSON dans un champ, puis le filtre JSON la parse.

Configuration de base

filter {
  grok {
    match => {
      "message" => "%{TIMESTAMP_ISO8601:ts} %{LOGLEVEL:level} \[%{DATA:service}\] %{DATA:log_msg} %{GREEDYDATA:json_data}"
    }
  }

  json {
    source => "json_data"
    target => "request"
    remove_field => ["json_data"]
  }
}

Résultat :

json{
  "ts": "2026-03-31 14:23:01",
  "level": "INFO",
  "service": "api",
  "log_msg": "Request completed",
  "request": {
    "user_id": 42,
    "action": "login",
    "ip": "192.168.1.10",
    "duration_ms": 156
  }
}

Le target => "request" met les champs JSON dans un sous-objet. Sans target, les champs se retrouvent a la racine de l'événement et peuvent ecraser des champs existants.

JSON a la racine

Si tout l'événement est du JSON (et que tu n'as pas utilise le codec json sur l'input pour une raison quelconque) :

filter {
  json {
    source => "message"
  }
}

Sans target, les champs JSON ecrasent les champs existants. Si le JSON contient un champ message, il ecrase le message original. C'est souvent ce qu'on veut pour des logs JSON purs.

Gestion des erreurs

Si le champ ne contient pas du JSON valide :

filter {
  json {
    source => "json_data"
    tag_on_failure => ["_jsonparsefailure"]
  }
}

L'événement est garde tel quel avec le tag _jsonparsefailure. Le champ source n'est pas modifie.

Le filtre XML

Plus rare, mais indispensable quand tu recois du XML (APIs legacy, fichiers de config, logs d'applications Java enterprise).

Configuration de base

filter {
  xml {
    source => "xml_data"
    target => "parsed_xml"
    store_xml => true
  }
}

Entree (champ xml_data) :

xml<event>
  <timestamp>2026-03-31T14:23:01Z</timestamp>
  <level>ERROR</level>
  <service>payment-gateway</service>
  <message>Transaction failed</message>
  <transaction>
    <id>txn-abc123</id>
    <amount>49.99</amount>
    <currency>EUR</currency>
  </transaction>
</event>

Résultat :

json{
  "parsed_xml": {
    "timestamp": ["2026-03-31T14:23:01Z"],
    "level": ["ERROR"],
    "service": ["payment-gateway"],
    "message": ["Transaction failed"],
    "transaction": [{
      "id": ["txn-abc123"],
      "amount": ["49.99"],
      "currency": ["EUR"]
    }]
  }
}

Note : XML est converti en tableaux (meme pour les éléments uniques), parce que XML permet des éléments repetes.

Extraire avec XPath

Pour cibler des éléments spécifiques sans garder tout le XML :

filter {
  xml {
    source => "xml_data"
    store_xml => false
    xpath => {
      "/event/level/text()" => "level"
      "/event/service/text()" => "service"
      "/event/message/text()" => "error_msg"
      "/event/transaction/id/text()" => "transaction_id"
      "/event/transaction/amount/text()" => "amount"
    }
  }
}

Résultat :

json{
  "level": ["ERROR"],
  "service": ["payment-gateway"],
  "error_msg": ["Transaction failed"],
  "transaction_id": ["txn-abc123"],
  "amount": ["49.99"]
}

Les résultats XPath sont toujours des tableaux. Pour obtenir un string simple, ajoute un mutate :

filter {
  # ... xml filter ...

  mutate {
    join => {
      "level" => ""
      "service" => ""
      "error_msg" => ""
      "transaction_id" => ""
    }
    convert => { "amount" => "float" }
  }
}

XML avec namespaces

Si le XML utilise des namespaces :

xml<ns:event xmlns:ns="http://example.com/logs">
  <ns:level>ERROR</ns:level>
</ns:event>
filter {
  xml {
    source => "xml_data"
    store_xml => false
    namespaces => { "ns" => "http://example.com/logs" }
    xpath => {
      "/ns:event/ns:level/text()" => "level"
    }
  }
}

Tableau recapitulatif : quel filtre pour quel format

Format d'entree Filtre recommande Exemple
key=value key2=value2 KV Logs structurels, query strings
{"key":"value"} (tout l'événement) Codec json API, apps modernes
text... {"json":true} (JSON embarque) Filtre JSON Logs mixtes texte+JSON
<xml>...</xml> Filtre XML APIs legacy, SOAP, configs
col1,col2,col3 Codec CSV ou filtre CSV Fichiers CSV
texte libre Grok ou Dissect Logs non structures

Ma recommandation sur paltemps.fr : si tu contrôles le format de tes logs, ecris en JSON. Le codec JSON sur l'input est le parsing le plus rapide et le plus fiable. Pas de regex, pas d'ambiguite. Si tu ne contrôles pas le format, utilise le filtre adapte au format que tu recois.

Résumé

  • Le filtre KV parse les paires cle=valeur sans regex, avec des separateurs configurables
  • KV est parfait pour les logs structures et les query strings d'URL
  • Le filtre JSON parse du JSON contenu dans un champ string (différent du codec JSON)
  • target évité les collisions de champs quand tu parses dans un sous-objet
  • Le filtre XML parse du XML avec store_xml ou xpath pour cibler des éléments
  • XPath renvoie des tableaux : utilise mutate { join } pour obtenir des strings

Precedent : 12 - Filtres Date et GeoIP | Suivant : 14 - Le filtre Ruby

Sources

Réservez un audit gratuit de 30 minutes. Je vous montre concrètement ce qu'on peut automatiser.