Monitoring System 0.1.0
System resource monitoring with pluggable collectors and alerting
Loading...
Searching...
No Matches
alert_triggers_example.cpp File Reference

Comprehensive example demonstrating various alert trigger types. More...

#include <chrono>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <random>
#include <thread>
#include <vector>
#include "kcenon/monitoring/alert/alert_triggers.h"
Include dependency graph for alert_triggers_example.cpp:

Go to the source code of this file.

Classes

class  periodic_trigger
 
class  moving_average_trigger
 

Functions

void print_eval_result (const std::string &trigger_name, double value, bool triggered, const std::string &description="")
 
int main ()
 

Detailed Description

Comprehensive example demonstrating various alert trigger types.

Definition in file alert_triggers_example.cpp.

Function Documentation

◆ main()

int main ( )

Definition at line 131 of file alert_triggers_example.cpp.

131 {
132 std::cout << "=== Alert Triggers Example ===" << std::endl;
133 std::cout << std::endl;
134
135 // =========================================================================
136 // Section 1: ThresholdTrigger - Basic Comparisons
137 // =========================================================================
138 std::cout << "1. Threshold Triggers" << std::endl;
139 std::cout << " -------------------" << std::endl;
140
141 // Create various threshold triggers using factory methods
142 auto above_80 = threshold_trigger::above(80.0);
143 auto above_or_equal_90 = threshold_trigger::above_or_equal(90.0);
144 auto below_20 = threshold_trigger::below(20.0);
145 auto below_or_equal_10 = threshold_trigger::below_or_equal(10.0);
146
147 // Using constructor directly with comparison operators
148 auto equal_50 = std::make_shared<threshold_trigger>(
149 50.0, comparison_operator::equal, 0.5); // epsilon = 0.5
150 auto not_equal_100 = std::make_shared<threshold_trigger>(
151 100.0, comparison_operator::not_equal);
152
153 std::cout << " Testing threshold triggers with various values:" << std::endl;
154 std::cout << std::endl;
155
156 std::vector<double> test_values = {5.0, 10.0, 20.0, 50.0, 50.3, 80.0, 85.0, 90.0, 100.0};
157
158 for (double val : test_values) {
159 std::cout << " Value: " << val << std::endl;
160 print_eval_result("above(80)", val, above_80->evaluate(val), above_80->description());
161 print_eval_result("above_or_equal(90)", val, above_or_equal_90->evaluate(val));
162 print_eval_result("below(20)", val, below_20->evaluate(val));
163 print_eval_result("below_or_equal(10)", val, below_or_equal_10->evaluate(val));
164 print_eval_result("equal(50, eps=0.5)", val, equal_50->evaluate(val));
165 print_eval_result("not_equal(100)", val, not_equal_100->evaluate(val));
166 std::cout << std::endl;
167 }
168
169 // =========================================================================
170 // Section 2: RangeTrigger - In/Out of Range
171 // =========================================================================
172 std::cout << "2. Range Triggers" << std::endl;
173 std::cout << " ---------------" << std::endl;
174
175 auto in_range_40_60 = threshold_trigger::in_range(40.0, 60.0);
176 auto out_of_range_40_60 = threshold_trigger::out_of_range(40.0, 60.0);
177
178 std::cout << " Range triggers test [40, 60]:" << std::endl;
179 std::cout << std::endl;
180
181 std::vector<double> range_values = {30.0, 40.0, 50.0, 60.0, 70.0};
182 for (double val : range_values) {
183 print_eval_result("in_range(40,60)", val, in_range_40_60->evaluate(val),
184 in_range_40_60->description());
185 print_eval_result("out_of_range(40,60)", val, out_of_range_40_60->evaluate(val),
186 out_of_range_40_60->description());
187 std::cout << std::endl;
188 }
189
190 // =========================================================================
191 // Section 3: RateOfChangeTrigger - Trend Detection
192 // =========================================================================
193 std::cout << "3. Rate of Change Trigger" << std::endl;
194 std::cout << " -----------------------" << std::endl;
195
196 // Trigger when value increases by more than 10 per 500ms window
197 auto rate_increasing = std::make_shared<rate_of_change_trigger>(
198 10.0, // rate threshold
199 500ms, // time window
200 rate_of_change_trigger::rate_direction::increasing, // direction
201 3 // minimum samples
202 );
203
204 // Trigger on rapid decrease
205 auto rate_decreasing = std::make_shared<rate_of_change_trigger>(
206 5.0,
207 500ms,
208 rate_of_change_trigger::rate_direction::decreasing,
209 3
210 );
211
212 // Trigger on any rapid change
213 auto rate_either = std::make_shared<rate_of_change_trigger>(
214 8.0,
215 500ms,
216 rate_of_change_trigger::rate_direction::either,
217 3
218 );
219
220 std::cout << " Simulating rapidly increasing values:" << std::endl;
221
222 // Simulate increasing values
223 std::vector<double> increasing_values = {10.0, 15.0, 25.0, 40.0, 60.0, 85.0};
224 for (size_t i = 0; i < increasing_values.size(); ++i) {
225 double val = increasing_values[i];
226 bool triggered = rate_increasing->evaluate(val);
227 std::cout << " Sample " << (i + 1) << ": value=" << val
228 << " | Rate trigger: " << (triggered ? "YES" : "NO") << std::endl;
229 std::this_thread::sleep_for(100ms); // Simulate time between samples
230 }
231 std::cout << std::endl;
232
233 // Reset and simulate decreasing values
234 rate_decreasing->reset();
235 std::cout << " Simulating rapidly decreasing values:" << std::endl;
236
237 std::vector<double> decreasing_values = {100.0, 95.0, 85.0, 70.0, 50.0, 25.0};
238 for (size_t i = 0; i < decreasing_values.size(); ++i) {
239 double val = decreasing_values[i];
240 bool triggered = rate_decreasing->evaluate(val);
241 std::cout << " Sample " << (i + 1) << ": value=" << val
242 << " | Rate trigger: " << (triggered ? "YES" : "NO") << std::endl;
243 std::this_thread::sleep_for(100ms);
244 }
245 std::cout << std::endl;
246
247 // =========================================================================
248 // Section 4: AnomalyTrigger - Statistical Deviation
249 // =========================================================================
250 std::cout << "4. Anomaly Trigger (Statistical)" << std::endl;
251 std::cout << " ------------------------------" << std::endl;
252
253 // Trigger when value is more than 2 standard deviations from mean
254 auto anomaly_trigger_ptr = std::make_shared<anomaly_trigger>(
255 2.0, // sensitivity (number of std devs)
256 20, // window size for baseline
257 5 // minimum samples before detection
258 );
259
260 std::cout << " Building baseline with normal values (around 50):" << std::endl;
261
262 // Random number generator for realistic simulation
263 std::random_device rd;
264 std::mt19937 gen(rd());
265 std::normal_distribution<> normal_dist(50.0, 5.0); // mean=50, stddev=5
266
267 // Feed normal values to build baseline
268 for (int i = 0; i < 15; ++i) {
269 double val = normal_dist(gen);
270 bool triggered = anomaly_trigger_ptr->evaluate(val);
271 std::cout << " Sample " << (i + 1) << ": value=" << std::fixed
272 << std::setprecision(1) << val
273 << " | Anomaly: " << (triggered ? "YES" : "NO") << std::endl;
274 }
275
276 std::cout << std::endl;
277 std::cout << " Current baseline - Mean: " << std::fixed << std::setprecision(2)
278 << anomaly_trigger_ptr->current_mean()
279 << ", StdDev: " << anomaly_trigger_ptr->current_stddev() << std::endl;
280 std::cout << std::endl;
281
282 // Now introduce anomalous values
283 std::cout << " Introducing anomalous values:" << std::endl;
284 std::vector<double> anomalous_values = {80.0, 20.0, 100.0, 52.0}; // Mix of anomalies and normal
285 for (double val : anomalous_values) {
286 bool triggered = anomaly_trigger_ptr->evaluate(val);
287 std::cout << " Value: " << val << " | Anomaly: " << (triggered ? "YES" : "NO")
288 << " (>" << (anomaly_trigger_ptr->current_stddev() * 2)
289 << " from mean " << anomaly_trigger_ptr->current_mean() << ")" << std::endl;
290 }
291 std::cout << std::endl;
292
293 // =========================================================================
294 // Section 5: CompositeTrigger - Logical Combinations
295 // =========================================================================
296 std::cout << "5. Composite Triggers (AND/OR/NOT)" << std::endl;
297 std::cout << " --------------------------------" << std::endl;
298
299 // Create individual triggers for composition
300 auto cpu_high = threshold_trigger::above(80.0);
301 auto memory_high = threshold_trigger::above(90.0);
302 auto disk_high = threshold_trigger::above(85.0);
303
304 // AND: All conditions must be true
305 auto all_high = composite_trigger::all_of({cpu_high, memory_high, disk_high});
306
307 // OR: Any condition can trigger
308 auto any_high = composite_trigger::any_of({cpu_high, memory_high, disk_high});
309
310 // NOT: Inverts a trigger
311 auto cpu_not_high = composite_trigger::invert(threshold_trigger::above(80.0));
312
313 // XOR: Exactly one condition true
314 auto xor_trigger = std::make_shared<composite_trigger>(
315 composite_operation::XOR,
316 std::vector<std::shared_ptr<alert_trigger>>{cpu_high, memory_high}
317 );
318
319 std::cout << " Composite trigger descriptions:" << std::endl;
320 std::cout << " - ALL (AND): " << all_high->description() << std::endl;
321 std::cout << " - ANY (OR): " << any_high->description() << std::endl;
322 std::cout << " - NOT: " << cpu_not_high->description() << std::endl;
323 std::cout << " - XOR: " << xor_trigger->description() << std::endl;
324 std::cout << std::endl;
325
326 // Test with different value combinations
327 struct TestCase {
328 double cpu;
329 double memory;
330 double disk;
331 };
332
333 std::vector<TestCase> composite_tests = {
334 {50.0, 50.0, 50.0}, // All low
335 {85.0, 50.0, 50.0}, // Only CPU high
336 {85.0, 95.0, 50.0}, // CPU and memory high
337 {85.0, 95.0, 90.0}, // All high
338 };
339
340 std::cout << " Testing composite triggers:" << std::endl;
341 for (const auto& tc : composite_tests) {
342 std::cout << " CPU=" << tc.cpu << ", Memory=" << tc.memory
343 << ", Disk=" << tc.disk << std::endl;
344
345 // For multi-value evaluation
346 std::vector<double> values = {tc.cpu, tc.memory, tc.disk};
347
348 // Note: evaluate() uses same value for all; evaluate_multi() uses individual values
349 bool all_result = all_high->evaluate_multi(values);
350 bool any_result = any_high->evaluate_multi(values);
351 bool not_result = cpu_not_high->evaluate(tc.cpu);
352 bool xor_result = xor_trigger->evaluate_multi({tc.cpu, tc.memory});
353
354 std::cout << " ALL: " << (all_result ? "YES" : "NO")
355 << " | ANY: " << (any_result ? "YES" : "NO")
356 << " | NOT(cpu>80): " << (not_result ? "YES" : "NO")
357 << " | XOR(cpu,mem): " << (xor_result ? "YES" : "NO") << std::endl;
358 }
359 std::cout << std::endl;
360
361 // =========================================================================
362 // Section 6: DeltaTrigger - Change Detection
363 // =========================================================================
364 std::cout << "6. Delta Trigger (Change Detection)" << std::endl;
365 std::cout << " ----------------------------------" << std::endl;
366
367 // Trigger on any change > 10 (absolute)
368 auto delta_absolute = std::make_shared<delta_trigger>(10.0, true);
369
370 // Trigger on positive change > 5
371 auto delta_positive = std::make_shared<delta_trigger>(5.0, false);
372
373 std::cout << " Testing delta triggers with sequential values:" << std::endl;
374 std::vector<double> delta_values = {50.0, 52.0, 55.0, 70.0, 68.0, 55.0};
375
376 for (size_t i = 0; i < delta_values.size(); ++i) {
377 double val = delta_values[i];
378 bool abs_triggered = delta_absolute->evaluate(val);
379 bool pos_triggered = delta_positive->evaluate(val);
380
381 std::cout << " Value: " << val;
382 if (i > 0) {
383 std::cout << " (delta=" << (val - delta_values[i-1]) << ")";
384 }
385 std::cout << " | Absolute(>10): " << (abs_triggered ? "YES" : "NO")
386 << " | Positive(>5): " << (pos_triggered ? "YES" : "NO") << std::endl;
387 }
388 std::cout << std::endl;
389
390 // =========================================================================
391 // Section 7: AbsentTrigger - Missing Data Detection
392 // =========================================================================
393 std::cout << "7. Absent Trigger (Missing Data)" << std::endl;
394 std::cout << " ------------------------------" << std::endl;
395
396 // Trigger if no data received for 200ms
397 auto absent_trigger_ptr = std::make_shared<absent_trigger>(200ms);
398
399 std::cout << " " << absent_trigger_ptr->description() << std::endl;
400 std::cout << " Simulating data with gaps:" << std::endl;
401
402 // Regular data
403 for (int i = 0; i < 3; ++i) {
404 bool triggered = absent_trigger_ptr->evaluate(100.0);
405 std::cout << " Evaluation " << (i + 1) << " (immediate): "
406 << (triggered ? "ABSENT" : "present") << std::endl;
407 std::this_thread::sleep_for(50ms);
408 }
409
410 // Simulate gap in data
411 std::cout << " ... waiting 300ms (simulating data gap) ..." << std::endl;
412 std::this_thread::sleep_for(300ms);
413
414 bool after_gap = absent_trigger_ptr->evaluate(100.0);
415 std::cout << " Evaluation after gap: " << (after_gap ? "ABSENT" : "present") << std::endl;
416 std::cout << std::endl;
417
418 // =========================================================================
419 // Section 8: Custom Trigger Implementation
420 // =========================================================================
421 std::cout << "8. Custom Trigger Implementations" << std::endl;
422 std::cout << " -------------------------------" << std::endl;
423
424 // Periodic trigger - fires every 3rd evaluation
425 auto periodic = std::make_shared<periodic_trigger>(3);
426 std::cout << " Periodic trigger: " << periodic->description() << std::endl;
427
428 for (int i = 1; i <= 9; ++i) {
429 bool triggered = periodic->evaluate(0); // Value doesn't matter
430 std::cout << " Evaluation " << i << ": " << (triggered ? "FIRE" : "-") << std::endl;
431 }
432 std::cout << std::endl;
433
434 // Moving average trigger
435 auto ma_trigger = std::make_shared<moving_average_trigger>(5, 60.0);
436 std::cout << " Moving average trigger: " << ma_trigger->description() << std::endl;
437
438 std::vector<double> ma_values = {50.0, 55.0, 60.0, 65.0, 70.0, 75.0, 80.0};
439 for (size_t i = 0; i < ma_values.size(); ++i) {
440 double val = ma_values[i];
441 bool triggered = ma_trigger->evaluate(val);
442 std::cout << " Value: " << val << " | MA(5)=" << std::fixed << std::setprecision(1)
443 << ma_trigger->current_average()
444 << " | Triggered: " << (triggered ? "YES" : "NO") << std::endl;
445 }
446 std::cout << std::endl;
447
448 // =========================================================================
449 // Section 9: Combining Triggers with Alert Rules
450 // =========================================================================
451 std::cout << "9. Using Triggers with Alert Rules" << std::endl;
452 std::cout << " ---------------------------------" << std::endl;
453
454 // Create an alert rule with a composite trigger
455 alert_rule complex_rule("complex_system_alert");
456 complex_rule.set_metric_name("system_health")
457 .set_severity(alert_severity::critical)
458 .set_summary("System health degraded")
459 .set_description("Multiple system metrics exceeded thresholds")
460 .add_label("team", "ops")
461 .add_label("priority", "p1");
462
463 // Complex trigger: (CPU > 80 AND Memory > 85) OR Disk > 95
464 auto cpu_mem_trigger = composite_trigger::all_of({
467 });
468
469 auto disk_critical = threshold_trigger::above(95.0);
470
471 auto complex_composite = composite_trigger::any_of({
472 cpu_mem_trigger,
473 disk_critical
474 });
475
476 complex_rule.set_trigger(complex_composite);
477
478 // Validate rule
479 if (auto result = complex_rule.validate(); result.is_ok()) {
480 std::cout << " Rule '" << complex_rule.name() << "' validated successfully" << std::endl;
481 std::cout << " Trigger type: " << complex_rule.trigger()->type_name() << std::endl;
482 std::cout << " Description: " << complex_rule.trigger()->description() << std::endl;
483 }
484 std::cout << std::endl;
485
486 // =========================================================================
487 // Summary
488 // =========================================================================
489 std::cout << "=== Alert Triggers Example Completed ===" << std::endl;
490 std::cout << std::endl;
491 std::cout << "Triggers demonstrated:" << std::endl;
492 std::cout << " - ThresholdTrigger (>, >=, <, <=, ==, !=)" << std::endl;
493 std::cout << " - RangeTrigger (in_range, out_of_range)" << std::endl;
494 std::cout << " - RateOfChangeTrigger (increasing, decreasing, either)" << std::endl;
495 std::cout << " - AnomalyTrigger (statistical deviation)" << std::endl;
496 std::cout << " - CompositeTrigger (AND, OR, XOR, NOT)" << std::endl;
497 std::cout << " - DeltaTrigger (change detection)" << std::endl;
498 std::cout << " - AbsentTrigger (missing data)" << std::endl;
499 std::cout << " - Custom triggers (periodic, moving average)" << std::endl;
500
501 return 0;
502}
void print_eval_result(const std::string &trigger_name, double value, bool triggered, const std::string &description="")
Defines conditions and behavior for alert triggering.
Definition alert_rule.h:82
static std::shared_ptr< composite_trigger > all_of(std::vector< std::shared_ptr< alert_trigger > > triggers)
Create AND composite.
static std::shared_ptr< composite_trigger > any_of(std::vector< std::shared_ptr< alert_trigger > > triggers)
Create OR composite.
static std::shared_ptr< composite_trigger > invert(std::shared_ptr< alert_trigger > trigger)
Create NOT composite.
static std::shared_ptr< threshold_trigger > below_or_equal(double threshold)
Create trigger for value <= threshold.
static std::shared_ptr< threshold_trigger > below(double threshold)
Create trigger for value < threshold.
static std::shared_ptr< class range_trigger > in_range(double min_val, double max_val)
Create trigger for value within range (inclusive)
static std::shared_ptr< threshold_trigger > above_or_equal(double threshold)
Create trigger for value >= threshold.
static std::shared_ptr< class range_trigger > out_of_range(double min_val, double max_val)
Create trigger for value outside range (exclusive)
static std::shared_ptr< threshold_trigger > above(double threshold)
Create trigger for value > threshold.
@ memory
Memory/DRAM power domain (RAPL)
@ cpu
CPU power domain (RAPL)

