06 - Inputs HTTP, TCP et UDP : recevoir des donnees réseau
Ce que tu vas apprendre
- Transformer Logstash en endpoint HTTP pour recevoir des webhooks ou des logs applicatifs
- Recevoir des donnees via TCP (connexion persistante) et UDP (syslog)
- Envoyer des événements depuis une application Node.js vers Logstash HTTP
- Recevoir des webhooks GitHub/GitLab dans Logstash
- Choisir entre HTTP, TCP et UDP selon le cas d'usage
Prerequisites
- Le lab Docker qui tourne (voir article 02)
- Savoir utiliser
curl(ou un client HTTP)
Envoyer des logs par HTTP, ca parait bizarre
La première fois qu'un collegue m'a dit "on envoie les logs en HTTP", j'ai grimace. HTTP pour des logs ? C'est pas fait pour des pages web, ca ?
En fait, c'est un des patterns les plus pratiques. Ton application envoie ses logs en JSON par POST a Logstash. Pas de fichier a lire, pas de Filebeat a installer, pas de volume Docker a partager. L'app fait un POST, Logstash recoit. C'est direct.
C'est aussi comme ca que fonctionnent les webhooks. GitHub envoie un POST quand quelqu'un push du code. Stripe envoie un POST quand un paiement est traite. Logstash peut recevoir ces webhooks, les parser, et les indexer dans Elasticsearch.
Input HTTP
Configuration de base
# logstash/pipeline/main.conf
input {
http {
port => 8080
codec => json
}
}
output {
stdout {
codec => rubydebug
}
}
Ajoute le port 8080 dans ton compose.yaml :
yamllogstash:
# ...
ports:
- "5044:5044"
- "8080:8080"
- "9600:9600"
Redarre Logstash, puis teste avec curl :
bashcurl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-d '{"level":"ERROR","service":"api-users","message":"Connection timeout","duration_ms":5023}'
Dans les logs Logstash, tu verras :
json{
"@timestamp" => 2026-03-31T14:30:00.123Z,
"@version" => "1",
"level" => "ERROR",
"service" => "api-users",
"message" => "Connection timeout",
"duration_ms" => 5023,
"host" => {
"ip" => "172.18.0.1"
},
"http" => {
"method" => "POST",
"request" => {
"body" => {
"bytes" => "89"
},
"mime_type" => "application/json"
},
"version" => "HTTP/1.1"
},
"url" => {
"path" => "/"
},
"user_agent" => {
"original" => "curl/8.4.0"
}
}
Logstash a parse le JSON et ajoute des metadonnees HTTP (méthode, IP source, user-agent).
Authentification basique
En production, tu ne veux pas que n'importe qui puisse envoyer des donnees a Logstash. Ajoute un login/mot de passe :
input {
http {
port => 8080
codec => json
user => "logstash"
password => "s3cret"
}
}
bashcurl -X POST http://localhost:8080 \
-u logstash:s3cret \
-H "Content-Type: application/json" \
-d '{"message":"authenticated event"}'
Sans les credentials, Logstash renvoie un 401.
Codes de réponse custom
Par défaut, Logstash répond 200 a chaque requête. Tu peux changer le code :
input {
http {
port => 8080
codec => json
response_code => 202
}
}
202 (Accepted) est plus semantiquement correct : "j'ai reçu ta requête, je la traiterai".
Recevoir du texte brut
Si l'expediteur n'envoie pas du JSON mais du texte brut :
input {
http {
port => 8080
codec => plain
}
}
Le texte entier arrive dans le champ message. A toi de le parser avec un filtre Grok ou Dissect.
Cas pratique : recevoir des webhooks GitHub
Quand tu configures un webhook GitHub sur un repo, GitHub envoie un POST a l'URL que tu specifies a chaque événement (push, PR, issue, etc.).
# logstash/pipeline/webhooks.conf
input {
http {
port => 8080
codec => json
additional_codecs => {}
type => "github"
}
}
filter {
if [type] == "github" {
# Extraire les infos utiles du payload GitHub
mutate {
rename => {
"[repository][full_name]" => "repo"
"[sender][login]" => "author"
"[head_commit][message]" => "commit_message"
}
}
# Garder le header X-GitHub-Event comme champ
mutate {
rename => {
"[headers][x-github-event]" => "github_event"
}
}
# Nettoyer : supprimer le payload brut
mutate {
remove_field => ["headers", "repository", "sender", "head_commit", "commits", "pusher", "organization", "compare", "ref"]
}
}
}
output {
if [type] == "github" {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
index => "github-events-%{+YYYY.MM.dd}"
}
}
stdout { codec => rubydebug }
}
Pour tester sans GitHub, simule un webhook push :
bashcurl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-H "X-GitHub-Event: push" \
-d '{
"ref": "refs/heads/main",
"repository": {"full_name": "paltemps/api"},
"sender": {"login": "nicolasnguyen"},
"head_commit": {"message": "fix: resolve timeout on /users endpoint"},
"commits": [{"id": "abc123"}]
}'
Cas pratique : envoyer des logs depuis Node.js
Ton application Node.js peut envoyer ses logs directement a Logstash par HTTP. Pas de fichier, pas de Filebeat.
typescript// logger.ts — envoi direct a Logstash
const LOGSTASH_URL = process.env.LOGSTASH_URL || "http://localhost:8080";
type LogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR";
async function log(level: LogLevel, message: string, meta?: Record<string, unknown>) {
const event = {
"@timestamp": new Date().toISOString(),
level,
message,
service: process.env.SERVICE_NAME || "unknown",
hostname: process.env.HOSTNAME || "localhost",
...meta,
};
try {
await fetch(LOGSTASH_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(event),
});
} catch {
// fallback sur stderr si Logstash est injoignable
console.error(JSON.stringify(event));
}
}
// Utilisation
await log("INFO", "User logged in", { userId: 42, ip: "192.168.1.10" });
await log("ERROR", "Database timeout", { query: "SELECT * FROM users", duration_ms: 5023 });
C'est simple, mais ca a un coût : chaque log fait une requête HTTP. Pour des applications a fort volume, préféré Filebeat ou un buffer local. Pour des applications legeres (API interne, scripts, workers), le HTTP direct marche bien.
Sur paltemps.fr, les workers de taches de fond utilisent ce pattern. Ils tournent dans des conteneurs éphémères et n'ont pas de volume partage. Envoyer les logs en HTTP est la solution la plus simple.
Input TCP
TCP est utile pour les connexions persistantes. Le client ouvre une connexion, envoie des événements ligne par ligne, et la connexion reste ouverte.
Configuration de base
input {
tcp {
port => 5000
codec => json_lines
}
}
output {
stdout { codec => rubydebug }
}
Le codec json_lines attend un objet JSON par ligne (NDJSON). Chaque \n marque la fin d'un événement.
Teste avec nc (netcat) :
bashecho '{"level":"INFO","message":"test TCP"}' | nc localhost 5000
Ou envoie plusieurs événements a la suite :
bashprintf '{"level":"INFO","msg":"event 1"}\n{"level":"ERROR","msg":"event 2"}\n' | nc localhost 5000
TCP pour les logs syslog
Beaucoup d'equipements réseau (firewalls, routeurs, switches) envoient leurs logs en syslog via TCP ou UDP. Logstash peut les recevoir :
input {
tcp {
port => 5514
codec => plain
type => "syslog-tcp"
}
}
filter {
if [type] == "syslog-tcp" {
grok {
match => { "message" => "%{SYSLOGLINE}" }
}
}
}
output {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
index => "syslog-%{+YYYY.MM.dd}"
}
}
TCP avec SSL
Pour chiffrer la connexion TCP :
input {
tcp {
port => 5000
codec => json_lines
ssl_enabled => true
ssl_certificate => "/certs/logstash.crt"
ssl_key => "/certs/logstash.key"
}
}
On verra les certificats en détail dans l'article 24 sur la sécurité.
Input UDP
UDP est non connecte et non fiable. Pas de garantie de livraison, pas d'accusee de reception. Mais c'est rapide et leger.
Configuration de base
input {
udp {
port => 5514
codec => plain
type => "syslog-udp"
}
}
Le syslog traditionnel (RFC 3164) utilise UDP sur le port 514. Dans Docker, on utilise un port non privilegie (5514).
Quand utiliser UDP
UDP est le choix par défaut pour :
- Les equipements réseau qui ne supportent que syslog UDP
- Les metriques a haut debit ou la perte occasionnelle est acceptable
- Les environnements ou la latence de TCP est un problème
Pour des logs applicatifs, préféré toujours TCP ou HTTP. La garantie de livraison est plus importante que la vitesse.
Comparaison : HTTP vs TCP vs UDP
| HTTP | TCP | UDP | |
|---|---|---|---|
| Protocole | Requete/réponse | Connexion persistante | Datagramme |
| Fiabilite | Oui (réponse 200) | Oui (TCP ACK) | Non |
| Format | JSON, texte, form | Ligne par ligne | Datagramme |
| Auth | Oui (user/password) | SSL | Non |
| Use case principal | Webhooks, apps web | Syslog, flux continus | Syslog legacy, metriques |
| Overhead | Eleve (headers HTTP) | Moyen (TCP handshake) | Faible |
| Backpressure | Oui (client attend 200) | Oui (TCP flow control) | Non |
Mon choix par défaut :
- Webhooks et applications web : HTTP
- Logs applicatifs en flux continu : TCP avec json_lines (ou Filebeat)
- Equipements réseau legacy : UDP
Exposer plusieurs inputs sur des ports différents
Un pipeline Logstash peut avoir plusieurs inputs. Chaque input écoûte sur son propre port :
input {
# Beats sur 5044
beats {
port => 5044
}
# HTTP sur 8080
http {
port => 8080
codec => json
type => "http"
}
# TCP sur 5000
tcp {
port => 5000
codec => json_lines
type => "tcp"
}
# UDP sur 5514
udp {
port => 5514
codec => plain
type => "syslog"
}
}
filter {
if [type] == "syslog" {
grok { match => { "message" => "%{SYSLOGLINE}" } }
}
# ... filtres specifiques par type
}
output {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
index => "logs-%{type}-%{+YYYY.MM.dd}"
}
}
N'oublie pas d'exposer tous les ports dans le compose.yaml :
yamllogstash:
ports:
- "5044:5044"
- "8080:8080"
- "5000:5000"
- "5514:5514/udp"
- "9600:9600"
Note le /udp pour le port UDP. Par défaut, Docker n'expose que le TCP.
Résumé
- L'input HTTP transforme Logstash en serveur web qui recoit des POST
- C'est le bon choix pour les webhooks (GitHub, Stripe) et les logs d'apps legeres
- L'input TCP recoit des flux continus (une ligne par événement, codec json_lines)
- L'input UDP recoit du syslog ou des metriques sans garantie de livraison
- Chaque input ajoute un champ
typepour router les événements dans les filtres - Un pipeline peut avoir plusieurs inputs sur des ports différents
Precedent : 05 - Input Beats | Suivant : 07 - Inputs Kafka et JDBC