Help us shape the future of Go! Take the 2026 Go Developer Survey

alerting

package
v1.0.14 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2026 License: Apache-2.0 Imports: 28 Imported by: 0

Documentation

Overview

Package alerting is a SQL alert-rule evaluator in the Prometheus shape.

A Rule carries an `expr` that is a single SELECT. Every row it returns is one firing instance: the non-value columns become the instance's labels and the `value` column its sample, e.g.

SELECT ServiceName AS service, quantile(0.95)(Duration)/1e6 AS value
FROM otel_traces GROUP BY service HAVING value > 800

The package owns what is the same for every deployment: the SQL guard, the label rules, the inactive → pending → firing → resolved state machine with flap damping, a jittered scheduler sharded over forge/pkg/lease, and an Alertmanager v2 client. It owns nothing about WHERE rules come from, WHICH database an owner's query runs against, or where state is kept; those are the four interfaces below, declared here at the consumer.

An owner is an opaque string. The package never interprets it: it is handed to the Querier and recorded beside every instance, nothing more. Identity labels (who an alert belongs to) are supplied by the RuleSource on the Rule and are stamped LAST, so no query column or user label can override them.

A self-hoster runs it with chquery (a single-owner ClickHouse Querier), the pgstore Postgres StateStore (or NewMemoryStore) and any RuleSource.

Example

A single-owner deployment: a rules file, one ClickHouse, in-memory state. Swap oneRow for chquery.New(conn), printNotifier for alerting.NewAlertmanagerNotifier(nil, "http://alertmanager:9093"), and the MemoryStore for pgstore.New(db, "") to run it for real.

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/reliant-labs/forge/pkg/alerting"
)

type oneRow struct{}

func (oneRow) Query(context.Context, string, string, alerting.QueryOptions) ([]alerting.Row, error) {
	return []alerting.Row{{"service": "api", "value": 912.0}}, nil
}

type printNotifier struct{}

func (printNotifier) Notify(_ context.Context, alerts []alerting.Alert) error {
	for _, a := range alerts {
		fmt.Println(a.Labels["alertname"], a.Labels["service"])
	}
	return nil
}

// A single-owner deployment: a rules file, one ClickHouse, in-memory state.
// Swap oneRow for chquery.New(conn), printNotifier for
// alerting.NewAlertmanagerNotifier(nil, "http://alertmanager:9093"), and the
// MemoryStore for pgstore.New(db, "") to run it for real.
func main() {
	ev, _ := alerting.NewEvaluator(alerting.EvaluatorDeps{
		Querier: oneRow{}, Notifier: printNotifier{}, Store: alerting.NewMemoryStore(),
	})
	rule := alerting.RuleSpec{
		Alert: "SlowService",
		Expr:  "SELECT service, quantile(0.95)(duration_ms) AS value FROM spans GROUP BY service HAVING value > 800",
	}.Rule("self", "prod", map[string]string{"reliant_org_id": "self"})

	_, _ = ev.EvaluateRule(context.Background(), rule, nil)
	_ = time.Second
}
Output:
SlowService api

Index

Examples

Constants

View Source
const (
	DetectorIssueSpike       = "issue_spike"
	DetectorReleaseRegressed = "release_regressed"
	DetectorServiceAnomaly   = "service_anomaly"
	// DetectorUptimeCheck is a check that failed N probes in a row;
	// DetectorCertificateExpiring a certificate close to its expiry. Both read
	// the uptime prober's metrics (uptime.check.up, tls.cert.expiry_seconds).
	DetectorUptimeCheck         = "uptime_check"
	DetectorCertificateExpiring = "certificate_expiring"
)

Detector names, the Why.Detector values.

View Source
const DefaultMaxRulesPerEnv = 200

