@@ -20,6 +20,7 @@ import (
2020 "time"
2121
2222 "github.com/oklog/run"
23+ "github.com/prometheus/client_golang/prometheus"
2324 "github.com/prometheus/common/model"
2425
2526 "github.com/prometheus/alertmanager/config"
@@ -33,26 +34,39 @@ import (
3334// currently active alerts and a set of inhibition rules. It implements the
3435// Muter interface.
3536type Inhibitor struct {
36- alerts provider.Alerts
37- rules []* InhibitRule
38- marker types.AlertMarker
39- logger * slog.Logger
37+ alerts provider.Alerts
38+ rules []* InhibitRule
39+ marker types.AlertMarker
40+ logger * slog.Logger
41+ metrics * InhibitorMetrics
4042
4143 mtx sync.RWMutex
4244 cancel func ()
4345}
4446
4547// NewInhibitor returns a new Inhibitor.
46- func NewInhibitor (ap provider.Alerts , rs []config.InhibitRule , mk types.AlertMarker , logger * slog.Logger ) * Inhibitor {
48+ func NewInhibitor (ap provider.Alerts , rs []config.InhibitRule , mk types.AlertMarker , logger * slog.Logger , metrics * InhibitorMetrics ) * Inhibitor {
4749 ih := & Inhibitor {
48- alerts : ap ,
49- marker : mk ,
50- logger : logger ,
50+ alerts : ap ,
51+ marker : mk ,
52+ logger : logger ,
53+ metrics : metrics ,
5154 }
52- for _ , cr := range rs {
53- r := NewInhibitRule (cr )
55+
56+ ruleNames := make (map [string ]struct {})
57+ for i , cr := range rs {
58+ if _ , ok := ruleNames [cr .Name ]; ok {
59+ ih .logger .Debug ("duplicate inhibition rule name" , "index" , i , "name" , cr .Name )
60+ }
61+
62+ r := NewInhibitRule (cr , NewRuleMetrics (cr .Name , metrics ))
5463 ih .rules = append (ih .rules , r )
64+
65+ if cr .Name != "" {
66+ ruleNames [cr .Name ] = struct {}{}
67+ }
5568 }
69+
5670 return ih
5771}
5872
@@ -70,16 +84,30 @@ func (ih *Inhibitor) run(ctx context.Context) {
7084 continue
7185 }
7286 // Update the inhibition rules' cache.
87+ cachedSum := 0
88+ indexedSum := 0
7389 for _ , r := range ih .rules {
7490 if r .SourceMatchers .Matches (a .Labels ) {
7591 if err := r .scache .Set (a ); err != nil {
7692 ih .logger .Error ("error on set alert" , "err" , err )
7793 continue
7894 }
79-
8095 r .updateIndex (a )
96+
97+ cached := r .scache .Len ()
98+ indexed := r .sindex .Len ()
99+
100+ if r .Name != "" {
101+ r .metrics .sourceAlertsCacheItems .With (prometheus.Labels {"rule" : r .Name }).Set (float64 (cached ))
102+ r .metrics .sourceAlertsIndexItems .With (prometheus.Labels {"rule" : r .Name }).Set (float64 (indexed ))
103+ }
104+
105+ cachedSum += cached
106+ indexedSum += indexed
81107 }
82108 }
109+ ih .metrics .sourceAlertsCacheItems .Set (float64 (cachedSum ))
110+ ih .metrics .sourceAlertsIndexItems .Set (float64 (indexedSum ))
83111 }
84112 }
85113}
@@ -128,21 +156,29 @@ func (ih *Inhibitor) Stop() {
128156// Mutes returns true iff the given label set is muted. It implements the Muter
129157// interface.
130158func (ih * Inhibitor ) Mutes (lset model.LabelSet ) bool {
159+ start := time .Now ()
131160 fp := lset .Fingerprint ()
132161
133162 for _ , r := range ih .rules {
163+ ruleStart := time .Now ()
134164 if ! r .TargetMatchers .Matches (lset ) {
135165 // If target side of rule doesn't match, we don't need to look any further.
166+ r .metrics .matchesDuration .With (prometheus.Labels {"rule" : r .Name , "matched" : "false" }).Observe (time .Since (ruleStart ).Seconds ())
136167 continue
137168 }
169+ r .metrics .matchesDuration .With (prometheus.Labels {"rule" : r .Name , "matched" : "true" }).Observe (time .Since (ruleStart ).Seconds ())
138170 // If we are here, the target side matches. If the source side matches, too, we
139171 // need to exclude inhibiting alerts for which the same is true.
140172 if inhibitedByFP , eq := r .hasEqual (lset , r .SourceMatchers .Matches (lset )); eq {
141173 ih .marker .SetInhibited (fp , inhibitedByFP .String ())
174+ ih .metrics .mutesDuration .With (prometheus.Labels {"muted" : "true" }).Observe (time .Since (start ).Seconds ())
175+ r .metrics .mutesDuration .With (prometheus.Labels {"rule" : r .Name , "muted" : "true" }).Observe (time .Since (ruleStart ).Seconds ())
142176 return true
143177 }
178+ r .metrics .mutesDuration .With (prometheus.Labels {"rule" : r .Name , "muted" : "false" }).Observe (time .Since (ruleStart ).Seconds ())
144179 }
145180 ih .marker .SetInhibited (fp )
181+ ih .metrics .mutesDuration .With (prometheus.Labels {"muted" : "false" }).Observe (time .Since (start ).Seconds ())
146182
147183 return false
148184}
@@ -173,14 +209,17 @@ type InhibitRule struct {
173209 // The index items might overwrite eachother if multiple source alerts have exact equal labels.
174210 // Overwrites only happen if the new source alert has bigger EndsAt value.
175211 sindex * index
212+
213+ metrics * RuleMetrics
176214}
177215
178216// NewInhibitRule returns a new InhibitRule based on a configuration definition.
179- func NewInhibitRule (cr config.InhibitRule ) * InhibitRule {
217+ func NewInhibitRule (cr config.InhibitRule , metrics * RuleMetrics ) * InhibitRule {
180218 var (
181219 sourcem labels.Matchers
182220 targetm labels.Matchers
183221 )
222+
184223 // cr.SourceMatch will be deprecated. This for loop appends regex matchers.
185224 for ln , lv := range cr .SourceMatch {
186225 matcher , err := labels .NewMatcher (labels .MatchEqual , ln , lv )
@@ -235,6 +274,7 @@ func NewInhibitRule(cr config.InhibitRule) *InhibitRule {
235274 Equal : equal ,
236275 scache : store .NewAlerts (),
237276 sindex : newIndex (),
277+ metrics : metrics ,
238278 }
239279
240280 rule .scache .SetGCCallback (rule .gcCallback )
@@ -310,6 +350,10 @@ func (r *InhibitRule) gcCallback(alerts []types.Alert) {
310350 fp := r .fingerprintEquals (a .Labels )
311351 r .sindex .Delete (fp )
312352 }
353+ if r .Name != "" {
354+ r .metrics .sourceAlertsCacheItems .With (prometheus.Labels {"rule" : r .Name }).Set (float64 (r .scache .Len ()))
355+ r .metrics .sourceAlertsIndexItems .With (prometheus.Labels {"rule" : r .Name }).Set (float64 (r .sindex .Len ()))
356+ }
313357}
314358
315359// hasEqual checks whether the source cache contains alerts matching the equal
0 commit comments