cloudflare/pint

Public

mirrored from https://github.com/cloudflare/pintAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.76.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

internal/checks/alerts_count.go

141lines · modecode

1package checks
2
3import (
4 "context"
5 "fmt"
6 "log/slog"
7 "net/url"
8 "time"
9
10 "github.com/prometheus/common/model"
11
12 "github.com/cloudflare/pint/internal/diags"
13 "github.com/cloudflare/pint/internal/discovery"
14 "github.com/cloudflare/pint/internal/output"
15 "github.com/cloudflare/pint/internal/promapi"
16)
17
18const (
19 AlertsCheckName = "alerts/count"
20)
21
22func NewAlertsCheck(prom *promapi.FailoverGroup, lookBack, step, resolve time.Duration, minCount int, comment string, severity Severity) AlertsCheck {
23 return AlertsCheck{
24 prom: prom,
25 lookBack: lookBack,
26 step: step,
27 resolve: resolve,
28 minCount: minCount,
29 comment: comment,
30 severity: severity,
31 instance: fmt.Sprintf("%s(%s)", AlertsCheckName, prom.Name()),
32 }
33}
34
35type AlertsCheck struct {
36 prom *promapi.FailoverGroup
37 comment string
38 instance string
39 lookBack time.Duration
40 step time.Duration
41 resolve time.Duration
42 minCount int
43 severity Severity
44}
45
46func (c AlertsCheck) Meta() CheckMeta {
47 return CheckMeta{
48 States: []discovery.ChangeType{
49 discovery.Noop,
50 discovery.Added,
51 discovery.Modified,
52 discovery.Moved,
53 },
54 Online: true,
55 AlwaysEnabled: false,
56 }
57}
58
59func (c AlertsCheck) String() string {
60 return c.instance
61}
62
63func (c AlertsCheck) Reporter() string {
64 return AlertsCheckName
65}
66
67func (c AlertsCheck) Check(ctx context.Context, entry discovery.Entry, _ []discovery.Entry) (problems []Problem) {
68 if entry.Rule.AlertingRule == nil {
69 return problems
70 }
71
72 if entry.Rule.AlertingRule.Expr.SyntaxError != nil {
73 return problems
74 }
75
76 params := promapi.NewRelativeRange(c.lookBack, c.step)
77
78 qr, err := c.prom.RangeQuery(ctx, entry.Rule.AlertingRule.Expr.Value.Value, params)
79 if err != nil {
80 problems = append(problems, problemFromError(err, entry.Rule, c.Reporter(), c.prom.Name(), Bug))
81 return problems
82 }
83
84 if len(qr.Series.Ranges) > 0 {
85 promUptime, err := c.prom.RangeQuery(ctx, wrapExpr(c.prom.UptimeMetric(), "count"), params)
86 if err != nil {
87 slog.LogAttrs(ctx, slog.LevelWarn, "Cannot detect Prometheus uptime gaps", slog.Any("err", err), slog.String("name", c.prom.Name()))
88 } else {
89 // FIXME: gaps are not used
90 qr.Series.FindGaps(promUptime.Series, qr.Series.From, qr.Series.Until)
91 }
92 }
93
94 var forDur model.Duration
95 if entry.Rule.AlertingRule.For != nil {
96 forDur, _ = model.ParseDuration(entry.Rule.AlertingRule.For.Value)
97 }
98 var keepFiringForDur model.Duration
99 if entry.Rule.AlertingRule.KeepFiringFor != nil {
100 keepFiringForDur, _ = model.ParseDuration(entry.Rule.AlertingRule.KeepFiringFor.Value)
101 }
102
103 var alerts int
104 for _, r := range qr.Series.Ranges {
105 // If `keepFiringFor` is not defined its Duration will be 0
106 if r.End.Sub(r.Start) > (time.Duration(forDur) + time.Duration(keepFiringForDur)) {
107 alerts++
108 }
109 }
110
111 if alerts < c.minCount {
112 return problems
113 }
114
115 delta := qr.Series.Until.Sub(qr.Series.From).Round(time.Minute)
116 details := fmt.Sprintf(`To get a preview of the alerts that would fire please [click here](%s/graph?g0.expr=%s&g0.tab=0&g0.range_input=%s).`,
117 qr.URI, url.QueryEscape(entry.Rule.AlertingRule.Expr.Value.Value), output.HumanizeDuration(delta),
118 )
119 if c.comment != "" {
120 details = fmt.Sprintf("%s\n%s", details, maybeComment(c.comment))
121 }
122
123 problems = append(problems, Problem{
124 Anchor: AnchorAfter,
125 Lines: entry.Rule.AlertingRule.Expr.Value.Pos.Lines(),
126 Reporter: c.Reporter(),
127 Summary: "alert count estimate",
128 Details: details,
129 Severity: c.severity,
130 Diagnostics: []diags.Diagnostic{
131 {
132 Message: fmt.Sprintf("%s would trigger %d alert(s) in the last %s.", promText(c.prom.Name(), qr.URI), alerts, output.HumanizeDuration(delta)),
133 Pos: entry.Rule.AlertingRule.Expr.Value.Pos,
134 FirstColumn: 1,
135 LastColumn: len(entry.Rule.AlertingRule.Expr.Value.Value),
136 Kind: diags.Issue,
137 },
138 },
139 })
140 return problems
141}
142