Logstash pour les devs - 12 - Filtres Date et GeoIP : temps et geolocalisation

Parser les dates dans tous les formats avec le filtre date, et ajouter la geolocalisation IP avec GeoIP. Deux filtres indispensables.

  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

12 - Filtres Date et GeoIP : temps et geolocalisation

Ce que tu vas apprendre

  • Parser des dates dans n'importe quel format avec le filtre date
  • Comprendre la différence entre @timestamp et un champ date custom
  • Gerer les fuseaux horaires
  • Ajouter la geolocalisation à partir d'une adresse IP avec GeoIP
  • Monter la base GeoLite2 dans Docker

Prerequisites


Le filtre Date

Pourquoi @timestamp est si important

Le champ @timestamp est le champ temporel par défaut dans l'Elastic Stack. C'est celui que Kibana utilise pour la timeline, les filtres par date, les histogrammes. Sans un @timestamp correct, tes donnees sont inutilisables dans Kibana.

Par défaut, @timestamp contient le moment ou Logstash a reçu l'événement, pas le moment ou l'événement s'est produit. Si tu traites des logs avec 5 minutes de retard, le @timestamp a 5 minutes de decalage.

Le filtre date resout ca : il parse le timestamp de tes logs et le met dans @timestamp.

Configuration de base

filter {
  grok {
    match => { "message" => "%{TIMESTAMP_ISO8601:log_date} %{LOGLEVEL:level} %{GREEDYDATA:msg}" }
  }

  date {
    match => ["log_date", "ISO8601"]
    target => "@timestamp"
    remove_field => ["log_date"]
  }
}

Le filtre date prend le champ log_date (extrait par Grok), le parse avec le format ISO8601, et ecrase @timestamp. Le champ log_date est supprime (plus besoin du string, on a le timestamp propre).

Les formats de date

Le paramètre match prend le nom du champ et un ou plusieurs formats. Logstash essaie les formats dans l'ordre.

Format Exemple Pattern
ISO 8601 2026-03-31T14:23:01.456Z ISO8601
Apache 31/Mar/2026:14:23:01 +0000 dd/MMM/yyyy:HH:mm:ss Z
Syslog Mar 31 14:23:01 MMM dd HH:mm:ss
Custom 2026-03-31 14:23:01 yyyy-MM-dd HH:mm:ss
Custom millis 2026-03-31 14:23:01.456 yyyy-MM-dd HH:mm:ss.SSS
Unix timestamp 1711889400 UNIX
Unix millis 1711889400000 UNIX_MS

Les tokens du format :

Token Signification Exemple
yyyy Annee (4 chiffres) 2026
yy Annee (2 chiffres) 26
MM Mois (numero) 03
MMM Mois (abrege) Mar
dd Jour 31
HH Heure (24h) 14
hh Heure (12h) 02
mm Minutes 23
ss Secondes 01
SSS Millisecondes 456
Z Timezone offset +0000, +0200
ZZZ Timezone nom CET, UTC

Plusieurs formats pour le meme champ

Si tes logs viennent de plusieurs sources avec des formats différents :

filter {
  date {
    match => [
      "log_date",
      "ISO8601",
      "yyyy-MM-dd HH:mm:ss",
      "dd/MMM/yyyy:HH:mm:ss Z",
      "UNIX"
    ]
  }
}

Logstash essaie chaque format dans l'ordre. Le premier qui matche gagne.

Gerer les fuseaux horaires

Si tes logs n'ont pas de timezone dans le timestamp, Logstash suppose UTC. C'est rarement ce que tu veux. Un serveur en France écrit 2026-03-31 14:23:01 en heure locale (CEST, UTC+2).

filter {
  date {
    match => ["log_date", "yyyy-MM-dd HH:mm:ss"]
    timezone => "Europe/Paris"
    target => "@timestamp"
  }
}

