distributor: log truncated oversized attributes with rate limited logger - #6467
Conversation
1566efd to
ae95d36
Compare
There was a problem hiding this comment.
I think we are in the good direction bu logging each truncated attribute in the inner loop is too costly for the distributor hot path.
My proposal is sampling plus a single log statement:
- On the first truncation observed, decide sampling rate of 0.001 if you want to log or not the truncated attributes
- Keep truncating and counting regardless of sampling outcome
- Emit one structured log
- Log the result in PushTraces
76dbc06 to
7e01c40
Compare
|
benchmark comparison with count 8, no significant difference |
7e01c40 to
e9311b8
Compare
5e48808 to
70079bb
Compare
680a76e to
fdd85ff
Compare
fdd85ff to
21f0dc8
Compare
| const maxAttrSize = 2048 | ||
| const numAttrs = 100 | ||
|
|
||
| makeAttrs := func() []*v1_common.KeyValue { |
There was a problem hiding this comment.
This is not reliable since most of the time of the benchmark is crafting the test data. We should do it just once and reuse it between iterations
There was a problem hiding this comment.
It is reliable in relative terms to show no extra allocations and a tiny overhead of nanoseconds. But you are right that ideally it would be done outside, but the problem is that processAttributes truncates the slice values in-place, so we would need to pre-generate millions of slices. Not doable. So I'm going do remove this once, since it's no longer useful and I pasted the benchstat results in the PR.
| if len(attr.Key) > maxAttrSize { | ||
| origSize := len(attr.Key) | ||
| attr.Key = attr.Key[:maxAttrSize] | ||
| if truncationExample != nil && truncationExample.origSize == 0 { // only capture the first truncation |
There was a problem hiding this comment.
I'm ok with this. We could also keep a example per scope but we should measure the impact
| metricAttributesTruncated.WithLabelValues(userID, "link").Add(float64(truncatedAttributesCount.Link)) | ||
|
|
||
| if truncationExample != nil { | ||
| d.truncationLogger.Log("msg", "attributes truncated", |
There was a problem hiding this comment.
Since we want to expose this to tenants should we include the insight=true?
There was a problem hiding this comment.
We can do this later once we see it's good from our side
Add warn-level logging when attribute keys or values exceed the configured max_attribute_bytes limit.
14c85cc to
3160d0a
Compare
…ger (grafana#6467) * distributor: log truncated oversized attributes with rate limited logger Add warn-level logging when attribute keys or values exceed the configured max_attribute_bytes limit. * remove benchmark, no longer useful
What this PR does:
global rate-limited logger for truncation warnings
The truncation log line is a supplementary diagnostic: it tells operators which attribute was truncated, at what scope, and how large it was. The primary observability channel is metricAttributesTruncated, which fires unconditionally per-tenant on every truncation.
This approach guarantees the first truncation event in any time window is always logged.
Rate-limited logger over sampling because at peak load (e.g. 20k push req/s per tenant), sampling at 0.1% still produces 20 log lines/s per pod. The rate-limited logger caps it at 1/s regardless of load.
The log lines are most useful for the noisiest tenant: the one generating the most truncations is the one operators most need diagnostic detail for. If tenant A dominates at 100k truncations/s, that's the tenant we want to see in logs. A quieter tenant B (10/s) is suppressed from logs but fully visible via per-tenant metrics.
Checklist
CHANGELOG.mdupdated - the order of entries should be[CHANGE],[FEATURE],[ENHANCEMENT],[BUGFIX]