ELK Stack — Pipeline Architecture
Pipeline Architecture
Every Logstash pipeline consists of three stages: inputs, filters, and outputs. Data flows through these stages as discrete units called events.
┌──────────┐ ┌─────────────────┐ ┌──────────┐ ┌──────────┐
│ INPUT │───▶│ INTERNAL QUEUE │───▶│ FILTER │───▶│ OUTPUT │
│ │ │ (in-memory or │ │ │ │ │
│ • beats │ │ persistent) │ │ • grok │ │ • ES │
│ • file │ │ │ │ • mutate │ │ • file │
│ • stdin │ │ │ │ • date │ │ • stdout │
└──────────┘ └─────────────────┘ └──────────┘ └──────────┘
│
┌────┴────┐
│ CODEC │
│(decode/ │
│ encode) │
└─────────┘
How It Works
- Input — generates events. An input plugin reads data from a source (file, network, queue) and creates an event.
- Queue — buffers events between input and filter stages. Can be in-memory (default) or persistent (disk-backed).
- Filter — processes events. Filter plugins parse, transform, and enrich data. Filters are optional but almost always used.
- Output — ships events. An output plugin sends the processed event to a destination (Elasticsearch, file, Kafka, etc.).
- Codec — acts as a stream decoder/encoder. Codecs operate on the input or output stage to decode/encode data formats (JSON, multiline, plain, rubydebug).
Pipeline Configuration File
A Logstash pipeline is defined in a .conf file with three sections:
input {
# Define data sources here
}
filter {
# Transform and enrich data here
}
output {
# Define destinations here
}
Each section can contain one or more plugin configurations.
Your First Pipeline — stdin to stdout
The simplest pipeline reads from the terminal and prints back to it:
# first-pipeline.conf
input {
stdin { }
}
output {
stdout {
codec => rubydebug
}
}
Run the pipeline:
bin/logstash -f first-pipeline.conf
Type hello world and press Enter. You’ll see:
{
"@timestamp" => 2026-04-01T09:06:00.000Z,
"message" => "hello world",
"@version" => "1",
"host" => {
"hostname" => "logstash-server"
},
"event" => {
"original" => "hello world"
}
}
Every event automatically gets @timestamp, @version, host, and event.original fields.
Adding a Filter
Let’s add a filter to parse a comma-separated line:
# csv-pipeline.conf
input {
stdin { }
}
filter {
csv {
separator => ","
columns => ["name", "age", "city"]
}
mutate {
convert => { "age" => "integer" }
}
}
output {
stdout {
codec => rubydebug
}
}
Sample input:
Alice,30,New York
Expected output:
{
"@timestamp" => 2026-04-01T09:10:00.000Z,
"message" => "Alice,30,New York",
"name" => "Alice",
"age" => 30,
"city" => "New York",
"@version" => "1"
}
Running Logstash with Config File
# Run with a single config file
bin/logstash -f /etc/logstash/conf.d/my-pipeline.conf
# Run with a directory of config files (merged into one pipeline)
bin/logstash -f /etc/logstash/conf.d/
# Test configuration syntax without starting
bin/logstash --config.test_and_exit -f my-pipeline.conf
# Auto-reload config on changes
bin/logstash -f my-pipeline.conf --config.reload.automatic
logstash.yml — Global Settings
The logstash.yml file controls global Logstash behavior:
# /etc/logstash/logstash.yml
# Node identity
node.name: "logstash-prod-01"
# Pipeline settings
pipeline.workers: 4 # Number of filter/output workers (default: CPU cores)
pipeline.batch.size: 125 # Events per worker batch
pipeline.batch.delay: 50 # Milliseconds to wait for batch to fill
pipeline.ordered: auto # Event ordering (auto, true, false)
# Config path
path.config: /etc/logstash/conf.d
# Data and logs
path.data: /var/lib/logstash
path.logs: /var/log/logstash
# Auto-reload
config.reload.automatic: true
config.reload.interval: 3s
# Persistent queue
queue.type: memory # memory (default) or persisted
queue.max_bytes: 1024mb # Max queue size (persisted only)
# Dead letter queue
dead_letter_queue.enable: false
dead_letter_queue.max_bytes: 1024mb
# HTTP API
api.http.host: "127.0.0.1"
api.http.port: 9600
# Monitoring
monitoring.enabled: true
pipelines.yml — Multiple Pipelines
Instead of merging all configs into one pipeline, you can run multiple isolated pipelines:
# /etc/logstash/pipelines.yml
- pipeline.id: apache-logs
path.config: "/etc/logstash/conf.d/apache.conf"
pipeline.workers: 2
pipeline.batch.size: 250
- pipeline.id: application-logs
path.config: "/etc/logstash/conf.d/app.conf"
pipeline.workers: 4
- pipeline.id: metrics
path.config: "/etc/logstash/conf.d/metrics.conf"
pipeline.workers: 1
Each pipeline runs independently with its own worker threads.
Event Lifecycle
Every event that flows through Logstash follows this lifecycle:
1. INPUT creates event with raw data
↓
2. CODEC decodes the raw data (e.g., JSON, plain, multiline)
↓
3. Event enters the QUEUE (in-memory or persisted)
↓
4. Worker thread picks up event from queue
↓
5. FILTERS process the event sequentially
• grok parses unstructured text
• mutate modifies fields
• date sets @timestamp
• conditionals route processing
↓
6. OUTPUT sends the event to destination(s)
↓
7. CODEC encodes the event for output (e.g., rubydebug, json_lines)
↓
8. Event is acknowledged and removed from queue
Codecs — Decoding and Encoding
Codecs handle serialization on the input/output boundary:
| Codec | Use Case | Example |
|---|---|---|
plain |
Raw text lines (default for stdin) | Each line is one event |
json |
JSON-formatted input/output | Parse JSON automatically |
json_lines |
Newline-delimited JSON | One JSON object per line |
multiline |
Multi-line events (stack traces) | Merge lines into one event |
rubydebug |
Pretty-print events (output only) | Human-readable debug output |
line |
Single line per event (output) | Output events as plain lines |
Multiline codec example (Java stack traces):
input {
file {
path => "/var/log/app/application.log"
codec => multiline {
pattern => "^%{TIMESTAMP_ISO8601}"
negate => true
what => "previous"
}
}
}
This merges lines that do NOT start with a timestamp into the previous event — perfect for stack traces.
Quick Reference
| Concept | Description |
|---|---|
| Pipeline | Input → Filter → Output processing chain |
| Event | A single unit of data flowing through the pipeline |
| Codec | Encodes/decodes data at input or output boundary |
| Worker | Thread that executes filter and output stages |
.conf file |
Pipeline configuration (input/filter/output blocks) |
logstash.yml |
Global Logstash settings |
pipelines.yml |
Defines multiple independent pipelines |
--config.test_and_exit |
Validates config syntax without running |
--config.reload.automatic |
Auto-reload config on file changes |
Tip: Always test your pipeline configuration with
--config.test_and_exitbefore deploying to production.
👉 Next: Input Plugins