timezone dit a Logstash : "ce timestamp est en heure de Paris". Logstash convertit en UTC pour le stocker dans @timestamp. Dans Kibana, tu verras l'heure convertie selon le fuseau du navigateur.

Stocker dans un champ custom

Par défaut, target => "@timestamp". Mais si tu veux garder le timestamp original et en parser un deuxieme :

filter {
  date {
    match => ["created_at", "yyyy-MM-dd HH:mm:ss"]
    target => "created_timestamp"
  }
}

Le champ created_timestamp contiendra un objet date (pas un string), utilisable pour les calculs de duree et les aggregations par date dans Elasticsearch.

L'erreur classique : _dateparsefailure

Si le format ne matche pas, Logstash ajoute le tag _dateparsefailure. Les raisons les plus courantes :

  • Le format ne correspond pas (tu as mis yyyy mais la date a 2 chiffres)
  • Le champ contient des espaces en début ou fin (utilise mutate { strip => ["log_date"] } avant)
  • Le champ est vide ou absent sur certains événements
filter {
  date {
    match => ["log_date", "yyyy-MM-dd HH:mm:ss"]
    tag_on_failure => ["_dateparsefailure"]
  }

  if "_dateparsefailure" in [tags] {
    mutate {
      add_field => { "date_parse_error" => "true" }
    }
  }
}

Le filtre GeoIP

A quoi ca sert

GeoIP prend une adresse IP et renvoie la localisation geographique : pays, ville, latitude, longitude, ASN (fournisseur d'acces). C'est ce qui te permet de faire des cartes dans Kibana.

Configuration de base

filter {
  geoip {
    source => "client_ip"
  }
}

Résultat ajoute a l'événement :

json{
  "geoip": {
    "country_code2": "FR",
    "country_code3": "FRA",
    "country_name": "France",
    "continent_code": "EU",
    "region_name": "Brittany",
    "city_name": "Vannes",
    "postal_code": "56000",
    "latitude": 47.6559,
    "longitude": -2.7603,
    "timezone": "Europe/Paris",
    "location": {
      "lon": -2.7603,
      "lat": 47.6559
    }
  }
}

Le champ geoip.location est au format geo_point d'Elasticsearch. Kibana sait le placer sur une carte automatiquement.

La base GeoLite2

GeoIP utilise la base de donnees GeoLite2 de MaxMind. Logstash 8.x inclut une version de la base dans l'image Docker. Mais cette base vieillit. Les IPs changent de proprietaire, les FAI changent.

Pour avoir une base à jour, créé un compte gratuit sur MaxMind et telecharge les bases mises à jour.

logstash-lab/
├── geoip/
│   ├── GeoLite2-City.mmdb
│   └── GeoLite2-ASN.mmdb

Monte le dossier dans Docker :

yamllogstash:
  volumes:
    - ./geoip/:/usr/share/logstash/geoip/:ro

Et référencé la base dans le pipeline :

filter {
  geoip {
    source => "client_ip"
    database => "/usr/share/logstash/geoip/GeoLite2-City.mmdb"
  }
}

Limiter les champs GeoIP

GeoIP ajoute beaucoup de champs. Si tu n'as besoin que du pays et des coordonnees :

filter {
  geoip {
    source => "client_ip"
    fields => ["country_name", "country_code2", "city_name", "location"]
  }
}

Ca economise du stockage dans Elasticsearch.

Ajouter l'ASN (fournisseur d'acces)

La base ASN est séparée de la base City :

filter {
  geoip {
    source => "client_ip"
    database => "/usr/share/logstash/geoip/GeoLite2-ASN.mmdb"
    target => "asn"
  }
}

Résultat :

json{
  "asn": {
    "asn": 3215,
    "as_org": "Orange S.A."
  }
}

Le target séparé les champs ASN des champs City pour éviter les collisions.

Gerer les IPs privees

Les IPs privees (192.168.x.x, 10.x.x.x, 172.16-31.x.x) et localhost (127.0.0.1) ne sont pas dans la base GeoIP. Logstash ajoute le tag _geoip_lookup_failure.

