feat: add ability to configure number of required points (#5242)

This commit is contained in:
Srikanth Chekuri 2024-10-03 16:48:32 +05:30 committed by GitHub
parent a98c8db949
commit 4f76e13dbe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 128 additions and 10 deletions

View File

@ -118,6 +118,8 @@
"exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data.", "exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data.",
"field_unit": "Threshold unit", "field_unit": "Threshold unit",
"text_alert_on_absent": "Send a notification if data is missing for", "text_alert_on_absent": "Send a notification if data is missing for",
"text_require_min_points": "Run alert evaluation only when there are minimum of",
"text_num_points": "data points in each result group",
"text_alert_frequency": "Run alert every", "text_alert_frequency": "Run alert every",
"text_for": "minutes", "text_for": "minutes",
"selected_query_placeholder": "Select query" "selected_query_placeholder": "Select query"

View File

@ -118,6 +118,8 @@
"exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data.", "exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data.",
"field_unit": "Threshold unit", "field_unit": "Threshold unit",
"text_alert_on_absent": "Send a notification if data is missing for", "text_alert_on_absent": "Send a notification if data is missing for",
"text_require_min_points": "Run alert evaluation only when there are minimum of",
"text_num_points": "data points in each result group",
"text_alert_frequency": "Run alert every", "text_alert_frequency": "Run alert every",
"text_for": "minutes", "text_for": "minutes",
"selected_query_placeholder": "Select query" "selected_query_placeholder": "Select query"

View File

@ -323,6 +323,45 @@ function RuleOptions({
<Typography.Text>{t('text_for')}</Typography.Text> <Typography.Text>{t('text_for')}</Typography.Text>
</Space> </Space>
</VerticalLine> </VerticalLine>
<VerticalLine>
<Space direction="horizontal" align="center">
<Form.Item noStyle name={['condition', 'requireMinPoints']}>
<Checkbox
checked={alertDef?.condition?.requireMinPoints}
onChange={(e): void => {
setAlertDef({
...alertDef,
condition: {
...alertDef.condition,
requireMinPoints: e.target.checked,
},
});
}}
/>
</Form.Item>
<Typography.Text>{t('text_require_min_points')}</Typography.Text>
<Form.Item noStyle name={['condition', 'requiredNumPoints']}>
<InputNumber
min={1}
value={alertDef?.condition?.requiredNumPoints}
onChange={(value): void => {
setAlertDef({
...alertDef,
condition: {
...alertDef.condition,
requiredNumPoints: Number(value) || 0,
},
});
}}
type="number"
onWheel={(e): void => e.currentTarget.blur()}
/>
</Form.Item>
<Typography.Text>{t('text_num_points')}</Typography.Text>
</Space>
</VerticalLine>
</Space> </Space>
</Collapse.Panel> </Collapse.Panel>
</Collapse> </Collapse>

View File

@ -37,6 +37,8 @@ export interface RuleCondition {
selectedQueryName?: string; selectedQueryName?: string;
alertOnAbsent?: boolean | undefined; alertOnAbsent?: boolean | undefined;
absentFor?: number | undefined; absentFor?: number | undefined;
requireMinPoints?: boolean | undefined;
requiredNumPoints?: number | undefined;
} }
export interface Labels { export interface Labels {
[key: string]: string; [key: string]: string;

View File

@ -116,6 +116,8 @@ type RuleCondition struct {
Algorithm string `json:"algorithm,omitempty"` Algorithm string `json:"algorithm,omitempty"`
Seasonality string `json:"seasonality,omitempty"` Seasonality string `json:"seasonality,omitempty"`
SelectedQuery string `json:"selectedQueryName,omitempty"` SelectedQuery string `json:"selectedQueryName,omitempty"`
RequireMinPoints bool `yaml:"requireMinPoints,omitempty" json:"requireMinPoints,omitempty"`
RequiredNumPoints int `yaml:"requiredNumPoints,omitempty" json:"requiredNumPoints,omitempty"`
} }
func (rc *RuleCondition) GetSelectedQueryName() string { func (rc *RuleCondition) GetSelectedQueryName() string {

View File

@ -353,6 +353,13 @@ func (r *BaseRule) ShouldAlert(series v3.Series) (Sample, bool) {
return alertSmpl, false return alertSmpl, false
} }
if r.ruleCondition.RequireMinPoints {
if len(series.Points) < r.ruleCondition.RequiredNumPoints {
zap.L().Info("not enough data points to evaluate series, skipping", zap.String("ruleid", r.ID()), zap.Int("numPoints", len(series.Points)), zap.Int("requiredPoints", r.ruleCondition.RequiredNumPoints))
return alertSmpl, false
}
}
switch r.matchType() { switch r.matchType() {
case AtleastOnce: case AtleastOnce:
// If any sample matches the condition, the rule is firing. // If any sample matches the condition, the rule is firing.

View File

@ -0,0 +1,64 @@
package rules
import (
"testing"
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
)
func TestBaseRule_RequireMinPoints(t *testing.T) {
threshold := 1.0
tests := []struct {
name string
rule *BaseRule
shouldAlert bool
series *v3.Series
}{
{
name: "test should skip if less than min points",
rule: &BaseRule{
ruleCondition: &RuleCondition{
RequireMinPoints: true,
RequiredNumPoints: 4,
},
},
series: &v3.Series{
Points: []v3.Point{
{Value: 1},
{Value: 2},
},
},
shouldAlert: false,
},
{
name: "test should alert if more than min points",
rule: &BaseRule{
ruleCondition: &RuleCondition{
RequireMinPoints: true,
RequiredNumPoints: 4,
CompareOp: ValueIsAbove,
MatchType: AtleastOnce,
Target: &threshold,
},
},
series: &v3.Series{
Points: []v3.Point{
{Value: 1},
{Value: 2},
{Value: 3},
{Value: 4},
},
},
shouldAlert: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
_, shouldAlert := test.rule.ShouldAlert(*test.series)
if shouldAlert != test.shouldAlert {
t.Errorf("expected shouldAlert to be %v, got %v", test.shouldAlert, shouldAlert)
}
})
}
}