Logging structure avec JSONL

Un guide complet sur le logging structure au format JSONL (JSON Lines). Apprenez a integrer avec ELK Stack, Fluentd, CloudWatch, GCP Logging et Azure Monitor avec des exemples de code prets pour la production.

Derniere mise a jour : fevrier 2026

Pourquoi le logging structure avec JSONL ?

Les journaux en texte brut traditionnels sont lisibles par les humains mais pratiquement impossibles a analyser de maniere fiable par les machines. Une ligne comme '2024-01-15 ERROR User login failed for admin' enfouit la severite, le type d'evenement et le nom d'utilisateur dans une chaine non structuree. Chaque outil d'agregation de journaux doit recourir a des expressions regulieres fragiles pour en extraire le sens, et tout changement dans le format du journal casse le pipeline.

Le logging structure resout ce probleme en emettant chaque entree de journal sous forme d'objet JSON avec des champs bien definis. JSONL (JSON Lines) va encore plus loin : un objet JSON par ligne, pas de tableau englobant, pas de virgules finales. Cela rend les journaux JSONL compatibles avec l'ajout, le streaming et trivialement analysables par toutes les principales plateformes de journaux. Dans ce guide, vous apprendrez a produire des journaux JSONL depuis Python, Node.js et Go, et a les envoyer vers ELK, Fluentd et les trois principaux fournisseurs cloud.

Par rapport aux autres formats structures comme CSV ou XML, JSONL offre le meilleur equilibre entre flexibilite, support outillage et lisibilite humaine. Les champs peuvent etre imbriques, les types sont preserves, et vous n'avez jamais besoin de definir un schema fixe a l'avance. C'est pourquoi JSONL est devenu le standard de facto pour le logging structure dans l'infrastructure moderne.

Bibliotheques de logging par langage

La plupart des bibliotheques de logging modernes supportent la sortie JSON structuree nativement ou via un simple changement de configuration. Voici les bibliotheques recommandees pour Python, Node.js et Go, chacune produisant une sortie compatible JSONL.

Python : structlog

Recommande

structlog est la bibliotheque de logging structure de reference pour Python. Elle encapsule le module logging standard et produit une sortie JSON par defaut. Son pipeline de processeurs vous permet d'enrichir, filtrer et transformer les entrees de journal avant la serialisation.

Python : structlog
import structlog
# Configurer structlog pour une sortie JSONL
structlog.configure(
processors=[
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.add_log_level,
structlog.processors.StackInfoRenderer(),
structlog.processors.JSONRenderer(),
],
wrapper_class=structlog.BoundLogger,
context_class=dict,
logger_factory=structlog.PrintLoggerFactory(),
)
log = structlog.get_logger()
# Chaque appel produit une ligne JSONL
log.info("user.login", user_id="u-42", ip="10.0.0.1")
# {"event":"user.login","user_id":"u-42","ip":"10.0.0.1","level":"info","timestamp":"2026-02-15T08:30:00Z"}
log.error("db.connection_failed", host="db-primary", retries=3)
# {"event":"db.connection_failed","host":"db-primary","retries":3,"level":"error","timestamp":"2026-02-15T08:30:01Z"}

Node.js : pino

Le plus rapide

pino est le logger Node.js le plus rapide, produisant une sortie JSONL par defaut sans aucune configuration. Il atteint une faible surcharge grace a l'ecriture asynchrone et un chemin de serialisation minimal. Le package companion pino-pretty fournit une sortie lisible par l'homme pour le developpement local.

Node.js : pino
import pino from 'pino';
// pino produit du JSONL par defaut
const log = pino({
level: 'info',
timestamp: pino.stdTimeFunctions.isoTime,
});
// Chaque appel produit une ligne JSONL
log.info({ userId: 'u-42', ip: '10.0.0.1' }, 'user.login');
// {"level":30,"time":"2026-02-15T08:30:00.000Z","userId":"u-42","ip":"10.0.0.1","msg":"user.login"}
log.error({ host: 'db-primary', retries: 3 }, 'db.connection_failed');
// {"level":50,"time":"2026-02-15T08:30:01.000Z","host":"db-primary","retries":3,"msg":"db.connection_failed"}
// Les loggers enfants heritent du contexte
const reqLog = log.child({ requestId: 'req-abc-123' });
reqLog.info({ path: '/api/users' }, 'request.start');

Go : zerolog

Zero allocation

