@@ -10,16 +10,36 @@ import (
1010// look for in the Trace2 event stream to help us decide how to
1111// filter data for a particular command.
1212type FilterSettings struct {
13- Keynames FilterKeynames `mapstructure:"keynames"`
14- Nicknames FilterNicknames `mapstructure:"nicknames"`
15- Rulesets FilterRulesets `mapstructure:"rulesets"`
16- Defaults FilterDefaults `mapstructure:"defaults"`
13+ Keynames FilterKeynames `mapstructure:"keynames"`
14+ Nicknames FilterNicknames `mapstructure:"nicknames"`
15+ Rulesets FilterRulesets `mapstructure:"rulesets"`
16+ Defaults FilterDefaults `mapstructure:"defaults"`
17+ ImportantEvents []ImportantEventRule `mapstructure:"important_events"`
1718
1819 // The set of custom rulesets defined in YML are each parsed
1920 // and loaded into definitions so that we can use them.
2021 rulesetDefs map [string ]* RulesetDefinition
2122}
2223
24+ // ImportantEventRule defines a rule for promoting values from data events
25+ // that match a specific (category, key prefix) pair into the process
26+ // summary, regardless of the active detail level. This lets operators
27+ // guarantee that certain data event values are always captured and
28+ // surfaced in the OTEL process span even when verbose telemetry is
29+ // disabled. Multiple matching values are collected into an array.
30+ type ImportantEventRule struct {
31+ // Category is the data event category to match (exact match)
32+ Category string `mapstructure:"category"`
33+
34+ // KeyPrefix is the string prefix to match at the beginning of
35+ // the data event's key field
36+ KeyPrefix string `mapstructure:"key_prefix"`
37+
38+ // FieldName is the name of the field in the summary object
39+ // where matched values will be stored (always as an array)
40+ FieldName string `mapstructure:"field_name"`
41+ }
42+
2343// FilterKeynames defines the names of the Git config settings that
2444// will be used in `def_param` events to send repository/worktree
2545// data to us. This lets a site have their own namespace for
@@ -100,9 +120,52 @@ func parseFilterSettingsFromBuffer(data []byte, path string) (*FilterSettings, e
100120 }
101121 }
102122
123+ fieldNames := make (map [string ]bool )
124+ for i , rule := range fs .ImportantEvents {
125+ if len (rule .Category ) == 0 {
126+ return nil , fmt .Errorf ("important_events[%d]: category cannot be empty" , i )
127+ }
128+ if len (rule .KeyPrefix ) == 0 {
129+ return nil , fmt .Errorf ("important_events[%d]: key_prefix cannot be empty" , i )
130+ }
131+ if len (rule .FieldName ) == 0 {
132+ return nil , fmt .Errorf ("important_events[%d]: field_name cannot be empty" , i )
133+ }
134+ if fieldNames [rule .FieldName ] {
135+ return nil , fmt .Errorf ("important_events[%d]: duplicate field_name '%s'" , i , rule .FieldName )
136+ }
137+ fieldNames [rule .FieldName ] = true
138+ }
139+
103140 return fs , nil
104141}
105142
143+ // apply__important_events checks if a data event matches any configured
144+ // important_events rules and appends the event's value to the
145+ // importantEvents map if a match is found. Matching events are captured
146+ // regardless of nesting level or detail level.
147+ func apply__important_events (tr2 * trace2Dataset , category string , key string , value interface {}) {
148+ if tr2 .process .importantEvents == nil {
149+ return
150+ }
151+
152+ if tr2 .rcvr_base == nil || tr2 .rcvr_base .RcvrConfig == nil {
153+ return
154+ }
155+
156+ fs := tr2 .rcvr_base .RcvrConfig .filterSettings
157+ if fs == nil {
158+ return
159+ }
160+
161+ for _ , rule := range fs .ImportantEvents {
162+ if category == rule .Category && strings .HasPrefix (key , rule .KeyPrefix ) {
163+ tr2 .process .importantEvents [rule .FieldName ] = append (
164+ tr2 .process .importantEvents [rule .FieldName ], value )
165+ }
166+ }
167+ }
168+
106169// Add a ruleset to the filter settings. This is primarily for writing test code.
107170func (fs * FilterSettings ) addRuleset (rs_name string , path string , rsdef * RulesetDefinition ) {
108171 if fs .Rulesets == nil {
0 commit comments