DefaultMaxRulesPerEnv is sized from measured evaluation cost. An owner's rules run one after another in a single lane per scheduler tick. Measured in prod (ClickHouse system.query_log, 6h, tenant zero's 100 evaluated rules; the Postgres-answered rules are not in it): 16.4s of ClickHouse time per minute, 164ms a rule averaged over all 100, of which two heavy queries are 12.9s and a typical one is 68ms (p95 116ms). At the 164ms average a lane saturates near 360 rules at a 1m interval; at the typical 68ms, past 800. The largest env there declares 79 rules, so 200 leaves every env 2.5x room, and one env at the cap costs about 33s of a 60s lane at the average rule. The cap bounds ONE env; the lane is shared by an owner's envs, which is why reaching the cap is never silent: see Overflow and OverCapAlertName.

View Source
const MaxExprBytes = 16 << 10

MaxExprBytes bounds a rule's expr.

View Source
const MaxNameLen = 128

MaxNameLen bounds a rule name.

View Source
const MaxWhyBytes = 16 << 10

MaxWhyBytes bounds one payload. An oversized payload is a bad result, not a truncation: cut JSON is not JSON.

View Source
const OverCapAlertName = "AlertRulesOverCap"

OverCapAlertName is the alert raised, once per env, while that env declares more rules than Limits.MaxRulesPerEnv: rules past the cap are not evaluated, so this is the only thing that says so.

View Source
const ReservedPrefix = "reliant_"

ReservedPrefix is the label-name prefix only the RuleSource may set, through Rule.Identity. A column or label a rule author writes with this prefix is dropped, in any letter case.

View Source
const ValueColumn = "value"

ValueColumn is the column whose number is an instance's sample.

View Source
const WhyAnnotation = "why"

WhyAnnotation is the annotation the Alertmanager notifier posts the payload under, so every receiver (in-app, Autopilot webhook, Slack templates) gets it.

View Source
const WhyColumn = "why"

WhyColumn is the column whose JSON object is an instance's why-fired payload (see Why). Like ValueColumn it is not a label, so a payload that changes between evaluations (the observed count, the baseline) never changes the alert's identity and never re-notifies.

Variables

View Source
var ErrBadExpr = errors.New("alerting: expr refused")

ErrBadExpr marks an expr the guard refused.

View Source
var (
	// ErrBadResult marks a result the evaluator could not turn into instances.
	ErrBadResult = errors.New("alerting: unusable result")
)

Functions

func AnnotationFuncs added in v1.0.14

func AnnotationFuncs() []string

AnnotationFuncs names the functions an annotation template may call beyond text/template's builtins, for docs and error messages.

func DedupeKey added in v1.0.0

func DedupeKey(detector, subject, release string) string

DedupeKey is the key alerts about one cause share: the detector, what it is about (a service, an env, an issue) and the release in play ("" when none).

func F added in v1.0.0

func F(v float64) *float64

F is a pointer to v, for Why's optional figures.

func ValidateAnnotations added in v1.0.14

func ValidateAnnotations(annotations map[string]string) error

ValidateAnnotations reports the first annotation whose template cannot render: it does not parse, uses a refused construct, calls an unknown function, or hands printf a constant format it refuses. It is the render-time check; evaluation itself never fails on an annotation.

func ValidateExpr

func ValidateExpr(expr string) error

ValidateExpr accepts exactly one read-only SELECT (optionally with a WITH prefix). It is a lexical check, not a parser, and is the FIRST of two lines of defence: the Querier must still run the statement with readonly settings and the evaluator's resource limits, which is what actually contains a query.

Types

type Alert

type Alert struct {
	Labels      map[string]string
	Annotations map[string]string
	StartsAt    time.Time
	// EndsAt is when the receiver should consider the alert resolved if it is
	// not refreshed: now+3×interval while firing, now once resolved.
	EndsAt time.Time
	// Why is the instance's why-fired payload (see Why): structured evidence
	// that rides with the alert but is not part of its identity. The
	// Alertmanager notifier posts it as the WhyAnnotation annotation. Nil when
	// the rule's rows carried no WhyColumn.
	Why json.RawMessage
}

Alert is one alert as posted to a Notifier.

type AlertmanagerNotifier

type AlertmanagerNotifier struct {

	// GeneratorURL, when set, is attached to every alert.
	GeneratorURL string
	// contains filtered or unexported fields
}

