in_statsd: timer metrics fall through into the "set" case and produce records with duplicate keys
In the non-metrics (log record) mode of in_statsd, the STATSD_TYPE_TIMER case in statsd_process_message() has no break, so every timer line (name:value|ms) appends a second set of type/bucket/value keys with type = "set" and a string value to the same record.
Details
case STATSD_TYPE_TIMER:
ret = flb_log_event_encoder_append_body_values(
ctx->log_encoder,
FLB_LOG_EVENT_CSTRING_VALUE("type"),
FLB_LOG_EVENT_CSTRING_VALUE("timer"),
... "bucket", ..., "value", <double>, "sample_rate", ...);
case STATSD_TYPE_SET: /* <- no break above */
ret = flb_log_event_encoder_append_body_values(
ctx->log_encoder,
FLB_LOG_EVENT_CSTRING_VALUE("type"),
FLB_LOG_EVENT_CSTRING_VALUE("set"),
... "bucket", ..., "value", <string>);
break;StatsD defines ms as a timing and s as a set; they are distinct metric types.
Trigger / observed result
UDP datagram lat:320|ms to an unmodified build (metrics off, the default). Raw record from the stdout output (issues/verification/fluentbit_inputs/fluent-bit_raw_records.txt):
{"type"=>"timer", "bucket"=>"lat", "value"=>320.000000, "sample_rate"=>1.000000,
"type"=>"set", "bucket"=>"lat", "value"=>"320"}With Format json_lines the duplicate keys collapse to the last occurrence, and the event is emitted as {"sample_rate":1.0,"type":"set","bucket":"lat","value":"320"}, so every timer is reported as a set with a string value.
Expected: {"type":"timer","bucket":"lat","value":320.0,"sample_rate":1.0}.
Impact
Data correctness only: every StatsD timer ingested in log mode is mislabeled (and, after JSON conversion, loses its numeric value type). No memory-safety impact. The metrics on path uses cmetrics' decoder and is not affected by this switch.
Suggested fix
Add break; at the end of the STATSD_TYPE_TIMER case, and add a runtime test for an |ms line.
Source: fluent/fluent-bit