04 - Inputs stdin et file : lire des donnees locales
Ce que tu vas apprendre
- Comment stdin fonctionne et quand l'utiliser
- Le plugin file en détail : path, globs, start_position
- Le mecanisme sincedb (comment Logstash se souvient de ce qu'il a deja lu)
- La gestion de la rotation des fichiers de log
- Les pièges classiques du file input
Prerequisites
- Le lab Docker qui tourne (voir article 02)
- Avoir compris l'anatomie d'un pipeline (voir article 03)
stdin : le plugin de dev
J'utilise stdin pour une seule chose : tester un filtre rapidement. Tu veux vérifier si ton pattern Grok fonctionne ? Tu colles ta ligne de log dans stdin et tu regardes le résultat dans stdout. Pas de fichier, pas de configuration, pas d'attente.
Configuration minimale
input {
stdin {
codec => plain
}
}
output {
stdout {
codec => rubydebug
}
}
Le codec plain est le défaut. Chaque ligne tapee dans le terminal devient un événement avec le texte dans le champ message.
stdin avec un codec JSON
Si ton application écrit des logs en JSON (et elle devrait), tu peux demander a stdin de parser le JSON directement :
input {
stdin {
codec => json
}
}
output {
stdout {
codec => rubydebug
}
}
Si tu colles {"level":"ERROR","service":"api","msg":"timeout"}, Logstash créé un événement avec trois champs directement accessibles : level, service, msg. Pas besoin de filtre JSON.
Les limites de stdin
stdin bloque le terminal. Tu ne peux pas l'utiliser dans un conteneur Docker en mode detache (-d). Et il ne fonctionne que pour un seul flux de donnees. En production, tu ne l'utiliseras jamais. C'est un outil de debug.
Pour le lab Docker, si tu veux tester avec stdin, lance Logstash en mode interactif :
bashdocker compose run --rm -it logstash \
bin/logstash -e 'input { stdin {} } output { stdout { codec => rubydebug } }'
Ca lance une instance temporaire. Tape du texte, regarde le résultat, quitte avec Ctrl+C.
file : le plugin le plus utilise en local
Le plugin file lit des fichiers. Ca parait simple, mais il y a un mecanisme interne qui surprend la première fois : sincedb.
Configuration de base
input {
file {
path => "/data/app.log"
start_position => "beginning"
sincedb_path => "/dev/null"
}
}
output {
stdout {
codec => rubydebug
}
}
Mets ce pipeline dans logstash/pipeline/main.conf et créé un fichier data/app.log avec quelques lignes :
2026-03-31 14:00:01 INFO User 42 logged in
2026-03-31 14:00:02 ERROR Database connection timeout
2026-03-31 14:00:03 WARN Slow query: 2340ms
Logstash va lire le fichier et créer un événement par ligne.
start_position : beginning vs end
Ce paramètre ne s'applique que la première fois que Logstash voit un fichier. Deux options :
beginning: lit le fichier depuis le débutend: ne lit que les nouvelles lignes ajoutees apres le démarrage (défaut)
Le défaut est end. Ca surprend tout le monde. Tu créés un fichier, tu demarres Logstash, et rien ne se passe. Le fichier existait deja, Logstash commence a la fin et attend de nouvelles lignes.
Pour du dev et du test, utilise toujours start_position => "beginning".
sincedb : la mémoire de Logstash
sincedb est le mecanisme qui permet a Logstash de se souvenir de sa position dans un fichier. C'est un petit fichier texte qui stocke, pour chaque fichier surveille, l'inode et le nombre d'octets deja lus.
# Contenu typique d'un fichier sincedb
1234567 0 54321 98765
Chaque ligne represente un fichier : inode, device majeur, device mineur, position en octets.
Pourquoi c'est important ? Parce que si Logstash redemarrage, il reprend là où il s'etait arrêté. Pas de doublon, pas de perte. C'est le meme principe que les offsets Kafka.
Le problème en dev : si tu modifies ton fichier de test et que tu veux le relire, Logstash refuse. sincedb lui dit "j'ai deja lu ce fichier jusqu'a l'octet 156". Pour contourner :
# Option 1 : envoyer sincedb dans /dev/null (oublie tout a chaque redemarrage)
sincedb_path => "/dev/null"
# Option 2 : supprimer le fichier sincedb manuellement
# Il est dans /usr/share/logstash/data/plugins/inputs/file/ par defaut
En production, ne touche jamais a sincedb. C'est ce qui garantit la fiabilité du file input. En dev, sincedb_path => "/dev/null" est ton ami.
Surveiller un dossier entier
Tu peux utiliser des globs dans le path :
input {
file {
path => "/data/logs/*.log"
start_position => "beginning"
sincedb_path => "/dev/null"
}
}
Logstash surveille tous les fichiers .log dans /data/logs/. Si un nouveau fichier apparaît, il le détecté et commence a le lire.
Globs supportes :
| Pattern | Signification |
|---|---|
* |
N'importe quel nom de fichier |
** |
N'importe quel sous-dossier (recursif) |
? |
Un seul caractère |
[abc] |
Un caractère parmi a, b, c |
# Tous les logs dans tous les sous-dossiers
path => "/data/logs/**/*.log"
# Les logs qui commencent par "app"
path => "/data/logs/app*.log"
# Plusieurs patterns
path => ["/data/logs/app.log", "/data/logs/error.log"]
Exclure des fichiers
input {
file {
path => "/data/logs/*.log"
exclude => ["debug.log", "*.gz"]
start_position => "beginning"
sincedb_path => "/dev/null"
}
}
exclude prend un tableau de globs. Ici, on ignore les fichiers de debug et les archives gzippees.
La rotation des fichiers
Les fichiers de log tournent. Logrotate, par exemple, renomme app.log en app.log.1 et créé un nouveau app.log. Ou alors il compresse en app.log.gz.
Logstash gere ca grace au tracking par inode. Il suit l'inode du fichier, pas son nom. Quand app.log est renomme en app.log.1, Logstash continue de lire app.log.1 jusqu'a la fin (meme inode), puis détecté le nouveau app.log (nouvel inode) et commence a le lire.
# Avant rotation
app.log (inode 12345, position 5000) <-- Logstash lit ici
# Apres rotation
app.log.1 (inode 12345, position 5000) <-- Logstash finit de lire
app.log (inode 67890, position 0) <-- Logstash commence ici
Paramètres lies a la rotation
input {
file {
path => "/data/logs/app.log"
start_position => "beginning"
# Combien de temps garder un fichier ouvert apres la derniere lecture
close_older => "1 hour"
# Combien de temps ignorer un fichier inactif
ignore_older => "24 hours"
# Frequence de verification des nouveaux fichiers
stat_interval => "1 second"
# Frequence de verification des fichiers connus
discover_interval => 15
}
}
close_older: ferme le file handle apres 1 heure d'inactivite. Important sur les systèmes avec beaucoup de fichiers pour éviter d'epuiser les file descriptors.ignore_older: ne lit pas les fichiers qui n'ont pas ete modifies depuis 24 heures. Evite de relire de vieux logs au démarrage.stat_interval: a quelle fréquence Logstash vérifié si les fichiers connus ont grandi.discover_interval: a quelle fréquence Logstash cherche de nouveaux fichiers dans le glob.
Cas pratique : lire plusieurs types de logs
Imaginons que tu as deux types de logs dans des dossiers différents :
data/
├── app/
│ ├── api.log
│ └── worker.log
└── nginx/
└── access.log
Tu peux utiliser deux inputs file et ajouter un champ type pour les distinguer dans les filtres :
input {
file {
path => "/data/app/*.log"
start_position => "beginning"
sincedb_path => "/dev/null"
type => "app"
add_field => { "source_dir" => "app" }
}
file {
path => "/data/nginx/*.log"
start_position => "beginning"
sincedb_path => "/dev/null"
type => "nginx"
add_field => { "source_dir" => "nginx" }
}
}
filter {
if [type] == "nginx" {
grok {
match => { "message" => "%{COMBINEDAPACHELOG}" }
}
}
if [type] == "app" {
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:ts} %{LOGLEVEL:level} %{GREEDYDATA:msg}" }
}
}
}
output {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
index => "logs-%{type}-%{+YYYY.MM.dd}"
}
}
Le champ type est un paramètre commun a tous les inputs. Il permet de router les événements dans les filtres avec des conditionnels. Les logs Nginx vont dans logs-nginx-2026.03.31, les logs app dans logs-app-2026.03.31.
Surveiller un fichier en temps réel
Pour simuler une application qui écrit des logs en continu, ouvre un deuxieme terminal et ajoute des lignes :
bash# Terminal 1 : Logstash tourne avec le pipeline file
# Terminal 2 : ajouter des lignes au fichier
docker exec -it logstash bash -c 'echo "2026-03-31 15:00:00 INFO New line added" >> /data/app.log'
Ou depuis l'hote, si le dossier data/ est monte :
bashecho "2026-03-31 15:00:00 INFO New line added" >> data/app/api.log
Logstash détecté la nouvelle ligne et la traite. Le delai depend de stat_interval (1 seconde par défaut).
Les paramètres communs a tous les inputs
Chaque input plugin accepte des paramètres génériques. On les a deja vus pour certains :
| Paramètre | Description | Exemple |
|---|---|---|
type |
Ajoute un champ type a chaque événement |
type => "nginx" |
tags |
Ajoute des tags a chaque événement | tags => ["web", "prod"] |
add_field |
Ajoute des champs custom | add_field => { "env" => "staging" } |
codec |
Change le codec d'entree | codec => json |
Ces paramètres fonctionnent sur tous les inputs : file, beats, http, kafka, jdbc. On les retrouvera dans les prochains articles.
Quand utiliser file vs Filebeat
Le plugin file de Logstash et Filebeat font la meme chose : lire des fichiers de log. Mais ils n'ont pas le meme usage.
| file input (Logstash) | Filebeat | |
|---|---|---|
| RAM | ~1 Go (c'est Logstash entier) | ~15 Mo |
| Use case | Dev, tests, machine unique | Production, multi-serveurs |
| Backpressure | Queue interne | Buffer disque + retry |
| Installation | Rien (dans Logstash) | Agent a installer sur chaque serveur |
En production, tu mets Filebeat sur chaque serveur et tu centralises vers Logstash. Tu n'installes pas Logstash sur chaque serveur, c'est du gaspillage de RAM.
Le file input est utile pour le dev local, les tests, et les cas ou tu as un seul serveur avec Logstash deja installe. Sur paltemps.fr, le file input sert surtout dans les scripts de reprocessing : relire un fichier de log archive pour le re-parser avec de nouvelles regles.
Résumé
- stdin est un outil de debug : rapide, interactif, inutile en production
- Le file input lit des fichiers et suit sa position avec sincedb
start_position => "beginning"relit le fichier depuis le début (sinon, seules les nouvelles lignes sont lues)sincedb_path => "/dev/null"oublie la position a chaque redemarrage (utile en dev)- La rotation des fichiers est geree automatiquement via le tracking par inode
- Le champ
typepermet de distinguer les sources dans les filtres - En production, préféré Filebeat au file input pour la collecte
Precedent : 03 - Anatomie d'un pipeline | Suivant : 05 - Input Beats