@@ -44,60 +44,66 @@ pub struct Config {
4444 pub per_metric_limits : HashMap < String , PerMetricConfig > ,
4545}
4646
47- /// Configuration for the `tag_cardinality_limit` transform for a specific group of metrics .
47+ /// Configuration block used at the global level .
4848#[ configurable_component]
4949#[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
5050pub struct Inner {
51- /// How many distinct values to accept for any given key. Ignored when `exclude` is `true`.
51+ /// How many distinct values to accept for any given key.
5252 #[ serde( default = "default_value_limit" ) ]
5353 pub value_limit : usize ,
5454
5555 #[ configurable( derived) ]
5656 #[ serde( default = "default_limit_exceeded_action" ) ]
5757 pub limit_exceeded_action : LimitExceededAction ,
5858
59- /// Cardinality tracking mode. Required unless `exclude` is `true`. When both `exclude` and
60- /// `mode` are set, `exclude` takes precedence and `mode` is ignored.
61- #[ serde( flatten, default ) ]
62- pub mode : Option < Mode > ,
59+ #[ serde( flatten) ]
60+ pub mode : Mode ,
6361
6462 #[ configurable( derived) ]
6563 #[ serde( default ) ]
6664 pub internal_metrics : InternalMetricsConfig ,
65+ }
66+
67+ /// Configuration block used at per-metric level. Same shape as the global configuration but
68+ /// with `OverrideMode`, which adds `excluded` for opting that metric out of cardinality
69+ /// control entirely.
70+ #[ configurable_component]
71+ #[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
72+ pub struct OverrideInner {
73+ /// How many distinct values to accept for any given key. Ignored when `mode: excluded`.
74+ #[ serde( default = "default_value_limit" ) ]
75+ pub value_limit : usize ,
76+
77+ #[ configurable( derived) ]
78+ #[ serde( default = "default_limit_exceeded_action" ) ]
79+ pub limit_exceeded_action : LimitExceededAction ,
6780
68- /// Exclude this metric or tag from cardinality control entirely. When `true`, all tag values
69- /// pass through and nothing is tracked, regardless of any other fields on this entry.
81+ #[ serde( flatten) ]
82+ pub mode : OverrideMode ,
83+
84+ #[ configurable( derived) ]
7085 #[ serde( default ) ]
71- pub exclude : bool ,
86+ pub internal_metrics : InternalMetricsConfig ,
7287}
7388
74- impl Inner {
75- /// Validate that at least one of `exclude` or `mode` is set.
76- pub ( super ) fn validate ( & self , location : & str ) -> crate :: Result < ( ) > {
77- if !self . exclude && self . mode . is_none ( ) {
78- return Err (
79- format ! ( "{location}: `mode` is required unless `exclude: true` is set" ) . into ( ) ,
80- ) ;
81- }
82- Ok ( ( ) )
83- }
84- }
89+ /// Configuration block used at the per-tag level. Same as `OverrideInner` minus
90+ /// `limit_exceeded_action` — that field is inherited from the enclosing per-metric config
91+ #[ configurable_component]
92+ #[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
93+ pub struct PerTagInner {
94+ /// How many distinct values to accept for this tag key. Ignored when `mode: excluded`.
95+ #[ serde( default = "default_value_limit" ) ]
96+ pub value_limit : usize ,
8597
86- impl Config {
87- pub ( super ) fn validate ( & self ) -> crate :: Result < ( ) > {
88- self . global . validate ( "global" ) ?;
89- for ( name, pmc) in & self . per_metric_limits {
90- pmc. config . validate ( & format ! ( "per_metric_limits[{name}]" ) ) ?;
91- for ( tag, ptc) in & pmc. per_tag_limits {
92- ptc. config
93- . validate ( & format ! ( "per_metric_limits[{name}].per_tag_limits[{tag}]" ) ) ?;
94- }
95- }
96- Ok ( ( ) )
97- }
98+ #[ serde( flatten) ]
99+ pub mode : OverrideMode ,
100+
101+ #[ configurable( derived) ]
102+ #[ serde( default ) ]
103+ pub internal_metrics : InternalMetricsConfig ,
98104}
99105
100- /// Controls the approach taken for tracking tag cardinality.
106+ /// Controls the approach taken for tracking tag cardinality at the global level .
101107#[ configurable_component]
102108#[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
103109#[ serde( tag = "mode" , rename_all = "snake_case" , deny_unknown_fields) ]
@@ -120,6 +126,41 @@ pub enum Mode {
120126 Probabilistic ( BloomFilterConfig ) ,
121127}
122128
129+ /// Controls the approach taken for tracking tag cardinality at the per-metric or per-tag level.
130+ /// Adds `excluded` to the global `Mode` variants.
131+ #[ configurable_component]
132+ #[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
133+ #[ serde( tag = "mode" , rename_all = "snake_case" , deny_unknown_fields) ]
134+ #[ configurable( metadata(
135+ docs:: enum_tag_description = "Controls the approach taken for tracking tag cardinality."
136+ ) ) ]
137+ pub enum OverrideMode {
138+ /// Tracks cardinality exactly. See `Mode::Exact` for details.
139+ Exact ,
140+
141+ /// Tracks cardinality probabilistically. See `Mode::Probabilistic` for details.
142+ Probabilistic ( BloomFilterConfig ) ,
143+
144+ /// Skip cardinality tracking for this scope. All tag values pass through and nothing is
145+ /// recorded. Other tracking fields on the entry (`value_limit`, `limit_exceeded_action`,
146+ /// `internal_metrics`) are ignored when this is selected.
147+ ///
148+ /// Only valid in `per_metric_limits` and `per_tag_limits` entries; using it as the global
149+ /// `mode` is a configuration error.
150+ Excluded ,
151+ }
152+
153+ impl OverrideMode {
154+ /// Returns the equivalent global `Mode` if this scope is tracked, or `None` if excluded.
155+ pub const fn as_mode ( & self ) -> Option < Mode > {
156+ match self {
157+ OverrideMode :: Exact => Some ( Mode :: Exact ) ,
158+ OverrideMode :: Probabilistic ( b) => Some ( Mode :: Probabilistic ( * b) ) ,
159+ OverrideMode :: Excluded => None ,
160+ }
161+ }
162+ }
163+
123164/// Bloom filter configuration in probabilistic mode.
124165#[ configurable_component]
125166#[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
@@ -156,9 +197,10 @@ pub struct PerMetricConfig {
156197
157198 /// Per-tag-key overrides scoped to this metric.
158199 ///
159- /// Each entry has the same fields as a per-metric configuration. When a tag has an entry here,
160- /// that entry replaces the per-metric configuration for that tag. Tags not listed here use this
161- /// per-metric configuration.
200+ /// Each entry has the same fields as a per-metric configuration except `limit_exceeded_action`,
201+ /// which is always inherited from the enclosing per-metric (or global) configuration. When a
202+ /// tag has an entry here, that entry replaces the per-metric `value_limit`, `mode`, and
203+ /// `internal_metrics` for that tag. Tags not listed here use the per-metric configuration.
162204 #[ configurable(
163205 derived,
164206 metadata( docs:: additional_props_description = "An individual tag configuration." )
@@ -167,15 +209,15 @@ pub struct PerMetricConfig {
167209 pub per_tag_limits : HashMap < String , PerTagConfig > ,
168210
169211 #[ serde( flatten) ]
170- pub config : Inner ,
212+ pub config : OverrideInner ,
171213}
172214
173215/// Tag cardinality limit configuration for a specific tag key, scoped under a per-metric override.
174216#[ configurable_component]
175217#[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
176218pub struct PerTagConfig {
177219 #[ serde( flatten) ]
178- pub config : Inner ,
220+ pub config : PerTagInner ,
179221}
180222
181223const fn default_limit_exceeded_action ( ) -> LimitExceededAction {
@@ -198,11 +240,10 @@ impl GenerateConfig for Config {
198240 fn generate_config ( ) -> toml:: Value {
199241 toml:: Value :: try_from ( Self {
200242 global : Inner {
201- mode : Some ( Mode :: Exact ) ,
243+ mode : Mode :: Exact ,
202244 value_limit : default_value_limit ( ) ,
203245 limit_exceeded_action : default_limit_exceeded_action ( ) ,
204246 internal_metrics : InternalMetricsConfig :: default ( ) ,
205- exclude : false ,
206247 } ,
207248 per_metric_limits : HashMap :: default ( ) ,
208249 } )
@@ -214,7 +255,6 @@ impl GenerateConfig for Config {
214255#[ typetag:: serde( name = "tag_cardinality_limit" ) ]
215256impl TransformConfig for Config {
216257 async fn build ( & self , _context : & TransformContext ) -> crate :: Result < Transform > {
217- self . validate ( ) ?;
218258 Ok ( Transform :: event_task ( TagCardinalityLimit :: new (
219259 self . clone ( ) ,
220260 ) ) )
0 commit comments