AlertmanagerNotifier posts to an Alertmanager v2 API. Give it every replica of an HA Alertmanager (they gossip and de-duplicate); an alert is delivered if ANY replica accepts it.

func NewAlertmanagerNotifier

func NewAlertmanagerNotifier(client *http.Client, baseURLs ...string) *AlertmanagerNotifier

NewAlertmanagerNotifier builds a notifier. baseURLs are scheme://host:port. A nil client gets a 10s-timeout default.

func (*AlertmanagerNotifier) Notify

func (n *AlertmanagerNotifier) Notify(ctx context.Context, alerts []Alert) error

Notify posts alerts to every replica.

type Changes

type Changes struct {
	Upsert []Instance
	Delete []InstanceRef
	Events []Event
}

Changes is what one evaluation step writes.

type Duration

type Duration time.Duration

Duration is a time.Duration that JSON-encodes as a Go duration string ("5m", "90s"), the way Prometheus rule files spell `for`. A bare number is read as seconds.

func (Duration) MarshalJSON

func (d Duration) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

func (Duration) Std

func (d Duration) Std() time.Duration

Std is the time.Duration.

func (*Duration) UnmarshalJSON

func (d *Duration) UnmarshalJSON(b []byte) error

UnmarshalJSON implements json.Unmarshaler.

type Evaluator

type Evaluator struct {
	// contains filtered or unexported fields
}

Evaluator runs rules one step at a time. It holds no timers; Scheduler drives it. Safe for concurrent use as long as one rule is never evaluated by two callers at once, which the Scheduler's lease guarantees.

func NewEvaluator

func NewEvaluator(d EvaluatorDeps) (*Evaluator, error)

NewEvaluator validates deps and builds an Evaluator.

func (*Evaluator) EffectiveInterval

func (e *Evaluator) EffectiveInterval(r Rule) time.Duration

EffectiveInterval is the interval a rule is evaluated at.

func (*Evaluator) EvaluateRule

func (e *Evaluator) EvaluateRule(ctx context.Context, rule Rule, prev []Instance) ([]Instance, error)

EvaluateRule runs one rule once. prev is the stored instances of this rule.

A rule that cannot be evaluated (a refused expr, a query error, an oversized or malformed result) changes nothing: its instances keep their state and any firing alert keeps being re-sent until it lapses on its own `endsAt`. Treating an error as "nothing returned" would resolve real incidents whenever the database hiccuped.

func (*Evaluator) Retire

func (e *Evaluator) Retire(ctx context.Context, key RuleKey, prev []Instance, interval time.Duration) ([]Instance, error)

Retire resolves the live instances of a rule that was removed.

func (*Evaluator) Select

func (e *Evaluator) Select(rules []Rule) (kept []Rule, over []Overflow)

Select applies the per-env rule cap to rules (which should all belong to one owner). Within an overflowing env the rules that stay are the most severe first (critical, error, warning, anything else), then by name, so the drop is deterministic and never takes a critical rule while a warning stays. Other envs are untouched. kept is ordered by rule key; every dropped rule is reported in over, and the caller must make that loud.

type EvaluatorDeps

type EvaluatorDeps struct {
	Querier  Querier
	Notifier Notifier
	Store    StateStore
	Limits   Limits
	Logger   *slog.Logger
	// Now is the clock; time.Now when nil.
	Now func() time.Time
}

EvaluatorDeps are the evaluator's collaborators.

type Event

type Event struct {
	RuleKey
	Fingerprint string
	Labels      map[string]string
	Transition  Transition
	Value       float64
	At          time.Time
	// Why is the payload the instance carried at this transition.
	Why json.RawMessage
}

Event is one history row.

type EventQuery

type EventQuery struct {
	Owner string
	// Env narrows to one env; empty means all of the owner's envs.
	Env string
	// Since is the oldest event wanted.
	Since time.Time
	// Limit defaults to 200 and is capped at 1000.
	Limit int
}

EventQuery selects history rows. Owner is required; the store never returns another owner's rows.

type FileRuleSource

type FileRuleSource struct {
	// Path of the rules document.
	Path string
	// Owner and Env key every rule. Owner may be any non-empty opaque
	// string; it only has to match what the Querier expects.
	Owner, Env string
	// Identity is stamped on every rule (see Rule.Identity).
	Identity map[string]string
	// Recheck is how often to stat the file. Default 5s.
	Recheck time.Duration
	Logger  *slog.Logger
	// contains filtered or unexported fields
}

FileRuleSource is a single-owner RuleSource that reads a rules document (RulesDocument, JSON) from a path, typically a mounted ConfigMap rendered by forge from the env's declared alerts. It is how a self-hosted, in-cluster evaluator gets its rules through the normal release path.

The file is re-stat'ed at most every Recheck and re-read only when its identity, size or mtime changed (a ConfigMap update swaps a symlink, which os.SameFile sees). A file that is missing, unreadable or invalid KEEPS the last good rules and warns, rate-limited: a broken edit must not silently delete every alert. Before the first good read there are no rules.

func (*FileRuleSource) Owners

func (f *FileRuleSource) Owners(context.Context) ([]string, error)

Owners implements RuleSource.

func (*FileRuleSource) Rules

func (f *FileRuleSource) Rules(_ context.Context, owner string) ([]Rule, error)

Rules implements RuleSource. An owner other than f.Owner has none.

type Instance

type Instance struct {
	RuleKey
	Fingerprint string
	// Labels are the final labels, identity included.
	Labels      map[string]string
	Annotations map[string]string
	State       State
	// ActiveAt is when the series was first seen in its current run.
	ActiveAt time.Time
	// FiringAt is when it became firing; zero while pending.
	FiringAt time.Time
	// ClearedAt is when a firing instance first stopped returning, while flap
	// damping holds it open; zero when it is returning.
	ClearedAt  time.Time
	ResolvedAt time.Time
	// ResolveSent records that the resolved notification was delivered.
	ResolveSent bool
	LastEvalAt  time.Time
	LastValue   float64
	// Why is the latest why-fired payload the rule's row carried (WhyColumn),
	// refreshed on every evaluation. It is not a label, so it never changes
	// the instance's fingerprint.
	Why json.RawMessage
}

Instance is one label set of one rule, with its state.

type InstanceRef

type InstanceRef struct {
	RuleKey
	Fingerprint string
}

InstanceRef addresses one stored instance.

type Limits

type Limits struct {
	// MinInterval is the floor on a rule's interval.
	MinInterval time.Duration
	// MaxExecutionTime is the per-query timeout, applied server-side and to the
	// call's context.
	MaxExecutionTime time.Duration
	// MaxInstancesPerRule: a result with more rows than this is an error, and the
	// rule's existing instances are left untouched for that evaluation.
	MaxInstancesPerRule int
	// MaxRulesPerEnv: rules past this count in ONE env are not evaluated (see
	// Select for which). The unit is the env, not the owner: an owner's envs
	// are independent deployments, and a cap over their sum let one env's
	// rules push another env's alerts out of evaluation.
	MaxRulesPerEnv int
}

Limits bound what one rule and one env may cost.

func DefaultLimits

func DefaultLimits() Limits

DefaultLimits are conservative.

type Locker

type Locker interface {
	Run(ctx context.Context, name string, fn func(ctx context.Context) error) error
}

Locker runs fn while holding a named, cluster-wide lease. forge/pkg/lease's *Locker satisfies it.

type MemoryStore

type MemoryStore struct {
	// contains filtered or unexported fields
}

MemoryStore is a StateStore that keeps everything in process: for tests and for a single-process deployment that accepts losing state on restart.

func NewMemoryStore

func NewMemoryStore() *MemoryStore

NewMemoryStore returns an empty store.

func (*MemoryStore) Apply

func (m *MemoryStore) Apply(_ context.Context, ch Changes) error

Apply implements StateStore.

func (*MemoryStore) Events

func (m *MemoryStore) Events(_ context.Context, q EventQuery) ([]Event, error)

Events implements StateStore. History is kept in memory without bound; a long-running process should use a persistent store.

func (*MemoryStore) LoadOwner

func (m *MemoryStore) LoadOwner(_ context.Context, owner string) ([]Instance, error)

LoadOwner implements StateStore.

func (*MemoryStore) Owners

func (m *MemoryStore) Owners(context.Context) ([]string, error)

Owners implements StateStore.

type Notifier

type Notifier interface {
	Notify(ctx context.Context, alerts []Alert) error
}

Notifier delivers alerts.

type Overflow added in v1.0.0

type Overflow struct {
	Env string
	// Configured is how many rules the env declares; Cap the limit it is over.
	Configured, Cap int
	// Dropped are the rules not evaluated, in the order they were dropped.
	Dropped []Rule
}

Overflow is one env whose declared rules exceed Limits.MaxRulesPerEnv: the rules in Dropped are NOT evaluated, so no alert can fire for them.

type Querier

type Querier interface {
	Query(ctx context.Context, owner, query string, opts QueryOptions) ([]Row, error)
}

Querier runs one guarded SELECT for an owner.

type QueryOptions

type QueryOptions struct {
	MaxExecutionTime time.Duration
	// MaxResultRows is one more than the instance cap, so exceeding the cap
	// surfaces as an error instead of a silently truncated result.
	MaxResultRows int
}

QueryOptions bound one evaluation. The Querier must apply them server-side.

func (QueryOptions) Limits

func (o QueryOptions) Limits() map[string]any

Limits are the per-query resource settings: a timeout and a row cap, both throwing on overflow rather than truncating.

A Querier that runs as an IMPERSONATED user (ClickHouse `EXECUTE AS`) sends only these. It must not send `readonly`: a readonly session refuses the EXECUTE AS statement itself, and the target user's settings profile (which should say readonly = 2 with MAX constraints) is what contains the query.

func (QueryOptions) Settings

func (o QueryOptions) Settings() map[string]any

Settings are Limits plus readonly=2 (no writes or DDL, while the per-query limits stay settable): what a Querier on its own, directly-authenticated connection sends.

type Row

type Row map[string]any

Row is one row a Querier returned: column name to value.

type Rule

type Rule struct {
	RuleKey

	// Expr is a single ClickHouse SELECT; see ValidateExpr.
	Expr string
	// For is how long a series must keep returning before it fires. Zero fires
	// on the first evaluation that returns it.
	For time.Duration
	// Interval is the evaluation period, raised to the scheduler's MinInterval.
	Interval time.Duration
	// KeepFiringFor holds a firing alert open this long after its series stops
	// returning (Prometheus's keep_firing_for). The hold is never shorter than
	// one evaluation, which is the flap damping every rule gets.
	KeepFiringFor time.Duration

	// Source is where the rule was declared.
	Source Source

	// Labels and Annotations are the rule author's. Labels whose name starts
	// with "reliant_" are dropped, and `alertname` is always the rule's name.
	Labels      map[string]string
	Annotations map[string]string

	// Identity is set by the RuleSource, never by the rule author, and is
	// applied after every other label: it is how a consumer routes an alert to
	// the owner it belongs to.
	Identity map[string]string
}

Rule is one alert rule, as declared.

type RuleKey

type RuleKey struct {
	Owner string
	Env   string
	Name  string
}

RuleKey identifies one rule. Owner and Env are opaque to this package.

func (RuleKey) String

func (k RuleKey) String() string

String is the key as a log field.

type RuleSet

type RuleSet []RuleSpec

RuleSet is a list of specs, the "alerts" section of a rules document.

func ParseRulesDocument

func ParseRulesDocument(b []byte) (RuleSet, error)

ParseRulesDocument decodes and validates a document.

func (RuleSet) Validate

func (rs RuleSet) Validate() error

Validate checks every spec and that names are unique.

type RuleSource

type RuleSource interface {
	// Owners lists every owner that has rules.
	Owners(ctx context.Context) ([]string, error)
	// Rules lists an owner's rules across all of its envs.
	Rules(ctx context.Context, owner string) ([]Rule, error)
}

RuleSource lists what to evaluate.

type RuleSpec

type RuleSpec struct {
	// Alert is the rule's name, unique within an env: becomes `alertname`.
	Alert string `json:"alert"`
	// Expr is a single ClickHouse SELECT; every row it returns is a firing
	// instance. See ValidateExpr and the package doc.
	Expr string `json:"expr"`
	// For is how long a series must keep returning before it fires.
	For Duration `json:"for,omitempty"`
	// Interval is the evaluation period; zero means the evaluator's floor.
	Interval Duration `json:"interval,omitempty"`
	// KeepFiringFor holds a firing alert open this long after it stops
	// returning, so a flapping series does not page on every recovery.
	KeepFiringFor Duration `json:"keep_firing_for,omitempty"`
	// Labels carry routing data (`severity` is a label). Names starting with
	// "reliant_" are reserved and dropped.
	Labels      map[string]string `json:"labels,omitempty"`
	Annotations map[string]string `json:"annotations,omitempty"`
	// Source is user or default; empty means user.
	Source Source `json:"source,omitempty"`
}

RuleSpec is one declared alert rule, in the Prometheus rule shape with a SQL `expr`. It is the unit a release's declared shape records and a rules file holds: no owner, no env and no identity, because those belong to WHERE the rule is deployed, not to what it says. A RuleSource turns a spec into a Rule with Rule.Identity set.

func (RuleSpec) Rule

func (s RuleSpec) Rule(owner, env string, identity map[string]string) Rule

Rule binds the spec to an owner and env, with the identity labels the RuleSource vouches for.

func (RuleSpec) Validate

func (s RuleSpec) Validate() error

Validate checks everything that can be checked without a database. A bad spec is refused at render/lint time instead of failing every evaluation.

type RulesDocument

type RulesDocument struct {
	Alerts RuleSet `json:"alerts"`
}

RulesDocument is the JSON document FileRuleSource reads: the alerts section of a release's declared shape, serialized. Other sections (routes, say) are ignored, so the same file can carry them.

type Scheduler

type Scheduler struct {
	// contains filtered or unexported fields
}

Scheduler evaluates every owner's rules on their intervals.

Owners hash into Shards partitions and each partition is a lease, so exactly one replica evaluates an owner at a time and a replica that dies hands its partitions over within the lease's retry interval. Every replica contends for every shard, each starting after a random delay so partitions spread across replicas rather than the fastest one taking all.

Within a shard each rule is due on its own interval, first offset by a jitter derived from its key so a restart does not fire every rule in the same second.

func NewScheduler

func NewScheduler(d SchedulerDeps) (*Scheduler, error)

NewScheduler validates deps.

func (*Scheduler) Run

func (s *Scheduler) Run(ctx context.Context) error

Run blocks until ctx is done.

func (*Scheduler) ShardOf

func (s *Scheduler) ShardOf(owner string) int

ShardOf is the partition an owner belongs to.

type SchedulerDeps

type SchedulerDeps struct {
	Rules RuleSource
	Store StateStore
	Eval  *Evaluator
	// Locker shards evaluation across replicas. Nil runs every shard in this
	// process, which is right for a single replica.
	Locker Locker
	// Shards is how many lease-owned partitions owners hash into. Default 16.
	Shards int
	// LeasePrefix names the leases: "<prefix>/shard/<n>". Default "alerting".
	LeasePrefix string
	// Tick is how often a shard looks for due rules. Default 10s.
	Tick time.Duration
	// Concurrency bounds owners evaluated at once within a shard. Default 4.
	Concurrency int
	Logger      *slog.Logger
	// Meter records the alerting.rules.* instruments. otel's global meter
	// provider when nil.
	Meter metric.Meter
	// Notifier receives the AlertRulesOverCap alert for an env that is over its
	// rule cap. Without one that alert is only logged and counted, so every
	// real deployment sets it (the Evaluator's own Notifier is the one to pass).
	Notifier Notifier
}

SchedulerDeps are the scheduler's collaborators.

type Source

type Source string

Source says where a declared rule came from.

const (
	// SourceUser: written by the project's author in its own deploy config.
	SourceUser Source = "user"
	// SourceDefault: generated by forge for every project; the author may
	// disable or override it.
	SourceDefault Source = "default"
)

type State

type State string

State is an instance's position in the state machine. An instance that is not stored is inactive.

const (
	StatePending  State = "pending"
	StateFiring   State = "firing"
	StateResolved State = "resolved"
)

type StateStore

type StateStore interface {
	// LoadOwner returns every stored instance of an owner, resolved included.
	LoadOwner(ctx context.Context, owner string) ([]Instance, error)
	// Owners lists owners that have stored instances.
	Owners(ctx context.Context) ([]string, error)
	// Apply persists one rule's changes atomically.
	Apply(ctx context.Context, ch Changes) error
	// Events reads the transition history, newest first.
	Events(ctx context.Context, q EventQuery) ([]Event, error)
}

StateStore keeps instance state and the transition history.

type StoreError

type StoreError struct {
	Rule RuleKey
	Err  error
}

StoreError is a failed write to the StateStore. The caller's in-memory copy of the rule's state may be ahead of what was stored and must be reloaded.

func (*StoreError) Error

func (e *StoreError) Error() string

func (*StoreError) Unwrap

func (e *StoreError) Unwrap() error

type Transition

type Transition string

Transition names a state change in the history.

const (
	TransitionPending  Transition = "pending"
	TransitionFiring   Transition = "firing"
	TransitionResolved Transition = "resolved"
	// TransitionInactive: a pending series stopped returning before `for` held.
	TransitionInactive Transition = "inactive"
	// TransitionRemoved: the rule was deleted while the instance was live.
	TransitionRemoved Transition = "removed"
)

type Why added in v1.0.0

type Why struct {
	// Detector is one of the Detector* names.
	Detector string `json:"detector"`
	// Signal is what was measured: error_rate, p95, throughput, issue_events,
	// release_health.
	Signal string `json:"signal"`
	// DedupeKey names what the alert is about, so a receiver that gets several
	// alerts for one cause (a bad deploy raises a regression, a spike and an
	// error-rate alert) can start one piece of work, not one per alert. Built
	// by DedupeKey.
	DedupeKey string `json:"dedupe_key,omitempty"`

	// Observed is the measured value in Unit; Baseline is what it is judged
	// against; Spread is the baseline's dispersion, when the method has one.
	// Nil means the detector does not have the figure, never zero.
	Observed *float64 `json:"observed,omitempty"`
	Baseline *float64 `json:"baseline,omitempty"`
	Spread   *float64 `json:"spread,omitempty"`
	// Unit names Observed, Baseline and Spread: "fraction", "ratio", "ms",
	// "events/hour".
	Unit string `json:"unit,omitempty"`
	// Method says how Baseline was found, in a line a person can check.
	Method string `json:"method,omitempty"`
	// Thresholds are the detector's own limits that Observed crossed, by name
	// ("factor": 5, "min_events": 20).
	Thresholds map[string]float64 `json:"thresholds,omitempty"`

	// Window is the span Observed covers ("15m", "1h-2h"); BaselineWindow the
	// span Baseline covers. Since is when the condition began.
	Window         string    `json:"window,omitempty"`
	BaselineWindow string    `json:"baseline_window,omitempty"`
	Since          time.Time `json:"since,omitzero"`
	// HistoryDays is how much history the baseline had to work with.
	HistoryDays int `json:"history_days,omitempty"`

	Context WhyContext `json:"context"`

	// Next are commands that lead from the alert to the evidence, runnable as
	// written ("forge obs check prod").
	Next []string `json:"next,omitempty"`
}

Why is the shared "why it fired" payload: what a detector observed, against what, over which window, what changed around it, and which commands lead from the alert to the evidence. Every detector fills the same shape, so a receiver (the in-app inbox, the Autopilot trigger, a Slack template) reads one document whatever fired.