zerolog est un logger JSON a zero allocation pour Go, concu pour une performance maximale dans les services a haut debit. Il ecrit directement dans un io.Writer sans allocations intermediaires, ce qui le rend ideal pour les applications sensibles a la latence. Son API chainee est a la fois ergonomique et efficace.

Go : zerolog
package main
import (
"os"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func main() {
// Configurer la sortie JSONL vers stdout
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
log.Logger = zerolog.New(os.Stdout).With().Timestamp().Logger()
// Chaque appel produit une ligne JSONL
log.Info().
Str("user_id", "u-42").
Str("ip", "10.0.0.1").
Msg("user.login")
// {"level":"info","user_id":"u-42","ip":"10.0.0.1","time":1739608200,"message":"user.login"}
log.Error().
Str("host", "db-primary").
Int("retries", 3).
Msg("db.connection_failed")
}

Integration ELK Stack

L'ELK Stack (Elasticsearch, Logstash, Kibana) est la plateforme de gestion de journaux open source la plus largement deployee. Les journaux JSONL s'integrent parfaitement car Logstash et Filebeat peuvent analyser le JSON nativement, eliminant le besoin de modeles grok complexes.

Filebeat est le collecteur de journaux leger qui surveille vos fichiers de journaux JSONL et les transmet a Logstash ou Elasticsearch. Definir json.keys_under_root a true promeut les champs JSON en champs Elasticsearch de niveau superieur, les rendant directement recherchables dans Kibana.

Configuration Filebeat
# filebeat.yml
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/app/*.jsonl
json.keys_under_root: true
json.add_error_key: true
json.message_key: message
json.overwrite_keys: true
output.elasticsearch:
hosts: ["http://elasticsearch:9200"]
index: "app-logs-%{+yyyy.MM.dd}"
# Optionnel : envoyer vers Logstash a la place
# output.logstash:
# hosts: ["logstash:5044"]

Si vous devez transformer ou enrichir les journaux avant l'indexation, Logstash se place entre Filebeat et Elasticsearch. Le codec json analyse automatiquement chaque ligne JSONL. Utilisez le filtre mutate pour renommer les champs, ajouter des tags ou supprimer les donnees sensibles avant le stockage.

Pipeline Logstash
# logstash.conf
input {
beats {
port => 5044
codec => json
}
}
filter {
# Analyser l'horodatage depuis le champ JSON
date {
match => ["timestamp", "ISO8601"]
target => "@timestamp"
}
# Ajouter un tag d'environnement
mutate {
add_field => { "environment" => "production" }
remove_field => ["agent", "ecs", "host"]
}
# Router par niveau de log
if [level] == "error" or [level] == "fatal" {
mutate { add_tag => ["alert"] }
}
}
output {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
index => "app-logs-%{+YYYY.MM.dd}"
}
}

Fluentd et Fluent Bit

Fluentd et son petit frere leger Fluent Bit sont des projets diplomes du CNCF largement utilises dans les environnements Kubernetes. Tous deux gerent les journaux JSONL nativement et peuvent les transmettre a pratiquement n'importe quelle destination, d'Elasticsearch a S3 en passant par les services de journaux cloud natifs.

Fluent Bit est le collecteur de journaux prefere pour Kubernetes et les environnements a ressources limitees. Il consomme environ 450 Ko de memoire et peut traiter des centaines de milliers d'enregistrements par seconde. L'analyseur json gere l'entree JSONL sans aucune configuration personnalisee.

Configuration Fluent Bit
[SERVICE]
Flush 5
Daemon Off
Log_Level info
Parsers_File parsers.conf
[INPUT]
Name tail
Path /var/log/app/*.jsonl
Parser json
Tag app.*
Refresh_Interval 5
[FILTER]
Name modify
Match app.*
Add cluster production-us-east-1
Add service my-api
[OUTPUT]
Name es
Match app.*
Host elasticsearch
Port 9200
Index app-logs
Type _doc
Logstash_Format On

Fluentd offre un ecosysteme de plugins plus riche compare a Fluent Bit, avec plus de 500 plugins communautaires. Utilisez-le quand vous avez besoin de routage, buffering ou transformations avances. Le plugin in_tail avec format json lit les fichiers JSONL nativement.

Configuration Fluentd
# fluentd.conf
<source>
@type tail
path /var/log/app/*.jsonl
pos_file /var/log/fluentd/app.pos
tag app.logs
<parse>
@type json
time_key timestamp
time_format %Y-%m-%dT%H:%M:%S%z
</parse>
</source>
<filter app.logs>
@type record_transformer
<record>
hostname "#{Socket.gethostname}"
environment production
</record>
</filter>
<match app.logs>
@type elasticsearch
host elasticsearch
port 9200
index_name app-logs
logstash_format true
<buffer>
@type memory
flush_interval 5s
chunk_limit_size 5m
</buffer>
</match>

Plateformes de logging cloud

Les trois principaux fournisseurs cloud acceptent les journaux au format JSONL et analysent automatiquement les champs JSON en attributs recherchables et filtrables. Cela elimine le besoin de gerer votre propre infrastructure de journaux tout en vous donnant les memes capacites d'interrogation structuree.

AWS CloudWatch Logs

AWS

CloudWatch Logs analyse automatiquement les entrees de journal JSON, rendant chaque champ interrogeable avec CloudWatch Logs Insights. Quand votre application s'execute sur ECS, Lambda ou EC2 avec l'agent CloudWatch, la sortie JSONL est indexee comme donnees structurees sans configuration supplementaire. Utilisez la syntaxe de type SQL de Logs Insights pour interroger des millions d'entrees de journal en secondes.

AWS CloudWatch Logs
# Exemples de requetes CloudWatch Logs Insights
# Trouver toutes les erreurs de la derniere heure
fields @timestamp, level, event, user_id
| filter level = "error"
| sort @timestamp desc
| limit 100
# Agreger les comptes d'erreurs par type d'evenement
fields event
| filter level = "error"
| stats count(*) as error_count by event
| sort error_count desc
# Latence P99 par endpoint
fields path, duration_ms
| filter ispresent(duration_ms)
| stats pct(duration_ms, 99) as p99 by path
| sort p99 desc

GCP Cloud Logging

GCP

Google Cloud Logging (anciennement Stackdriver) traite les charges JSON comme des entrees de journal structurees avec un support natif. Quand vous ecrivez du JSONL vers stdout sur Cloud Run, GKE ou Cloud Functions, l'agent de logging analyse chaque ligne en un LogEntry structure. Le champ severity correspond directement aux niveaux de severite de Cloud Logging, et les champs jsonPayload deviennent indexes et recherchables.

GCP Cloud Logging
# Exemple Python pour Cloud Run / Cloud Functions
import json
import sys
def log(severity: str, message: str, **fields):
"""Ecrire une entree de journal JSONL compatible avec GCP Cloud Logging."""
entry = {
"severity": severity.upper(), # Correspond a la severite Cloud Logging
"message": message,
**fields,
}
print(json.dumps(entry), file=sys.stdout, flush=True)
# GCP analyse automatiquement ces journaux comme structures
log("INFO", "user.login", user_id="u-42", ip="10.0.0.1")
log("ERROR", "db.timeout", host="db-primary", latency_ms=5200)
# Requete dans Cloud Logging :
# jsonPayload.user_id = "u-42"
# severity >= ERROR

Azure Monitor Logs

Azure

Azure Monitor ingere les journaux JSONL via l'Azure Monitor Agent ou l'ingestion directe par API. Application Insights analyse automatiquement les traces JSON et les evenements personnalises. KQL (Kusto Query Language) fournit des requetes puissantes sur les donnees de journaux structures avec support pour l'agregation, l'analyse de series temporelles et la detection d'anomalies.

Azure Monitor Logs
// Requetes KQL pour les journaux JSONL dans Azure Monitor
// Trouver les erreurs recentes
AppTraces
| where SeverityLevel >= 3
| extend parsed = parse_json(Message)
| project TimeGenerated, parsed.event, parsed.user_id,
parsed.error_message
| order by TimeGenerated desc
| take 100
// Taux d'erreur dans le temps (intervalles de 5 minutes)
AppTraces
| where SeverityLevel >= 3
| summarize ErrorCount = count() by bin(TimeGenerated, 5m)
| render timechart
// Top des evenements d'erreur
AppTraces
| where SeverityLevel >= 3
| extend parsed = parse_json(Message)
| summarize Count = count() by tostring(parsed.event)
| top 10 by Count

Bonnes pratiques pour le logging JSONL

Suivre des conventions coherentes dans tous vos services rend les journaux JSONL plus faciles a interroger, correler et alerter. Ces bonnes pratiques sont tirees de systemes de production traitant des milliards d'entrees de journal par jour.

Utiliser des niveaux de log coherents

Adoptez une echelle de severite standard et utilisez-la de maniere coherente dans tous les services. La convention la plus courante utilise cinq niveaux : DEBUG pour les diagnostics de developpement, INFO pour les operations normales, WARN pour les problemes recuperables, ERROR pour les defaillances necessitant une attention, et FATAL pour les crashs irrecuperables. Mappez les niveaux numeriques (comme l'echelle 10-60 de pino) a ces noms de chaines au moment de l'ingestion pour une interrogation uniforme.

Utiliser des niveaux de log coherents
{"level":"debug","event":"cache.miss","key":"user:42","timestamp":"2026-02-15T08:30:00Z"}
{"level":"info","event":"request.completed","method":"GET","path":"/api/users","status":200,"duration_ms":45,"timestamp":"2026-02-15T08:30:01Z"}
{"level":"warn","event":"rate_limit.approaching","client_id":"c-99","current":950,"limit":1000,"timestamp":"2026-02-15T08:30:02Z"}
{"level":"error","event":"payment.failed","order_id":"ord-123","error":"card_declined","timestamp":"2026-02-15T08:30:03Z"}
{"level":"fatal","event":"startup.failed","reason":"missing DATABASE_URL","timestamp":"2026-02-15T08:30:04Z"}

Standardiser les noms de champs

Definissez un schema partage pour les champs communs a travers tous les services. Utilisez le snake_case pour les noms de champs, ISO 8601 pour les horodatages et une denomination coherente pour les identifiants de correlation. Au minimum, chaque entree de journal doit contenir : timestamp, level, event (un nom d'evenement lisible par la machine) et service. Les entrees liees aux requetes doivent inclure request_id et user_id pour le tracage.

Standardiser les noms de champs
// Schema de champs recommande
{
"timestamp": "2026-02-15T08:30:00.000Z", // ISO 8601, toujours UTC
"level": "info", // debug|info|warn|error|fatal
"event": "order.created", // nom d'evenement en notation pointee
"service": "order-api", // service emetteur
"version": "1.4.2", // version du service
"request_id": "req-abc-123", // ID de correlation
"trace_id": "4bf92f3577b34da6", // ID de trace distribuee
"user_id": "u-42", // utilisateur authentifie
"duration_ms": 120, // numerique, pas chaine
"order_id": "ord-789", // contexte specifique au domaine
"items_count": 3 // contexte specifique au domaine
}

Considerations de performance

Le logging structure ajoute une surcharge de serialisation a chaque appel de log. Dans les services a haut debit, cela peut devenir significatif. Utilisez le logging asynchrone (pino, zerolog) pour deplacer la serialisation hors du chemin critique. Evitez de loguer de gros objets ou des corps de requete/reponse au niveau INFO. Utilisez l'echantillonnage pour les journaux de debug a haut volume. Ecrivez dans des fichiers locaux et laissez un sidecar (Filebeat, Fluent Bit) gerer l'envoi, plutot que d'envoyer les journaux via le reseau de maniere synchrone.

Utilisez des loggers asynchrones (pino, zerolog) pour eviter de bloquer le thread principal pendant la serialisation JSON.

Ne loguez jamais les corps complets de requete ou reponse au niveau INFO. Utilisez le niveau DEBUG et activez-le uniquement lors du depannage.

Echantillonnez les evenements a haut volume. Loguez 1 requete de health-check sur 100 au lieu de toutes.

Ecrivez les journaux dans des fichiers locaux et envoyez-les de maniere asynchrone avec Filebeat ou Fluent Bit. Evitez les appels reseau synchrones dans le chemin de logging.

Definissez une taille maximale de ligne de journal (par ex. 16 Ko) pour empecher les entrees surdimensionnees d'encombrer le pipeline.

Effectuez la rotation des fichiers de journaux avec logrotate ou la rotation integree de la bibliotheque pour eviter l'epuisement du disque.

Validez vos journaux JSONL en ligne

Utilisez nos outils gratuits dans le navigateur pour inspecter, valider et convertir des fichiers de journaux JSONL. Sans telechargement, tout le traitement se fait localement.

Inspectez des journaux JSONL dans votre navigateur

Visualisez, recherchez et validez des fichiers de journaux JSONL jusqu'a 1 Go. Chargement instantane, traitement cote client, 100% prive.

Questions frequemment posees

Logging JSONL — Logs structurés, ELK/Fluentd et ingestion...