-
Notifications
You must be signed in to change notification settings - Fork 0
Monitoring
The forwarder exposes three complementary signals for operations: HTTP metrics, periodic status logs, and process health (via your supervisor or orchestrator).
Set metrics.enabled: true in your config. The forwarder starts a small HTTP server that serves:
| Endpoint | Purpose |
|---|---|
GET /metrics |
Prometheus scrape endpoint (OpenTelemetry) |
GET /health |
Liveness probe — returns {"status":"UP","process_id":<pid>}
|
GET /ready |
Readiness probe (sink, buffer, hibernate) — includes process_id
|
GET /deadletters |
Dead letter batch metadata only (when publish_batch.dead_letter.path is configured) |
Both endpoints share the same metrics.host and metrics.port. /health is only available when metrics are enabled.
curl http://127.0.0.1:8080/health
curl http://127.0.0.1:8080/metrics
curl http://127.0.0.1:8080/deadlettersGET /deadletters returns a JSON array of batch metadata (filename, created_at, event_count, bytes, failure_reason, sink_type, batch_attempts). It does not return log record bodies. Retrieve spilled content from the dead_letter.path volume (for example kubectl exec in Kubernetes). Do not expose the management port to untrusted networks without authentication.
Bind to 127.0.0.1 when Prometheus runs on the same host. Use 0.0.0.0 only if a remote scraper needs direct access, and restrict access at the network layer.
Add a scrape job targeting the forwarder management port:
scrape_configs:
- job_name: log-forwarder
static_configs:
- targets: ["localhost:8080"]
metrics_path: /metrics
scrape_interval: 15sIf you changed metrics.path in config, use that value for metrics_path.
Use /health for liveness probes. It confirms the management server is running only; it does not verify sink connectivity after startup. Both /health and /ready include process_id (the forwarder OS PID) so controllers and operators can correlate probes with ATC registration.
Example:
{"status":"UP","process_id":12345}
{"status":"READY","process_id":12345}Use /ready for readiness probes when metrics.enabled: true. It returns 503 when:
- the sink fails its connectivity check (when the sink implements
sink.Checkerandmetrics.readiness.sink_checkis true) -
pipeline buffer depth / capacityexceedsmetrics.readiness.buffer_threshold(default0.8) -
metrics.readiness.require_files: trueand no log files are being tailed - the forwarder is in hibernate mode after a failed publish batch (
reason: sink_hibernating)
Use /health for liveness only during hibernate: the process is still running and blocked on backpressure, but it should not receive new traffic until /ready is 200 again.
Kubernetes example:
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 30
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10Optional readiness tuning:
metrics:
enabled: true
host: 0.0.0.0
port: 8080
readiness:
path: /ready
buffer_threshold: 0.8
sink_check: true
require_files: false
sink_check_timeout: 5sPair readiness with Prometheus alerts on log_forwarder_publish_failures and log_forwarder_pipeline_buffer_depth for sustained sink or backlog issues.
Configure periodic status logging to stderr or a log file:
logging:
level: info
format: json
status_interval: 30sEvery status_interval, the forwarder logs a forwarder status line with watched_files — useful when metrics are disabled or as a secondary signal. Set status_interval: 0 to disable.
Other useful log lines:
| Log message | Meaning |
|---|---|
log forwarder started |
Process is up; includes sources, sink type, metrics address, and atc_url when ATC is enabled |
sink connectivity verified |
Startup sink check passed |
sink unavailable at startup |
Forwarder refused to start — destination unreachable |
atc registration status |
ATC lifecycle (status: disabled, registering, registered, failed, deregistering, deregistered, deregistration_failed) — see Log Forwarder ATC
|
shutdown signal received |
SIGINT/SIGTERM received; deregister runs before runner cancel |
publish failed, retrying |
Transient publish error; check sink destination and network |
forwarder stopped |
Clean or error shutdown |
Forwarder metrics:
| Metric | Description |
|---|---|
log_forwarder_lines_read |
Newly appended log lines read from watched files (excludes at-least-once replays on restart) |
log_forwarder_lines_replayed |
Lines re-read after restart because the on-disk watermark lagged behind published data |
log_forwarder_lines_published |
Lines published to the configured sink |
log_forwarder_lines_filtered |
Lines dropped by configured filters (after transform) |
log_forwarder_lines_skipped |
Lines dropped (transform.on_error: skip) |
log_forwarder_pipeline_buffer_dropped |
Lines dropped when pipeline.on_full: drop and buffer is full |
log_forwarder_transform_errors |
Transform failures |
log_forwarder_timestamp_parse_failures |
Records that fell back to processing time during timestamp normalization |
log_forwarder_publish_failures |
Failed sink publish attempts |
log_forwarder_publish_truncations |
Records truncated to fit pipeline.max_publish_bytes
|
log_forwarder_publish_batch_flushes |
Publish buffer flushes (reason: size, timer, shutdown, wake; result: success, hibernate, dead_letter, error) |
log_forwarder_publish_hibernating |
1 when the forwarder is in sink hibernate mode after a failed batch flush |
log_forwarder_publish_dead_letter_batches |
Publish batches written to dead letter storage |
log_forwarder_publish_consecutive_dlq_batches |
Consecutive dead-letter batches without a successful sink publish |
log_forwarder_publish_batch_size |
Records per publish batch flush (histogram) |
log_forwarder_publish_batch_bytes |
Serialized JSON bytes per publish batch flush (histogram) |
log_forwarder_publish_buffer_active_bytes |
Serialized JSON bytes waiting in the publish buffer |
log_forwarder_publish_retries |
Retries after a publish failure |
log_forwarder_publish_duration |
Sink publish latency (histogram, seconds) |
log_forwarder_watermark_flush_errors |
Failed watermark persist attempts |
log_forwarder_files_watched |
Files currently being tailed |
log_forwarder_pipeline_buffer_depth |
Events queued between watcher and pipeline |
log_forwarder_pipeline_buffer_capacity |
Configured pipeline.buffer_size
|
Line accounting: lines_read counts only bytes appended after the file size observed at tail open (or all lines when tailing from the beginning). On restart with a stale watermark, bytes that were already in the file but not yet persisted to disk are counted as lines_replayed, not lines_read. Together:
lines_read + lines_replayed ≈ lines_published + lines_filtered + lines_skipped + pipeline_buffer_dropped + (lines waiting in publish batch) + (lines waiting in multiline parser buffer)
Replayed lines may still be published again (at-least-once delivery). With default parser.flush_interval (100ms for multiline), the multiline parser buffer is usually empty within ~100ms of the last line — a gap between lines_read and lines_published alone is not data loss. Check lines_replayed, filter/skip/drop counters, publish batch timing, and whether parser.flush_interval is 0.
Process and runtime metrics:
| Metric | Description |
|---|---|
process_memory_usage |
Process RSS in bytes |
process_cpu_utilization |
CPU used as percent of one core (1.3 = 1.3%; 100 = one core fully busy; can exceed 100 when using multiple cores). Same basis as top / psutil. Value is averaged over the interval since the previous scrape — align scrape_interval with how reactive you want the number; first scrape is 0. |
process_cpu_time |
Cumulative process CPU time in seconds (user/system counters) |
go_memory_used |
Go runtime memory in use |
go_memory_allocated |
Heap memory allocated by the application |
go_cpu_time |
CPU time spent by the Go runtime |
go_goroutine_count |
Number of live goroutines |
Machine-readable catalog: examples/config-catalog.yaml (metrics_catalog section).
| Signal | Suggested condition | Likely cause |
|---|---|---|
| Publish failures |
rate(log_forwarder_publish_failures[5m]) > 0 sustained |
Sink unreachable, auth/TLS issue, or network partition |
| Publish retries |
rate(log_forwarder_publish_retries[5m]) rising |
Intermittent sink or timeout pressure |
| Publish latency |
histogram_quantile(0.95, rate(log_forwarder_publish_duration_bucket[5m])) high |
Sink load, network latency, or slow endpoint |
| Buffer backlog |
log_forwarder_pipeline_buffer_depth / log_forwarder_pipeline_buffer_capacity > 0.8 sustained |
Pipeline slower than ingest; risk of backpressure |
| No files watched |
log_forwarder_files_watched == 0 while logs are expected |
Wrong watch paths, patterns, or permissions |
| Buffer drops | rate(log_forwarder_pipeline_buffer_dropped[5m]) > 0 |
pipeline.on_full: drop under overload — permanent log loss; not recoverable on restart |
| Read/publish gap |
rate(log_forwarder_lines_read[5m]) >> rate(log_forwarder_lines_published[5m])
|
Transform skips, filter drops, buffer drops (on_full: drop), persistent publish failures, or pipeline stall |
| Replay after restart | rate(log_forwarder_lines_replayed[5m]) > 0 |
On-disk watermark lagged behind published data (at-least-once replay); tighten watch.state.flush_interval or use flush_interval: 0 if duplicates are costly |
| High filter rate |
rate(log_forwarder_lines_filtered[5m]) high vs lines_read
|
Expected when filtering noisy logs; tune rules if too aggressive |
| Memory growth |
process_memory_usage or go_memory_used trending up without stabilizing |
Possible leak or sustained backlog |
| High CPU |
process_cpu_utilization sustained above your baseline (e.g. > 80 for one full core) |
Heavy regex/multiline parsing, publish backlog, or undersized host |
| Process down |
/health failing or scrape target up == 0
|
Crash, OOM kill, or misconfigured port |
curl -s http://127.0.0.1:8080/metrics | grep -E 'process_cpu_utilization|process_memory_usage'
curl -s http://127.0.0.1:8080/metrics | grep log_forwarder_lines_
curl -s http://127.0.0.1:8080/health
curl -s http://127.0.0.1:8080/ready
curl -s http://127.0.0.1:8080/metrics | headScrape at least twice (or wait one scrape_interval) before expecting a non-zero process_cpu_utilization.
Optional registration with log-forwarder-atc — a separate fleet controller that tracks agents, polls /health and /ready, and stores metric snapshots. Disabled by default.
Full guide (both repos): Log Forwarder ATC
| Component | Role |
|---|---|
| Forwarder |
PUT once after startup; DELETE once before shutdown. Exposes /health, /ready, /metrics on metrics.host:metrics.port. |
| ATC | Registry + fleet dashboard. Polls agents every 30s (default). No forwarder traffic while running. |
metrics:
enabled: true
host: 0.0.0.0
port: 10001
atc:
enabled: true
url: http://localhost:8090/api/instancesRegistration failures log atc registration status at WARN and do not stop tailing or publishing.
See Log Forwarder ATC, configs/example-atc.yaml, and configs/example-spring-boot-kafka.yaml.
User guide
- Home
- Installation and First Run
- How It Works
- Filtering Vendor Noise
- Log Forwarder ATC
- Configuration Guide
- Configuration-Reference
- Config-Catalog
- Choosing a Sink
- Spring Boot Logs
- Watermarks and Restarts
- Built-in-Components
- Custom-Extensions
- Monitoring
- Testing
- Docker
- Deployment
- Example Configs
- Troubleshooting
Contributors