diff --git a/src/internal_events/throttle.rs b/src/internal_events/throttle.rs index 08321d194588f..8d5484e75352a 100644 --- a/src/internal_events/throttle.rs +++ b/src/internal_events/throttle.rs @@ -4,27 +4,30 @@ use vector_lib::internal_event::{ComponentEventsDropped, InternalEvent, INTENTIO #[derive(Debug)] pub(crate) struct ThrottleEventDiscarded { pub key: String, + pub emit_events_discarded_per_key: bool, } impl InternalEvent for ThrottleEventDiscarded { fn emit(self) { - // TODO: Technically, the Component Specification states that the discarded events metric - // must _only_ have the `intentional` tag, in addition to the core tags like - // `component_kind`, etc, and nothing else. - // - // That doesn't give us the leeway to specify which throttle bucket the events are being - // discarded for... but including the key/bucket as a tag does seem useful and so I wonder - // if we should change the specification wording? Sort of a similar situation to the - // `error_code` tag for the component errors metric, where it's meant to be optional and - // only specified when relevant. - counter!( - "events_discarded_total", 1, - "key" => self.key, - ); // Deprecated. + let message = "Rate limit exceeded."; + + debug!(message, key = self.key, internal_log_rate_limit = true); + if self.emit_events_discarded_per_key { + // TODO: Technically, the Component Specification states that the discarded events metric + // must _only_ have the `intentional` tag, in addition to the core tags like + // `component_kind`, etc, and nothing else. + // + // That doesn't give us the leeway to specify which throttle bucket the events are being + // discarded for... but including the key/bucket as a tag does seem useful and so I wonder + // if we should change the specification wording? Sort of a similar situation to the + // `error_code` tag for the component errors metric, where it's meant to be optional and + // only specified when relevant. + counter!("events_discarded_total", 1, "key" => self.key); // Deprecated. + } emit!(ComponentEventsDropped:: { count: 1, - reason: "Rate limit exceeded." + reason: message }) } } diff --git a/src/transforms/throttle.rs b/src/transforms/throttle.rs index 721509f3b4038..496eb1ac9523c 100644 --- a/src/transforms/throttle.rs +++ b/src/transforms/throttle.rs @@ -18,6 +18,24 @@ use crate::{ transforms::{TaskTransform, Transform}, }; +/// Configuration of internal metrics for the Throttle transform. +#[configurable_component] +#[derive(Clone, Debug, PartialEq, Eq, Default)] +#[serde(deny_unknown_fields)] +pub struct ThrottleInternalMetricsConfig { + /// Whether or not to emit the `events_discarded_total` internal metric with the `key` tag. + /// + /// If true, the counter will be incremented for each discarded event, including the key value + /// associated with the discarded event. If false, the counter will not be emitted. Instead, the + /// number of discarded events can be seen through the `component_discarded_events_total` internal + /// metric. + /// + /// Note that this defaults to false because the `key` tag has potentially unbounded cardinality. + /// Only set this to true if you know that the number of unique keys is bounded. + #[serde(default)] + pub emit_events_discarded_per_key: bool, +} + /// Configuration for the `throttle` transform. #[serde_as] #[configurable_component(transform("throttle", "Rate limit logs passing through a topology."))] @@ -43,6 +61,10 @@ pub struct ThrottleConfig { /// A logical condition used to exclude events from sampling. exclude: Option, + + #[configurable(derived)] + #[serde(default)] + internal_metrics: ThrottleInternalMetricsConfig, } impl_generate_config_from_default!(ThrottleConfig); @@ -79,6 +101,7 @@ pub struct Throttle, I: clock::Reference> { key_field: Option