References kcenon::monitoring::threshold_trigger::above(), kcenon::monitoring::threshold_trigger::above_or_equal(), kcenon::monitoring::alert_rule::add_label(), kcenon::monitoring::composite_trigger::all_of(), kcenon::monitoring::composite_trigger::any_of(), kcenon::monitoring::threshold_trigger::below(), kcenon::monitoring::threshold_trigger::below_or_equal(), kcenon::monitoring::cpu, kcenon::monitoring::threshold_trigger::in_range(), kcenon::monitoring::composite_trigger::invert(), kcenon::monitoring::memory, kcenon::monitoring::alert_rule::name(), kcenon::monitoring::threshold_trigger::out_of_range(), print_eval_result(), kcenon::monitoring::alert_rule::set_description(), kcenon::monitoring::alert_rule::set_metric_name(), kcenon::monitoring::alert_rule::set_severity(), kcenon::monitoring::alert_rule::set_summary(), kcenon::monitoring::alert_rule::set_trigger(), kcenon::monitoring::alert_rule::trigger(), and kcenon::monitoring::alert_rule::validate().

Here is the call graph for this function:

◆ print_eval_result()

void print_eval_result ( const std::string & trigger_name,
double value,
bool triggered,
const std::string & description = "" )
Examples
alert_triggers_example.cpp.

Definition at line 33 of file alert_triggers_example.cpp.

36 {
37 std::cout << " " << std::setw(25) << std::left << trigger_name
38 << " | Value: " << std::setw(8) << std::fixed << std::setprecision(2) << value
39 << " | Triggered: " << (triggered ? "YES" : "NO ");
40 if (!description.empty()) {
41 std::cout << " | " << description;
42 }
43 std::cout << std::endl;
44}

Referenced by main().

Here is the caller graph for this function: