mirror of
https://github.com/hanzoai/o11y.git
synced 2026-10-15 12:58:33 +00:00
Re-sync the entire SigNoz-derived Go tree to a clean fork of latest SigNoz main (upstream 3e6339019), eliminating the version-skew that broke `go build ./...` (mixed-era packages, ~14 imported-but-absent packages, partial signoz->o11y / clickhouse->datastore rebrands, self-import cycle, duplicate package decls). - pkg/ + cmd/ wholesale replaced with latest SigNoz; imports rewritten github.com/SigNoz/signoz/ -> github.com/hanzoai/o11y/. - Adopt SigNoz's CURRENT structure (resourcefilter->telemetryresourcefilter, integrationtypes->cloudintegrationtypes, roletypes/grammar/times folded in upstream); old paths dropped. - Fork deps repathed to hanzoai + bumped: signoz-otel-collector v0.144.6, clickhouse-go-mock v0.14.1, govaluate v0.1.0; expr-lang/expr => hanzoai/expr v1.17.8 (replace). go.mod reconciled from latest SigNoz; go.sum re-tidied. - ee/ + cmd/enterprise stripped — o11y stays MIT-only; community edition imports no ee packages. - Preserved o11y additions: module path github.com/hanzoai/o11y, mount.go (cloud.Register/zip mount adapter, HIP-0106), NOTICE + LICENSE (SigNoz §4c attribution retained; upstream pkg files are headerless per SigNoz convention). - Reverted o11y cosmetic renames (o11y*/datastore*) and experimental originals (authz/iamauthz, billing, zapreceiver, zapmetricreceiver) to SigNoz canonical to kill the skew; snapshots retained in git history for re-layering. go build ./... = exit 0. go vet clean except benign "unreachable code" in generated ANTLR parser files (inherited from upstream).
229 lines
6.9 KiB
Go
229 lines
6.9 KiB
Go
package rules
|
|
|
|
import (
|
|
"math"
|
|
"time"
|
|
|
|
"github.com/hanzoai/o11y/pkg/types/metrictypes"
|
|
qbtypes "github.com/hanzoai/o11y/pkg/types/querybuildertypes/querybuildertypesv5"
|
|
ruletypes "github.com/hanzoai/o11y/pkg/types/ruletypes"
|
|
"github.com/hanzoai/o11y/pkg/types/telemetrytypes"
|
|
"github.com/hanzoai/o11y/pkg/valuer"
|
|
)
|
|
|
|
// ThresholdRuleTestCase defines test case structure for threshold rule test notifications
|
|
type ThresholdRuleTestCase struct {
|
|
Name string
|
|
Values [][]any
|
|
ExpectAlerts int
|
|
ExpectValue float64
|
|
}
|
|
|
|
// PromRuleTestValue represents a single value point in a PromQL rule test case
|
|
type PromRuleTestValue struct {
|
|
Offset time.Duration // offset from baseTime (negative = in the past)
|
|
Value float64
|
|
}
|
|
|
|
// PromRuleTestCase defines test case structure for PromQL rule test notifications
|
|
type PromRuleTestCase struct {
|
|
Name string
|
|
Values []PromRuleTestValue
|
|
ExpectAlerts int
|
|
ExpectValue float64
|
|
}
|
|
|
|
// ThresholdRuleAtLeastOnceValueAbove creates a PostableRule for threshold rule test notifications
|
|
func ThresholdRuleAtLeastOnceValueAbove(target float64, recovery *float64) ruletypes.PostableRule {
|
|
return ruletypes.PostableRule{
|
|
AlertName: "test-alert",
|
|
AlertType: ruletypes.AlertTypeMetric,
|
|
RuleType: ruletypes.RuleTypeThreshold,
|
|
Evaluation: &ruletypes.EvaluationEnvelope{Kind: ruletypes.RollingEvaluation, Spec: ruletypes.RollingWindow{
|
|
EvalWindow: valuer.MustParseTextDuration("5m"),
|
|
Frequency: valuer.MustParseTextDuration("1m"),
|
|
}},
|
|
Labels: map[string]string{
|
|
"service.name": "frontend",
|
|
},
|
|
Annotations: map[string]string{
|
|
"value": "{{$value}}",
|
|
},
|
|
Version: "v5",
|
|
RuleCondition: &ruletypes.RuleCondition{
|
|
MatchType: ruletypes.AtleastOnce,
|
|
CompareOperator: ruletypes.ValueIsAbove,
|
|
Target: &target,
|
|
CompositeQuery: &ruletypes.AlertCompositeQuery{
|
|
QueryType: ruletypes.QueryTypeBuilder,
|
|
Queries: []qbtypes.QueryEnvelope{
|
|
{
|
|
Type: qbtypes.QueryTypeBuilder,
|
|
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
|
|
Name: "A",
|
|
StepInterval: qbtypes.Step{Duration: 60 * time.Second},
|
|
Signal: telemetrytypes.SignalMetrics,
|
|
|
|
Aggregations: []qbtypes.MetricAggregation{
|
|
{
|
|
MetricName: "probe_success",
|
|
TimeAggregation: metrictypes.TimeAggregationAvg,
|
|
SpaceAggregation: metrictypes.SpaceAggregationAvg,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Thresholds: &ruletypes.RuleThresholdData{
|
|
Kind: ruletypes.BasicThresholdKind,
|
|
Spec: ruletypes.BasicRuleThresholds{
|
|
{
|
|
Name: "primary",
|
|
TargetValue: &target,
|
|
RecoveryTarget: recovery,
|
|
MatchType: ruletypes.AtleastOnce,
|
|
CompareOperator: ruletypes.ValueIsAbove,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
NotificationSettings: &ruletypes.NotificationSettings{},
|
|
}
|
|
}
|
|
|
|
// BuildPromAtLeastOnceValueAbove creates a PostableRule for PromQL rule test notifications
|
|
func BuildPromAtLeastOnceValueAbove(target float64, recovery *float64) ruletypes.PostableRule {
|
|
return ruletypes.PostableRule{
|
|
AlertName: "test-prom-alert",
|
|
AlertType: ruletypes.AlertTypeMetric,
|
|
RuleType: ruletypes.RuleTypeProm,
|
|
Evaluation: &ruletypes.EvaluationEnvelope{Kind: ruletypes.RollingEvaluation, Spec: ruletypes.RollingWindow{
|
|
EvalWindow: valuer.MustParseTextDuration("5m"),
|
|
Frequency: valuer.MustParseTextDuration("1m"),
|
|
}},
|
|
Labels: map[string]string{
|
|
"service.name": "frontend",
|
|
},
|
|
Annotations: map[string]string{
|
|
"value": "{{$value}}",
|
|
},
|
|
Version: "v5",
|
|
RuleCondition: &ruletypes.RuleCondition{
|
|
MatchType: ruletypes.AtleastOnce,
|
|
SelectedQuery: "A",
|
|
CompareOperator: ruletypes.ValueIsAbove,
|
|
Target: &target,
|
|
CompositeQuery: &ruletypes.AlertCompositeQuery{
|
|
QueryType: ruletypes.QueryTypePromQL,
|
|
PanelType: ruletypes.PanelTypeGraph,
|
|
Queries: []qbtypes.QueryEnvelope{
|
|
{
|
|
Type: qbtypes.QueryTypePromQL,
|
|
Spec: qbtypes.PromQuery{
|
|
Name: "A",
|
|
Query: "{\"test_metric\"}",
|
|
Disabled: false,
|
|
Stats: false,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Thresholds: &ruletypes.RuleThresholdData{
|
|
Kind: ruletypes.BasicThresholdKind,
|
|
Spec: ruletypes.BasicRuleThresholds{
|
|
{
|
|
Name: "primary",
|
|
TargetValue: &target,
|
|
RecoveryTarget: recovery,
|
|
MatchType: ruletypes.AtleastOnce,
|
|
CompareOperator: ruletypes.ValueIsAbove,
|
|
Channels: []string{"slack"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
NotificationSettings: &ruletypes.NotificationSettings{},
|
|
}
|
|
}
|
|
|
|
var (
|
|
// TcTestNotiSendUnmatchedThresholdRule contains test cases for threshold rule test notifications
|
|
TcTestNotiSendUnmatchedThresholdRule = []ThresholdRuleTestCase{
|
|
{
|
|
Name: "return first valid point in case of test notification",
|
|
Values: [][]any{
|
|
{float64(3), "attr", time.Now()},
|
|
{float64(4), "attr", time.Now().Add(1 * time.Minute)},
|
|
},
|
|
ExpectAlerts: 1,
|
|
ExpectValue: 3,
|
|
},
|
|
{
|
|
Name: "No data in DB so no alerts fired",
|
|
Values: [][]any{},
|
|
ExpectAlerts: 0,
|
|
},
|
|
{
|
|
Name: "return first valid point in case of test notification skips NaN and Inf",
|
|
Values: [][]any{
|
|
{math.NaN(), "attr", time.Now()},
|
|
{math.Inf(1), "attr", time.Now().Add(1 * time.Minute)},
|
|
{float64(7), "attr", time.Now().Add(2 * time.Minute)},
|
|
},
|
|
ExpectAlerts: 1,
|
|
ExpectValue: 7,
|
|
},
|
|
{
|
|
Name: "If found matching alert with given target value, return the alerting value rather than first valid point",
|
|
Values: [][]any{
|
|
{float64(1), "attr", time.Now()},
|
|
{float64(2), "attr", time.Now().Add(1 * time.Minute)},
|
|
{float64(3), "attr", time.Now().Add(2 * time.Minute)},
|
|
{float64(12), "attr", time.Now().Add(3 * time.Minute)},
|
|
},
|
|
ExpectAlerts: 1,
|
|
ExpectValue: 12,
|
|
},
|
|
}
|
|
|
|
// TcTestNotificationSendUnmatchedPromRule contains test cases for PromQL rule test notifications
|
|
TcTestNotificationSendUnmatchedPromRule = []PromRuleTestCase{
|
|
{
|
|
Name: "return first valid point in case of test notification",
|
|
Values: []PromRuleTestValue{
|
|
{Offset: -4 * time.Minute, Value: 3},
|
|
{Offset: -3 * time.Minute, Value: 4},
|
|
},
|
|
ExpectAlerts: 1,
|
|
ExpectValue: 3,
|
|
},
|
|
{
|
|
Name: "No data in DB so no alerts fired",
|
|
Values: []PromRuleTestValue{},
|
|
ExpectAlerts: 0,
|
|
},
|
|
{
|
|
Name: "return first valid point in case of test notification skips NaN and Inf",
|
|
Values: []PromRuleTestValue{
|
|
{Offset: -4 * time.Minute, Value: math.NaN()},
|
|
{Offset: -3 * time.Minute, Value: math.Inf(1)},
|
|
{Offset: -2 * time.Minute, Value: 7},
|
|
},
|
|
ExpectAlerts: 1,
|
|
ExpectValue: 7,
|
|
},
|
|
{
|
|
Name: "If found matching alert with given target value, return the alerting value rather than first valid point",
|
|
Values: []PromRuleTestValue{
|
|
{Offset: -4 * time.Minute, Value: 1},
|
|
{Offset: -3 * time.Minute, Value: 2},
|
|
{Offset: -2 * time.Minute, Value: 3},
|
|
{Offset: -1 * time.Minute, Value: 12},
|
|
},
|
|
ExpectAlerts: 1,
|
|
ExpectValue: 12,
|
|
},
|
|
}
|
|
)
|