From 9f089e0784eee3b0aec817a82fa55c7411f8e8c0 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Wed, 12 Nov 2025 05:51:26 +0530 Subject: [PATCH] fix(pagerduty): add severity for labels (#9538) --- pkg/contextlinks/types.go | 2 +- pkg/query-service/app/parser.go | 3 +- pkg/query-service/rules/threshold_rule.go | 4 +-- pkg/types/ruletypes/constants.go | 13 ++++++-- pkg/types/ruletypes/threshold.go | 4 +++ pkg/types/ruletypes/threshold_test.go | 37 ++++++++++++++--------- 6 files changed, 42 insertions(+), 21 deletions(-) diff --git a/pkg/contextlinks/types.go b/pkg/contextlinks/types.go index 9edf92a5e5..516f7c0c06 100644 --- a/pkg/contextlinks/types.go +++ b/pkg/contextlinks/types.go @@ -42,4 +42,4 @@ type URLShareableOptions struct { SelectColumns []v3.AttributeKey `json:"selectColumns"` } -var PredefinedAlertLabels = []string{ruletypes.LabelThresholdName} +var PredefinedAlertLabels = []string{ruletypes.LabelThresholdName, ruletypes.LabelSeverityName, ruletypes.LabelLastSeen} diff --git a/pkg/query-service/app/parser.go b/pkg/query-service/app/parser.go index aa6a50a251..4480cc9897 100644 --- a/pkg/query-service/app/parser.go +++ b/pkg/query-service/app/parser.go @@ -6,7 +6,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/SigNoz/signoz/pkg/types/thirdpartyapitypes" "math" "net/http" "sort" @@ -15,6 +14,8 @@ import ( "text/template" "time" + "github.com/SigNoz/signoz/pkg/types/thirdpartyapitypes" + "github.com/SigNoz/govaluate" "github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/kafka" queues2 "github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/queues" diff --git a/pkg/query-service/rules/threshold_rule.go b/pkg/query-service/rules/threshold_rule.go index e881ba4fb1..2b873c4320 100644 --- a/pkg/query-service/rules/threshold_rule.go +++ b/pkg/query-service/rules/threshold_rule.go @@ -467,7 +467,7 @@ func (r *ThresholdRule) buildAndRunQuery(ctx context.Context, orgID valuer.UUID, r.logger.InfoContext(ctx, "no data found for rule condition", "rule_id", r.ID()) lbls := labels.NewBuilder(labels.Labels{}) if !r.lastTimestampWithDatapoints.IsZero() { - lbls.Set("lastSeen", r.lastTimestampWithDatapoints.Format(constants.AlertTimeFormat)) + lbls.Set(ruletypes.LabelLastSeen, r.lastTimestampWithDatapoints.Format(constants.AlertTimeFormat)) } resultVector = append(resultVector, ruletypes.Sample{ Metric: lbls.Labels(), @@ -544,7 +544,7 @@ func (r *ThresholdRule) buildAndRunQueryV5(ctx context.Context, orgID valuer.UUI r.logger.InfoContext(ctx, "no data found for rule condition", "rule_id", r.ID()) lbls := labels.NewBuilder(labels.Labels{}) if !r.lastTimestampWithDatapoints.IsZero() { - lbls.Set("lastSeen", r.lastTimestampWithDatapoints.Format(constants.AlertTimeFormat)) + lbls.Set(ruletypes.LabelLastSeen, r.lastTimestampWithDatapoints.Format(constants.AlertTimeFormat)) } resultVector = append(resultVector, ruletypes.Sample{ Metric: lbls.Labels(), diff --git a/pkg/types/ruletypes/constants.go b/pkg/types/ruletypes/constants.go index 43f97055a9..f7f9c126cb 100644 --- a/pkg/types/ruletypes/constants.go +++ b/pkg/types/ruletypes/constants.go @@ -1,5 +1,12 @@ package ruletypes -const CriticalThresholdName = "CRITICAL" -const LabelThresholdName = "threshold.name" -const LabelRuleId = "ruleId" +const ( + CriticalThresholdName = "critical" + ErrorThresholdName = "error" + WarningThresholdName = "warning" + InfoThresholdName = "info" + LabelThresholdName = "threshold.name" + LabelSeverityName = "severity" + LabelLastSeen = "lastSeen" + LabelRuleId = "ruleId" +) diff --git a/pkg/types/ruletypes/threshold.go b/pkg/types/ruletypes/threshold.go index 40641f11ba..598ba9b866 100644 --- a/pkg/types/ruletypes/threshold.go +++ b/pkg/types/ruletypes/threshold.go @@ -4,6 +4,7 @@ import ( "encoding/json" "math" "sort" + "strings" "github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/query-service/converter" @@ -198,7 +199,10 @@ func (b BasicRuleThreshold) shouldAlert(series v3.Series, ruleUnit string) (Samp target := b.target(ruleUnit) + // TODO(srikanthccv): is it better to move the logic to notifier instead of + // adding two labels? lbls = append(lbls, labels.Label{Name: LabelThresholdName, Value: b.Name}) + lbls = append(lbls, labels.Label{Name: LabelSeverityName, Value: strings.ToLower(b.Name)}) series.Points = removeGroupinSetPoints(series) diff --git a/pkg/types/ruletypes/threshold_test.go b/pkg/types/ruletypes/threshold_test.go index ff54a10421..cfa81eea13 100644 --- a/pkg/types/ruletypes/threshold_test.go +++ b/pkg/types/ruletypes/threshold_test.go @@ -1,6 +1,7 @@ package ruletypes import ( + "strings" "testing" "github.com/stretchr/testify/assert" @@ -21,7 +22,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) { { name: "milliseconds to seconds conversion - should alert", threshold: BasicRuleThreshold{ - Name: "test", + Name: CriticalThresholdName, TargetValue: &target, // 100ms TargetUnit: "ms", MatchType: AtleastOnce, @@ -39,7 +40,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) { { name: "milliseconds to seconds conversion - should not alert", threshold: BasicRuleThreshold{ - Name: "test", + Name: WarningThresholdName, TargetValue: &target, // 100ms TargetUnit: "ms", MatchType: AtleastOnce, @@ -57,7 +58,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) { { name: "seconds to milliseconds conversion - should alert", threshold: BasicRuleThreshold{ - Name: "test", + Name: CriticalThresholdName, TargetValue: &target, // 100s TargetUnit: "s", MatchType: AtleastOnce, @@ -76,7 +77,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) { { name: "bytes to kibibytes conversion - should alert", threshold: BasicRuleThreshold{ - Name: "test", + Name: InfoThresholdName, TargetValue: &target, // 100 bytes TargetUnit: "bytes", MatchType: AtleastOnce, @@ -94,7 +95,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) { { name: "kibibytes to mebibytes conversion - should alert", threshold: BasicRuleThreshold{ - Name: "test", + Name: ErrorThresholdName, TargetValue: &target, // 100KiB TargetUnit: "kbytes", MatchType: AtleastOnce, @@ -113,7 +114,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) { { name: "milliseconds to seconds with ValueIsBelow - should alert", threshold: BasicRuleThreshold{ - Name: "test", + Name: WarningThresholdName, TargetValue: &target, // 100ms TargetUnit: "ms", MatchType: AtleastOnce, @@ -131,7 +132,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) { { name: "milliseconds to seconds with OnAverage - should alert", threshold: BasicRuleThreshold{ - Name: "test", + Name: CriticalThresholdName, TargetValue: &target, // 100ms TargetUnit: "ms", MatchType: OnAverage, @@ -151,7 +152,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) { { name: "decimal megabytes to gigabytes with InTotal - should alert", threshold: BasicRuleThreshold{ - Name: "test", + Name: WarningThresholdName, TargetValue: &target, // 100MB TargetUnit: "decmbytes", MatchType: InTotal, @@ -171,7 +172,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) { { name: "milliseconds to seconds with AllTheTimes - should alert", threshold: BasicRuleThreshold{ - Name: "test", + Name: InfoThresholdName, TargetValue: &target, // 100ms TargetUnit: "ms", MatchType: AllTheTimes, @@ -191,7 +192,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) { { name: "kilobytes to megabytes with Last - should not alert", threshold: BasicRuleThreshold{ - Name: "test", + Name: ErrorThresholdName, TargetValue: &target, // 100kB TargetUnit: "deckbytes", MatchType: Last, @@ -211,7 +212,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) { { name: "bytes per second to kilobytes per second - should alert", threshold: BasicRuleThreshold{ - Name: "test", + Name: CriticalThresholdName, TargetValue: &target, // 100 bytes/s TargetUnit: "Bps", MatchType: AtleastOnce, @@ -230,7 +231,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) { { name: "same unit - no conversion needed - should alert", threshold: BasicRuleThreshold{ - Name: "test", + Name: InfoThresholdName, TargetValue: &target, // 100ms TargetUnit: "ms", MatchType: AtleastOnce, @@ -249,7 +250,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) { { name: "empty unit - no conversion - should alert", threshold: BasicRuleThreshold{ - Name: "test", + Name: ErrorThresholdName, TargetValue: &target, // 100 (unitless) TargetUnit: "", MatchType: AtleastOnce, @@ -280,12 +281,20 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) { hasThresholdLabel := false for _, label := range sample.Metric { - if label.Name == LabelThresholdName && label.Value == "test" { + if label.Name == LabelThresholdName && label.Value == tt.threshold.Name { hasThresholdLabel = true break } } assert.True(t, hasThresholdLabel) + hasSeverityLabel := false + for _, label := range sample.Metric { + if label.Name == LabelSeverityName && label.Value == strings.ToLower(tt.threshold.Name) { + hasSeverityLabel = true + break + } + } + assert.True(t, hasSeverityLabel) assert.Equal(t, *tt.threshold.TargetValue, sample.Target) assert.Equal(t, tt.threshold.TargetUnit, sample.TargetUnit) }