Data Model for Alerting Rules and Condition Evaluation in InfiniLabs Console
The alerting engine in InfiniLabs Console uses a JSON-driven Rule struct that combines resource targets, metric expressions, and prioritized conditions to evaluate Elasticsearch data via govaluate expressions.
The InfiniLabs Console repository (infinilabs/console) provides a comprehensive alerting subsystem designed to monitor Elasticsearch clusters. At its core lies a structured data model for alerting rules that supports both numeric thresholds and complex bucket-diff conditions. This article examines the Rule struct definition, condition evaluation logic, and the engine implementation that processes these rules against time-series data.
Alerting Rule Data Model
The Rule Struct
The foundation of the alerting system is the Rule struct defined in model/alerting/rule.go. This structure encapsulates all configuration necessary to define, schedule, and execute a monitoring rule:
- Identification and Metadata:
ID,Created,Updated,Name, andEnabledfields provide basic rule management. - Resource Targeting: The
Resourcefield (defined inmodel/alerting/resource.go) specifies the Elasticsearch indices, time field, and raw filters to query. - Metric Configuration: The
Metricsfield (frommodel/alerting/metric.go) defines aggregation functions, bucket sizes, and mathematical formulas. - Condition Logic:
Conditionsand optionalBucketConditions(frommodel/alerting/condition.go) establish the logical thresholds that trigger alerts. - Notification Settings:
Channels,NotificationConfig, andRecoveryNotificationConfigcontrol webhook, email, and escalation behaviors. - Scheduling: The
Schedulefield (frommodel/alerting/schedule.go) provides cron-like execution timing. - Runtime State: Fields like
LastNotificationTime,LastEscalationTime, andLastTermStartTimemanage throttling and stateful tracking.
The struct also provides Rule.GetOrInitExpression, which generates a full evaluable expression combining metric formulas with condition expressions.
Condition Definitions
Conditions are defined in model/alerting/condition.go through two primary structures:
Condition acts as a container with:
Operator: Currently supports "or" logic between itemsItems: A slice ofConditionItemstructs
ConditionItem represents individual threshold checks with:
Operator: Comparison operators (equals,gte,lt,gt,lte,neq)Values: Threshold values as stringsMinimumPeriodMatch: Number of consecutive data points that must satisfy the conditionPriority: Severity level (affects evaluation order)TypeandBucketCount: Special fields for bucket-diff conditions
The ConditionItem.GenerateConditionExpression method produces evaluable expressions using the placeholder variable result. For example, a "gte" operator generates:
conditionExpression = fmt.Sprintf("result >= %v", cond.Values[0])
Metric Expressions
The Metric struct in model/alerting/metric.go defines how to extract and compute values from Elasticsearch:
Items: List ofMetricItemstructs specifying fields and aggregations (max, avg, percentile, etc.)BucketSize: Time interval for aggregationFormula: Mathematical expression combining metric items (e.g.,"a + b"or"result")
The Metric.GenerateExpression method constructs the evaluable formula used by the engine.
How Conditions Are Evaluated in the Alerting Engine
The evaluation logic resides in service/alerting/elasticsearch/engine.go. The engine processes rules through a pipeline that prepares metric data, evaluates conditions by priority, and manages stateful notifications.
Data Preparation and Metric Calculation
The engine.GetTargetMetricData method orchestrates the initial data retrieval:
- Query Generation: Constructs Elasticsearch queries using
GenerateQuerybased on the rule's resource and metric definitions. - Data Retrieval: Executes the query against the target cluster.
- Formula Evaluation: For rules with multiple metric items, evaluates the user-supplied formula using
govaluate.NewEvaluableExpression. The result is a slice ofinsight.MetricDatawhere computed values are stored under the key"result".
This preparation ensures that subsequent condition evaluation works against normalized result values regardless of the underlying metric complexity.
Bucket Condition Evaluation
For rules requiring stateful bucket analysis (such as detecting content changes or document count anomalies), the engine uses CheckBucketCondition (lines 600-740 in engine.go):
- Timestamp Collation: Gathers all unique bucket timestamps across metric groups.
- Diff State Calculation: Computes either
ContentChangeState(for content-based rules) orDocCount(for volume-based rules) for each timestamp. - Expression Evaluation: Evaluates bucket-condition expressions using
govaluateagainst the computed diff values. - Consecutive Period Matching: Tracks how many consecutive buckets satisfy the condition. When the count reaches
MinimumPeriodMatch, the condition triggers.
This approach supports complex scenarios like "alert if index content changes for 3 consecutive minutes" or "alert if document count drops for 2 consecutive buckets."
Standard Numeric Condition Checking
When no bucket conditions exist, CheckCondition (lines 870-990 in engine.go) handles numeric threshold evaluation:
Priority-Based Ordering: Conditions are sorted by priority weight to ensure critical alerts evaluate first:
sort.Slice(rule.Conditions.Items, func(i, j int) bool {
return alerting.PriorityWeights[rule.Conditions.Items[i].Priority] >
alerting.PriorityWeights[rule.Conditions.Items[j].Priority]
})
Per-Series Evaluation: For each metric data series in targetMetricData:
- Determines the shortest series length and identifies the
dataKeycontaining result values. - Iterates through each data point index
i. - Builds a
relationValuesmap containing raw values of all metric items for that timestamp. - Evaluates the metric formula using these values to produce
valueExpressionResult. - Applies the condition expression (generated from
ConditionItem) tovalueExpressionResultusinggovaluate. - Maintains a
triggerCountfor consecutive matches. WhentriggerCountreachescond.MinimumPeriodMatch, the condition is marked triggered and aConditionResultItemis recorded. - Stops checking further conditions for that series once a condition fires (breaks the
LoopCondition).
This design ensures efficient evaluation where high-priority conditions short-circuit lower-priority checks, and multi-period requirements prevent flapping alerts from single-point anomalies.
Notification and Escalation Handling
After condition evaluation completes, the Do method (lines 530-730 in engine.go) manages the alert lifecycle:
- Alert Creation: Creates or updates
Alertrecords based onConditionResult. - Throttling: Checks
LastNotificationTimeagainst notification configuration to prevent spam. - Escalation: Tracks
LastEscalationTimeto escalate unacknowledged alerts after configured periods. - Recovery Detection: When conditions no longer trigger, sends recovery notifications using
RecoveryNotificationConfig. - Channel Dispatch: Routes messages through configured channels (webhook, email) using template resolution from
service/alerting/common.
Practical Implementation Examples
Defining and Loading a Rule
The following JSON structure represents a complete alerting rule configuration targeting high CPU usage:
{
"name": "CPU usage high",
"enabled": true,
"resource": {
"id": "es-cluster-1",
"objects": ["metrics*"],
"time_field": "@timestamp"
},
"metrics": {
"items": [
{"name":"cpu","field":"system.cpu.total.pct","statistic":"avg"}
],
"bucket_size":"1m",
"formula":"result"
},
"conditions": {
"items": [
{"operator":"gte","values":["0.9"],"minimum_period_match":3,"priority":"high"}
]
},
"notification_config": {
"enabled": true,
"title":"{{ .rule_name }} triggered",
"message":"CPU > 90% for {{ .condition_params[0].threshold }} ({{ .priority }})"
}
}
Loading and evaluating this rule in Go:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"infini.sh/console/model/alerting"
alerting2 "infini.sh/console/service/alerting"
_ "infini.sh/console/service/alerting/elasticsearch"
)
func main() {
data, err := ioutil.ReadFile("rule.json")
if err != nil {
panic(err)
}
var rule alerting.Rule
if err := json.Unmarshal(data, &rule); err != nil {
panic(err)
}
// Retrieve the Elasticsearch engine
eng := alerting2.GetEngine("elasticsearch")
// Execute condition check
result, err := eng.CheckCondition(&rule)
if err != nil {
panic(err)
}
fmt.Printf("Condition triggered: %v\n", len(result.ResultItems) > 0)
fmt.Printf("Result items count: %d\n", len(result.ResultItems))
}
Triggering a Full Alerting Task
For production deployments, rules run as scheduled tasks. The engine provides GenerateTask to create executable task functions:
eng := alerting2.GetEngine("elasticsearch")
// Generate a task function for the rule
task := eng.GenerateTask(&rule)
// Execute with context for timeout control
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
task(ctx) // Performs full lifecycle: Do() → creates Alert, sends notifications, updates KV state
This approach handles the complete alerting lifecycle including throttling, escalation, and recovery detection defined in service/alerting/elasticsearch/engine.go.
Summary
-
Rule Structure: The
Rulestruct inmodel/alerting/rule.godefines a comprehensive data model for alerting rules, encompassing resources, metrics, conditions, schedules, and notification configurations. -
Condition Types: Conditions support both standard numeric thresholds (
ConditionItemwith operators likegte,lt) and specialized bucket-diff conditions for detecting content or volume changes across time buckets. -
Evaluation Engine: The Elasticsearch engine in
service/alerting/elasticsearch/engine.goevaluates rules by preparing metric data viaGetTargetMetricData, then processing conditions throughCheckBucketConditionorCheckConditiondepending on rule type. -
Priority and Throttling: Conditions are evaluated by priority weight, with high-priority conditions short-circuiting lower ones. The
Domethod manages alert lifecycle, throttling, escalation, and recovery notifications. -
Expression Evaluation: Both metric formulas and condition expressions use the
govaluatelibrary, with metric results stored under the"result"key and condition expressions generated viaConditionItem.GenerateConditionExpression.
Frequently Asked Questions
How does the alerting engine handle consecutive period requirements?
The engine tracks consecutive matches using a triggerCount variable during condition evaluation in CheckCondition. When evaluating each data point, if the condition expression evaluates to true, the counter increments; otherwise, it resets to zero. The condition only fires when triggerCount reaches the MinimumPeriodMatch value defined in the ConditionItem struct, preventing alerts from single-point anomalies.
What is the difference between standard conditions and bucket conditions?
Standard conditions evaluate numeric thresholds against calculated metric values (like CPU > 90%), processed by the CheckCondition method. Bucket conditions, handled by CheckBucketCondition, monitor state changes across Elasticsearch buckets—such as content changes (ContentChangeState) or document count variations (DocCount)—requiring consecutive bucket matches to trigger. Bucket conditions are stored in the optional BucketConditions field of the Rule struct.
How are condition priorities enforced during evaluation?
Conditions are sorted by priority weight before evaluation begins in CheckCondition. The engine uses sort.Slice with PriorityWeights to arrange ConditionItem instances from highest to lowest priority. During the evaluation loop, once any condition triggers for a specific data series, the engine breaks out of the condition loop immediately, ensuring lower-priority conditions never execute for that series when a higher-priority one has already fired.
Where does the alerting engine store runtime state for throttling?
Runtime state fields including LastNotificationTime, LastEscalationTime, and LastTermStartTime are defined directly in the Rule struct in model/alerting/rule.go. The Do method in service/alerting/elasticsearch/engine.go reads and updates these timestamps to enforce notification throttling, manage escalation timeouts, and track recovery periods. These fields persist alongside the rule configuration, enabling stateful alerting across scheduled execution cycles.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →