Most Fluentd monitoring setups watch retry_count, buffer_queue_length, and process liveness. Almost none watch emit_error_count. The usual discovery is postmortem: hours of logs are missing, and the missing window is the exact window the incident needed.
emit_error_count counts emit transactions that failed inside the pipeline: events that could not be handed off and will never be delivered. Any nonzero rate means data loss is happening right now. This is not a degradation signal or a leading indicator. It is confirmation that events are gone.
This article covers what increments the counter, how to find it on your installation (the field is not exposed identically everywhere), how to tell a buffer overflow from a plugin exception, and which companion signals you must watch because one common overflow path never increments this counter at all.
What this means
Every event in Fluentd travels Input to Parser to Filter chain to Buffer to Output. An emit error is raised when an event cannot complete a handoff along that path. The two dominant causes:
- The buffer rejected the event. The buffer is full (
total_limit_sizereached) andoverflow_actionis throwing an exception for incoming events. - A plugin raised an exception while processing the event. A parser, filter, or output plugin crashed mid-transaction, or a malformed event was rejected.
The same failures also surface in Fluentd’s own log as buffer overflow and emit transaction failed messages, so even on builds where the counter field is not exposed through the monitor_agent API, the loss is visible in the log stream.
The counter is cumulative since process start. The absolute value is nearly meaningless on its own (a long-running process may carry a stale nonzero value from an old incident). What matters is the rate: delta(emit_error_count) over your sampling interval. Zero tolerance in production. Each increment is a permanently lost event.
flowchart LR
A[Input plugins] --> B[Filter chain]
B --> C{Buffer has space?}
C -->|yes| D[Chunk staged and queued]
D --> E[Output flush]
C -->|no| F[overflow_action fires]
F -->|throw_exception| G[Event lost at input - often no counter]
F -->|drop_oldest_chunk| H[Oldest chunk discarded - drop_oldest_chunk_count]
B -->|plugin exception| I[emit transaction failed - emit_error_count]
F -->|exception path| ICommon causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| Buffer overflow, destination down or slow | buffer_available_buffer_space_ratios near 0%, retry_count climbing, write_count flat | Destination health, Fluentd error log for connection or HTTP errors |
overflow_action: drop_oldest_chunk doing its job | Buffer never looks full, queue stays low, data silently discarded, drop_oldest_chunk_count rising | The overflow_action setting on each output’s buffer section |
| Plugin exception during processing | emit transaction failed in logs with an exception class and backtrace, throughput normal otherwise | Fluentd log around the first failure timestamp |
| Malformed events rejected by a filter or parser | Small steady trickle of emit errors correlated with one application or tag | Which plugin_id carries the counter; sample the offending source |
| Retry exhaustion without a secondary | Chunks discarded after retry_timeout, buffer drains without delivery | Retry state in monitor_agent, whether <secondary> is configured |
Quick checks
All read-only. Paths shown are for the td-agent package; fluent-package uses /var/log/fluent/fluentd.log and /etc/fluent/fluentd.conf, and Kubernetes deployments usually read logs via kubectl logs.
# 1. Check whether emit_error_count is exposed for any plugin
curl -s http://localhost:24220/api/plugins.json | python3 -c "
import sys,json
for p in json.load(sys.stdin)['plugins']:
if 'emit_error_count' in p:
print(p['plugin_id'], 'emit_errors=', p['emit_error_count'])"
# 2. Count overflow and emit-failure lines in Fluentd's own log
grep -c "buffer overflow\|emit transaction failed" /var/log/td-agent/td-agent.log
# 3. See the most recent occurrences with context
grep -E "buffer overflow|emit transaction failed" /var/log/td-agent/td-agent.log | tail -20
# 4. Check how close each output's buffer is to its limit
curl -s http://localhost:24220/api/plugins.json | \
jq '.plugins[] | select(.plugin_category=="output") | {id: .plugin_id, avail_pct: .buffer_available_buffer_space_ratios, queue: .buffer_queue_length}'
# 5. Check whether chunks are being discarded by overflow policy
curl -s http://localhost:24220/api/plugins.json | \
jq '.plugins[] | select(.plugin_category=="output") | {id: .plugin_id, dropped: .drop_oldest_chunk_count}'
# 6. Compare input vs output volume (sustained deficit = loss somewhere)
curl -s http://localhost:24220/api/plugins.json | \
jq '[.plugins[] | select(.plugin_category=="input") | .emit_records // 0] | add'
curl -s http://localhost:24220/api/plugins.json | \
jq '[.plugins[] | select(.plugin_category=="output") | .emit_records // 0] | add'
# 7. Confirm the retry and write state of each output
curl -s http://localhost:24220/api/plugins.json | \
jq '.plugins[] | select(.plugin_category=="output") | {id: .plugin_id, retries: .retry_count, writes: .write_count}'
# 8. Check the configured overflow_action on every output buffer
grep -B2 -A8 "overflow_action\|<buffer" /etc/td-agent/td-agent.conf
Two caveats on check 1. Field availability varies by Fluentd version and build; on some installations the counter is not present in the monitor_agent response at all, which is exactly why check 2 (the log grep) matters. And depending on how you scrape metrics, the same underlying counter may be exported under a different name by your metrics pipeline. Verify what your collector actually ingests rather than assuming the field name.
How to diagnose it
Confirm loss is active, not historical. The counter is cumulative. Take two readings 60 seconds apart. If the value did not move, you are looking at residue from an old incident; find the incident window in the log instead of chasing a live problem. If it moved, continue.
Identify which plugin carries the increment. The counter is per plugin. The plugin_id tells you which output (or which route) is failing. In multi-worker mode, query each worker’s monitor_agent port (24220, 24221, and so on); an aggregate view can hide a single worker that is losing data.
Separate the two failure families. Look at the buffer gauges for that output:
buffer_available_buffer_space_ratiosat or near 0% andbuffer_queue_lengthat maximum: this is buffer overflow. Go to step 4.- Buffer healthy, plenty of free space, counter still rising: this is a plugin exception or event rejection. Go to step 5.
For overflow: find out why the buffer filled. Check
retry_count(destination rejecting),write_count(flat means fully stalled), andslow_flush_countplusflush_time_count / write_count(destination slow rather than dead). Then check the destination directly: cluster health for Elasticsearch, broker state for Kafka, endpoint reachability for S3 or HTTP outputs. The Fluentd error log will usually name the failure: connection refused, 401/403, 429, TLS errors.For exceptions: read the log.
emit transaction failedentries are accompanied by an exception class and usually a backtrace. Common patterns are a parser choking on a format change from an upstream application, a filter raising on an unexpected field type, or one poison event hitting a route repeatedly. Correlate the timestamp with any recent deploys, config reloads, or upstream application changes.Check the overflow_action configuration regardless. This determines your exposure during the next destination outage.
throw_exceptionis the default and drops new events at the input when the buffer is full;drop_oldest_chunkdiscards old buffered data;blockstops the input threads and pushes backpressure upstream. None of these is “safe” by default. They just move the loss to a different place.Quantify the loss for the incident record. Compare input
emit_recordsagainst outputemit_recordsover the incident window. The gap is your lower bound on lost events. On older Fluentd versions, input-side metrics may need to be enabled explicitly; the draft this was edited from citedenable_input_metrics truein<system>for versions before v1.19.0. Without input metrics, the input counter reads zero and this comparison is impossible.
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
emit_error_count rate | The direct data-loss signal | Any nonzero rate, however small |
buffer overflow / emit transaction failed log lines | Catches loss on builds where the counter is not exposed | Any occurrence in production |
drop_oldest_chunk_count | Confirmed chunk discard when overflow_action is drop_oldest_chunk | Any increment |
buffer_available_buffer_space_ratios | Leading indicator before overflow fires | Below 20% and falling |
buffer_queue_length | Backlog depth ahead of the output | Sustained growth, or pinned at limit |
retry_count and retry state | Destination failing; backoff stretching recovery | Nonzero sustained, retry.next_time far in the future |
Input vs output emit_records balance | The net check that catches loss paths with no counter | Sustained deficit over a 15-minute window |
write_count rate | Proof chunks are actually leaving | Flat while input is active |
The last row of that table deserves emphasis. With the default overflow_action: throw_exception, events rejected at a full buffer are dropped silently and no error counter reliably increments. Your only reliable detection for that path is buffer_available_buffer_space_ratios at 0% combined with an input/output rate gap. Monitoring emit_error_count alone does not close the hole; it closes most of it.
Fixes
Destination failure causing buffer overflow
Restore the destination first; everything else is buying time. While it is down:
- Raise
total_limit_sizeif you have disk. For file-backed buffers this extends time-to-overflow. Compute runway as(total_limit_size - buffer_total_queued_size) / buffer growth ratebefore and after the change so you know what you bought. Make sure the filesystem holding the buffer has the space, and that the buffer is not sharing a partition with system logs. - Do not restart Fluentd to “clear” retries as a first move. File-backed buffers survive restart, but memory-backed buffers lose everything unflushed. A restart with memory buffers converts a delayed-delivery problem into confirmed data loss.
Wrong overflow_action for your tolerance
Pick the policy per output based on which loss you can survive:
block: no Fluentd-side loss, but input threads stall. For UDP syslog inputs the kernel drops packets instead, so the loss moves upstream and becomes invisible to Fluentd entirely. Acceptable when senders buffer (for example, forward protocol clients with their own disk buffers).drop_oldest_chunk: pipeline keeps flowing, newest data wins, and the loss is at least countable viadrop_oldest_chunk_count. Reasonable for metrics-like logs, dangerous for audit or security logs where the old events are exactly the ones you need.throw_exception(default): silent loss at the input with no reliable counter. Rarely what you want in production. If you keep it, monitoring buffer space ratios becomes non-optional.
Changing the buffer section requires a config reload or restart. A SIGHUP reload that partially applies is its own failure mode; verify the plugin list in monitor_agent after reloading.
Plugin exceptions
Fix the root cause in the parser or filter, not the symptom. If an upstream application changed its log format, update the parser to match or route that tag to a permissive fallback while you fix it. If a single malformed event is crashing the pipeline repeatedly, isolate the offending source and quarantine the pattern with a grep filter. Keep in mind that one bad line can also kill the whole process on every restart; see the poison pill pattern in the related guides.
Retry exhaustion
If chunks are being discarded after retry_timeout (default 72 hours) with no secondary configured, add a <secondary> output as a dead-letter destination (a local file or cheap object store works). Then write_secondary_count becomes your tripwire: any increment means the primary failed exhaustively for at least one chunk, and the data is recoverable from the fallback.
Prevention
- Alert on the rate, with zero tolerance. Any sustained nonzero
emit_error_countrate pages. Do not batch this into a weekly review metric; small increments accumulate into significant loss over hours, and they are easy to miss on dashboards. - Alert on the log pattern too. On builds where the counter is not exposed,
buffer overflowandemit transaction failedlog lines are the only emission-path loss signal. Ship Fluentd’s own logs somewhere that is not Fluentd. - Monitor the companions. Buffer space ratio, queue length, retry count, drop_oldest_chunk_count, and the input/output rate balance. The throw_exception path has no counter, so the balance check is your net.
- Choose overflow_action explicitly on every output. Write it into the config with a comment explaining the choice. The default is a data-loss decision made for you.
- Prefer file-backed buffers in production. Memory buffers are fast and volatile; an OOM kill during a destination outage destroys everything buffered. File buffers trade I/O for durability.
- Enable input metrics. Without input-side
emit_records, the input/output balance check is impossible. See the version caveat in the diagnosis section before assuming it works on your build. - Size the buffer for the outage you expect, not the one you hope for. Use the runway formula with your real growth rate and your real mean-time-to-repair for the destination.
How Netdata helps
- Netdata’s Fluentd collector scrapes the monitor_agent endpoint and charts per-plugin buffer and retry stats, so the “buffer filling, retries climbing” part of the overflow cascade is visible without manual polling.
- Cumulative counters are charted as rates, which is the value you actually alert on rather than an ever-growing total.
- Alerts can be set on buffer queue length and retry count, which covers the overflow paths that have no error counter of their own.
- Correlating Fluentd metrics with host-level signals (disk free on the buffer partition, process RSS, OOM events) shortens the jump from “data is being lost” to “here is why.”
Related guides
- Fluentd buffer available space low: computing time-to-overflow before it fires
- Fluentd BufferOverflowError: buffer space has too many data
- Fluentd buffer queue length growing: the output cannot keep pace with the input
- Fluentd config reload failed: SIGHUP that partially applies
- Fluentd CrashLoopBackOff: rapid restart cycling in Kubernetes
- How Fluentd actually works in production: a mental model for operators
- Fluentd monitor_agent not responding: a process that is up but hung
- Fluentd monitoring checklist: the signals every production log pipeline needs
- Fluentd monitoring maturity model: from survival to expert
- Fluentd plugin load error at startup: LoadError and missing gems
- Fluentd poison pill crash loop: one bad log line that kills the process on every restart
- Fluentd process not running: the log pipeline is dead and the host has gone dark