filter {
  geoip {
    source => "client_ip"
    tag_on_failure => ["_geoip_lookup_failure"]
  }

  # Ne pas s'affoler sur les IPs privees
  if "_geoip_lookup_failure" in [tags] and [client_ip] =~ /^(10\.|172\.(1[6-9]|2|3[01])\.|192\.168\.)/ {
    mutate {
      remove_tag => ["_geoip_lookup_failure"]
      add_field => { "[geoip][country_name]" => "Private Network" }
    }
  }
}

Cas pratique : pipeline complet logs Nginx

Un pipeline qui parse des logs Nginx, corrige le timestamp et geolocalise les visiteurs :

input {
  file {
    path => "/data/nginx-access.log"
    start_position => "beginning"
    sincedb_path => "/dev/null"
  }
}

filter {
  # 1. Parser le log Nginx
  grok {
    match => { "message" => "%{COMBINEDAPACHELOG}" }
  }

  # 2. Corriger le timestamp
  date {
    match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]
    target => "@timestamp"
    remove_field => ["timestamp"]
  }

  # 3. Convertir les types
  mutate {
    convert => {
      "response" => "integer"
      "bytes" => "integer"
    }
  }

  # 4. Geolocaliser l'IP
  geoip {
    source => "clientip"
    fields => ["country_name", "city_name", "location"]
  }

  # 5. Ajouter l'ASN
  geoip {
    source => "clientip"
    database => "/usr/share/logstash/geoip/GeoLite2-ASN.mmdb"
    target => "asn"
  }

  # 6. Parser le user agent
  useragent {
    source => "agent"
    target => "user_agent"
    remove_field => ["agent"]
  }

  # 7. Nettoyer
  mutate {
    remove_field => ["message", "@version", "ident"]
  }
}

output {
  elasticsearch {
    hosts => ["http://elasticsearch:9200"]
    index => "nginx-%{+YYYY.MM.dd}"
  }
}

Ligne d'entree :

83.156.42.123 - - [31/Mar/2026:14:23:01 +0200] "GET /api/products HTTP/1.1" 200 4523 "https://paltemps.fr" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"

Événement final dans Elasticsearch :

json{
  "@timestamp": "2026-03-31T12:23:01.000Z",
  "clientip": "83.156.42.123",
  "auth": "-",
  "verb": "GET",
  "request": "/api/products",
  "httpversion": "1.1",
  "response": 200,
  "bytes": 4523,
  "referrer": "\"https://paltemps.fr\"",
  "geoip": {
    "country_name": "France",
    "city_name": "Vannes",
    "location": { "lat": 47.6559, "lon": -2.7603 }
  },
  "asn": {
    "asn": 3215,
    "as_org": "Orange S.A."
  },
  "user_agent": {
    "name": "Safari",
    "os": "Mac OS X",
    "os_name": "Mac OS X",
    "device": "Mac"
  }
}

C'est ce genre de pipeline qu'on utilise sur paltemps.fr pour analyser le trafic. Dans Kibana, on peut faire une carte des visiteurs, un top 10 des FAI, et un histogramme des codes de réponse.

Résumé

  • Le filtre date parse les timestamps et les met dans @timestamp (le champ que Kibana utilise)
  • Plusieurs formats peuvent etre testes dans l'ordre avec un seul filtre
  • timezone est obligatoire si tes logs n'incluent pas le fuseau horaire
  • GeoIP ajoute pays, ville, coordonnees et ASN à partir d'une adresse IP
  • La base GeoLite2 est incluse dans Logstash, mais il vaut mieux monter une version à jour
  • Les IPs privees ne sont pas geolocalisables, gere le tag _geoip_lookup_failure

Precedent : 11 - Le filtre Mutate | Suivant : 13 - Filtres KV, JSON, XML

Sources

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