It rides with the alert instance and its notification as the rule row's WhyColumn, NOT as labels. A label is part of an alert's identity: a number that moves between evaluations (the observed count) would make each evaluation a new alert and notify again, and an annotation template can quote only labels and the value. The payload is refreshed on every evaluation, stored beside the instance and its history, and posted as the WhyAnnotation annotation.

func ParseWhy added in v1.0.0

func ParseWhy(raw json.RawMessage) (Why, error)

ParseWhy reads a payload back. Unknown fields are ignored: a newer detector may write more than this reader knows.

func (Why) JSON added in v1.0.0

func (w Why) JSON() (json.RawMessage, error)

JSON is the payload as an instance carries it: compact, and bounded by MaxWhyBytes. The lists with a natural order of importance are cut from the tail until it fits; a payload that cannot be made to fit is an error.

type WhyActivity added in v1.0.0

type WhyActivity struct {
	At     time.Time `json:"at"`
	Actor  string    `json:"actor,omitempty"`
	Verb   string    `json:"verb"`
	Target string    `json:"target,omitempty"`
}

WhyActivity is one change in the window.

type WhyContext added in v1.0.0

type WhyContext struct {
	// Findings are the individual findings a detector judged: one per
	// service, issue or threshold that moved.
	Findings []WhyFinding `json:"findings,omitempty"`
	// Releases are the releases promoted in the window, with their commit range.
	Releases []WhyRelease `json:"releases_in_window,omitempty"`
	// Activity is the other changes in the window (a config change, a deploy).
	Activity []WhyActivity `json:"activity_in_window,omitempty"`
	// TopIssues are the issues most active in the window.
	TopIssues []WhyIssue `json:"top_issues,omitempty"`
	// ShiftedAttributes name attributes whose distribution shifted
	// ("build=3f9c: 92% vs 4% baseline").
	ShiftedAttributes []string `json:"shifted_attributes,omitempty"`
}

WhyContext is what was going on around the alert.

type WhyFinding added in v1.0.0

type WhyFinding struct {
	// Code is the detector's own name for it (a release-health finding code).
	Code    string   `json:"code"`
	Service string   `json:"service,omitempty"`
	Ref     string   `json:"ref,omitempty"`
	Value   float64  `json:"value"`
	Limit   float64  `json:"limit"`
	Before  *float64 `json:"before,omitempty"`
	After   *float64 `json:"after,omitempty"`
	// Policy says whether the finding counted ("fail") or only warned.
	Policy  string `json:"policy,omitempty"`
	Message string `json:"message,omitempty"`
}

WhyFinding is one thing a detector found.

type WhyIssue added in v1.0.0

type WhyIssue struct {
	ID      string `json:"id"`
	Service string `json:"service,omitempty"`
	Title   string `json:"title,omitempty"`
	// Events is the count over the alert's window.
	Events      int64     `json:"events"`
	FirstSeenAt time.Time `json:"first_seen_at,omitzero"`
	LastSeenAt  time.Time `json:"last_seen_at,omitzero"`
}

WhyIssue is an issue active in the window.

type WhyRelease added in v1.0.0

type WhyRelease struct {
	Version     string    `json:"version"`
	Previous    string    `json:"previous,omitempty"`
	PromotionID string    `json:"promotion_id,omitempty"`
	PromotedAt  time.Time `json:"promoted_at"`
	// Commit and PreviousCommit bound the change: PreviousCommit..Commit.
	Commit         string `json:"commit,omitempty"`
	PreviousCommit string `json:"previous_commit,omitempty"`
}

WhyRelease is a promotion in the window.

Directories

Path Synopsis
Package amconfig renders an Alertmanager configuration from stored receivers and routes.
Package amconfig renders an Alertmanager configuration from stored receivers and routes.
Package chquery is a single-owner alerting.Querier over one ClickHouse connection: the owner key is ignored and every rule runs against the connection's database.
Package chquery is a single-owner alerting.Querier over one ClickHouse connection: the owner key is ignored and every rule runs against the connection's database.
Package pgstore is the Postgres alerting.StateStore.
Package pgstore is the Postgres alerting.StateStore.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL