Monitoring System 0.1.0
System resource monitoring with pluggable collectors and alerting
Loading...
Searching...
No Matches
alert_notifiers_example.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2021-2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
20#include <chrono>
21#include <cstdio>
22#include <filesystem>
23#include <fstream>
24#include <iostream>
25#include <map>
26#include <mutex>
27#include <sstream>
28#include <thread>
29#include <vector>
30
34
35using namespace std::chrono_literals;
36
37namespace kcenon::monitoring {
38
39// Helper function to create sample alerts
40alert create_sample_alert(const std::string& name,
41 alert_severity severity,
42 alert_state state,
43 double value,
44 const std::string& team = "ops") {
45 alert a;
46 a.name = name;
47 a.severity = severity;
48 a.state = state;
49 a.value = value;
50 a.labels.set("team", team);
51 a.labels.set("environment", "production");
52 a.annotations.summary = "Alert: " + name;
53 a.annotations.description = "Detailed description for " + name;
54 a.rule_name = name + "_rule";
55 return a;
56}
57
58// Custom notifier implementation: Console notifier with color
60public:
61 explicit console_color_notifier(std::string notifier_name)
62 : name_(std::move(notifier_name)) {}
63
64 std::string name() const override { return name_; }
65
66 common::VoidResult notify(const alert& a) override {
67 std::string color = get_severity_color(a.severity);
68 std::string reset = "\033[0m";
69
70 std::cout << color << "[" << name_ << "] "
72 << a.name << " (" << alert_state_to_string(a.state) << ")"
73 << reset << std::endl;
74 std::cout << " Summary: " << a.annotations.summary << std::endl;
75 std::cout << " Value: " << a.value << std::endl;
76
77 return common::ok();
78 }
79
80 common::VoidResult notify_group(const alert_group& group) override {
81 std::cout << "[" << name_ << "] Alert Group: " << group.group_key
82 << " (" << group.size() << " alerts)" << std::endl;
83
84 for (const auto& alert_item : group.alerts) {
85 auto result = notify(alert_item);
86 if (!result.is_ok()) {
87 return result;
88 }
89 }
90 return common::ok();
91 }
92
93 bool is_ready() const override { return true; }
94
95private:
96 static std::string get_severity_color(alert_severity sev) {
97 switch (sev) {
98 case alert_severity::emergency: return "\033[41m\033[37m"; // White on red
99 case alert_severity::critical: return "\033[31m"; // Red
100 case alert_severity::warning: return "\033[33m"; // Yellow
101 case alert_severity::info: return "\033[32m"; // Green
102 default: return "\033[0m"; // Reset
103 }
104 }
105
106 std::string name_;
107};
108
109// Custom notifier: Statistics collector
111public:
112 explicit statistics_notifier(std::string notifier_name)
113 : name_(std::move(notifier_name)) {}
114
115 std::string name() const override { return name_; }
116
117 common::VoidResult notify(const alert& a) override {
118 std::lock_guard<std::mutex> lock(mutex_);
121 state_counts_[a.state]++;
122 return common::ok();
123 }
124
125 common::VoidResult notify_group(const alert_group& group) override {
126 for (const auto& alert_item : group.alerts) {
127 auto result = notify(alert_item);
128 if (!result.is_ok()) {
129 return result;
130 }
131 }
132 return common::ok();
133 }
134
135 bool is_ready() const override { return true; }
136
137 void print_statistics() const {
138 std::lock_guard<std::mutex> lock(mutex_);
139
140 std::cout << "Statistics from " << name_ << ":" << std::endl;
141 std::cout << " Total alerts: " << total_alerts_ << std::endl;
142 std::cout << " By severity:" << std::endl;
143 for (const auto& [sev, count] : severity_counts_) {
144 std::cout << " " << alert_severity_to_string(sev) << ": " << count << std::endl;
145 }
146 std::cout << " By state:" << std::endl;
147 for (const auto& [state, count] : state_counts_) {
148 std::cout << " " << alert_state_to_string(state) << ": " << count << std::endl;
149 }
150 }
151
152 void reset() {
153 std::lock_guard<std::mutex> lock(mutex_);
154 total_alerts_ = 0;
155 severity_counts_.clear();
156 state_counts_.clear();
157 }
158
159private:
160 std::string name_;
161 mutable std::mutex mutex_;
162 size_t total_alerts_ = 0;
163 std::map<alert_severity, size_t> severity_counts_;
164 std::map<alert_state, size_t> state_counts_;
165};
166
167} // namespace kcenon::monitoring
168
169using namespace kcenon::monitoring;
170
171int main() {
172 std::cout << "=== Alert Notifiers Example ===" << std::endl;
173 std::cout << std::endl;
174
175 // Temporary directory for file output
176 const std::string temp_dir = "/tmp/alert_notifiers_example";
177 std::filesystem::create_directories(temp_dir);
178
179 // =========================================================================
180 // Section 1: Alert Formatters
181 // =========================================================================
182 std::cout << "1. Alert Formatters" << std::endl;
183 std::cout << " -----------------" << std::endl;
184
185 // Create sample alert for formatting demo
186 alert sample = create_sample_alert(
187 "high_cpu_usage",
188 alert_severity::critical,
189 alert_state::firing,
190 95.5
191 );
192
193 // JSON formatter
194 json_alert_formatter json_fmt;
195 std::cout << " JSON format:" << std::endl;
196 std::cout << " " << json_fmt.format(sample) << std::endl;
197 std::cout << std::endl;
198
199 // Text formatter
200 text_alert_formatter text_fmt;
201 std::cout << " Text format:" << std::endl;
202 std::cout << " " << text_fmt.format(sample) << std::endl;
203 std::cout << std::endl;
204
205 // =========================================================================
206 // Section 2: LogNotifier (Built-in)
207 // =========================================================================
208 std::cout << "2. Log Notifier" << std::endl;
209 std::cout << " -------------" << std::endl;
210
211 auto log_notifier_ptr = std::make_shared<log_notifier>("system_logger");
212
213 std::cout << " Notifier name: " << log_notifier_ptr->name() << std::endl;
214 std::cout << " Ready: " << (log_notifier_ptr->is_ready() ? "yes" : "no") << std::endl;
215
216 // Send alert to log notifier
217 std::cout << " Sending alert to log notifier..." << std::endl;
218 if (auto result = log_notifier_ptr->notify(sample); result.is_ok()) {
219 std::cout << " Alert logged successfully" << std::endl;
220 } else {
221 std::cout << " Failed to log alert: " << result.error().message << std::endl;
222 }
223 std::cout << std::endl;
224
225 // =========================================================================
226 // Section 3: FileNotifier
227 // =========================================================================
228 std::cout << "3. File Notifier" << std::endl;
229 std::cout << " --------------" << std::endl;
230
231 std::string alert_log_path = temp_dir + "/alerts.log";
232 auto file_notifier_ptr = std::make_shared<file_notifier>(
233 alert_log_path,
234 std::make_shared<text_alert_formatter>()
235 );
236
237 std::cout << " Notifier name: " << file_notifier_ptr->name() << std::endl;
238 std::cout << " Output file: " << alert_log_path << std::endl;
239
240 // Create several alerts and write to file
241 std::vector<alert> alerts_to_log = {
242 create_sample_alert("cpu_high", alert_severity::critical, alert_state::firing, 92.0),
243 create_sample_alert("memory_low", alert_severity::warning, alert_state::pending, 15.0),
244 create_sample_alert("disk_full", alert_severity::emergency, alert_state::firing, 98.0),
245 };
246
247 for (const auto& a : alerts_to_log) {
248 if (auto result = file_notifier_ptr->notify(a); !result.is_ok()) {
249 std::cout << " Failed to write alert: " << result.error().message << std::endl;
250 }
251 }
252
253 std::cout << " Wrote " << alerts_to_log.size() << " alerts to file" << std::endl;
254
255 // Read back and display file contents
256 std::cout << " File contents:" << std::endl;
257 std::ifstream file(alert_log_path);
258 std::string line;
259 while (std::getline(file, line)) {
260 std::cout << " " << line << std::endl;
261 }
262 std::cout << std::endl;
263
264 // =========================================================================
265 // Section 4: WebhookNotifier Configuration
266 // =========================================================================
267 std::cout << "4. Webhook Notifier Configuration" << std::endl;
268 std::cout << " -------------------------------" << std::endl;
269
270 webhook_config webhook_cfg;
271 webhook_cfg.url = "https://hooks.example.com/alerts";
272 webhook_cfg.method = "POST";
273 webhook_cfg.timeout = 30000ms;
274 webhook_cfg.max_retries = 3;
275 webhook_cfg.retry_delay = 1000ms;
276 webhook_cfg.send_resolved = true;
277 webhook_cfg.content_type = "application/json";
278 webhook_cfg.add_header("Authorization", "Bearer token-xxx")
279 .add_header("X-Alert-Source", "monitoring-system");
280
281 std::cout << " URL: " << webhook_cfg.url << std::endl;
282 std::cout << " Method: " << webhook_cfg.method << std::endl;
283 std::cout << " Timeout: " << webhook_cfg.timeout.count() << "ms" << std::endl;
284 std::cout << " Max retries: " << webhook_cfg.max_retries << std::endl;
285 std::cout << " Headers:" << std::endl;
286 for (const auto& [key, value] : webhook_cfg.headers) {
287 std::cout << " " << key << ": " << value << std::endl;
288 }
289 std::cout << std::endl;
290
291 // Create webhook notifier
292 auto webhook_notifier_ptr = std::make_shared<webhook_notifier>(
293 webhook_cfg,
294 std::make_shared<json_alert_formatter>()
295 );
296
297 std::cout << " Notifier name: " << webhook_notifier_ptr->name() << std::endl;
298 std::cout << " Ready: " << (webhook_notifier_ptr->is_ready() ? "yes" : "no")
299 << " (no HTTP sender configured)" << std::endl;
300
301 // Configure mock HTTP sender for testing
302 int http_call_count = 0;
303 webhook_notifier_ptr->set_http_sender(
304 [&http_call_count](const std::string& url,
305 const std::string& method,
306 const std::unordered_map<std::string, std::string>& headers,
307 const std::string& body) -> kcenon::common::VoidResult {
308 http_call_count++;
309 std::cout << " [MOCK HTTP] " << method << " " << url << std::endl;
310 std::cout << " [MOCK HTTP] Headers: " << headers.size() << std::endl;
311 std::cout << " [MOCK HTTP] Body length: " << body.length() << " chars" << std::endl;
312 return kcenon::common::ok();
313 }
314 );
315
316 std::cout << " Ready after setting HTTP sender: "
317 << (webhook_notifier_ptr->is_ready() ? "yes" : "no") << std::endl;
318
319 // Test webhook notification
320 std::cout << " Testing webhook notification:" << std::endl;
321 if (auto result = webhook_notifier_ptr->notify(sample); result.is_ok()) {
322 std::cout << " Webhook notification sent (HTTP calls: " << http_call_count << ")" << std::endl;
323 }
324 std::cout << std::endl;
325
326 // =========================================================================
327 // Section 5: CallbackNotifier
328 // =========================================================================
329 std::cout << "5. Callback Notifier" << std::endl;
330 std::cout << " ------------------" << std::endl;
331
332 size_t callback_count = 0;
333 auto callback_notifier_ptr = std::make_shared<callback_notifier>(
334 "custom_callback",
335 [&callback_count](const alert& a) {
336 callback_count++;
337 std::cout << " [CALLBACK] Received: " << a.name
338 << " (severity: " << alert_severity_to_string(a.severity) << ")"
339 << std::endl;
340 },
341 [&callback_count](const alert_group& group) {
342 callback_count += group.size();
343 std::cout << " [CALLBACK GROUP] Received group: " << group.group_key
344 << " (" << group.size() << " alerts)" << std::endl;
345 }
346 );
347
348 std::cout << " Testing callback notifier:" << std::endl;
349 callback_notifier_ptr->notify(sample);
350 std::cout << " Callbacks executed: " << callback_count << std::endl;
351 std::cout << std::endl;
352
353 // =========================================================================
354 // Section 6: MultiNotifier - Multiple Targets
355 // =========================================================================
356 std::cout << "6. Multi Notifier (Multiple Targets)" << std::endl;
357 std::cout << " -----------------------------------" << std::endl;
358
359 auto multi = std::make_shared<multi_notifier>("multi_channel");
360
361 // Add multiple child notifiers
362 auto log_child = std::make_shared<log_notifier>("log_child");
363 auto stats_child = std::make_shared<statistics_notifier>("stats_child");
364 auto console_child = std::make_shared<console_color_notifier>("console_child");
365
366 multi->add_notifier(log_child);
367 multi->add_notifier(stats_child);
368 multi->add_notifier(console_child);
369
370 std::cout << " Added 3 child notifiers to multi_channel" << std::endl;
371 std::cout << " Sending alert to all channels:" << std::endl;
372
373 if (auto result = multi->notify(sample); result.is_ok()) {
374 std::cout << " All notifiers succeeded" << std::endl;
375 } else {
376 std::cout << " Some notifiers failed: " << result.error().message << std::endl;
377 }
378 std::cout << std::endl;
379
380 // =========================================================================
381 // Section 7: BufferedNotifier - Batching
382 // =========================================================================
383 std::cout << "7. Buffered Notifier (Batching)" << std::endl;
384 std::cout << " -----------------------------" << std::endl;
385
386 auto inner_notifier = std::make_shared<statistics_notifier>("buffered_inner");
387 auto buffered = std::make_shared<buffered_notifier>(
388 inner_notifier,
389 5, // buffer size
390 10000ms // flush interval
391 );
392
393 std::cout << " Buffer size: 5, flush interval: 10s" << std::endl;
394 std::cout << " Sending alerts (will buffer until size reached):" << std::endl;
395
396 for (int i = 1; i <= 7; ++i) {
397 auto a = create_sample_alert(
398 "buffered_alert_" + std::to_string(i),
399 alert_severity::warning,
400 alert_state::firing,
401 static_cast<double>(i * 10)
402 );
403 buffered->notify(a);
404 std::cout << " Sent alert " << i << ", pending: " << buffered->pending_count() << std::endl;
405 }
406
407 // Force flush remaining
408 std::cout << " Forcing flush of remaining alerts..." << std::endl;
409 buffered->flush();
410 std::cout << " Pending after flush: " << buffered->pending_count() << std::endl;
411
412 inner_notifier->print_statistics();
413 std::cout << std::endl;
414
415 // =========================================================================
416 // Section 8: RoutingNotifier - Conditional Routing
417 // =========================================================================
418 std::cout << "8. Routing Notifier (Conditional Routing)" << std::endl;
419 std::cout << " ---------------------------------------" << std::endl;
420
421 auto router = std::make_shared<routing_notifier>("alert_router");
422
423 // Create notifiers for different routes
424 auto critical_notifier = std::make_shared<console_color_notifier>("critical_channel");
425 auto warning_notifier = std::make_shared<console_color_notifier>("warning_channel");
426 auto default_notifier = std::make_shared<console_color_notifier>("default_channel");
427
428 // Route by severity
429 router->route_by_severity(alert_severity::critical, critical_notifier);
430 router->route_by_severity(alert_severity::emergency, critical_notifier);
431 router->route_by_severity(alert_severity::warning, warning_notifier);
432 router->set_default_route(default_notifier);
433
434 std::cout << " Routing rules configured:" << std::endl;
435 std::cout << " - critical/emergency -> critical_channel" << std::endl;
436 std::cout << " - warning -> warning_channel" << std::endl;
437 std::cout << " - default -> default_channel" << std::endl;
438 std::cout << std::endl;
439
440 std::cout << " Testing routing with different severities:" << std::endl;
441
442 std::vector<alert> routing_tests = {
443 create_sample_alert("critical_alert", alert_severity::critical, alert_state::firing, 99.0),
444 create_sample_alert("warning_alert", alert_severity::warning, alert_state::firing, 75.0),
445 create_sample_alert("info_alert", alert_severity::info, alert_state::firing, 50.0),
446 };
447
448 for (const auto& a : routing_tests) {
449 std::cout << " Routing '" << a.name << "' (severity: "
450 << alert_severity_to_string(a.severity) << "):" << std::endl;
451 router->notify(a);
452 }
453 std::cout << std::endl;
454
455 // Route by label
456 std::cout << " Adding label-based routing:" << std::endl;
457 auto ops_notifier = std::make_shared<console_color_notifier>("ops_team_channel");
458 router->route_by_label("team", "ops", ops_notifier);
459
460 auto ops_alert = create_sample_alert("ops_alert", alert_severity::info, alert_state::firing, 60.0, "ops");
461 std::cout << " Routing alert with team=ops:" << std::endl;
462 router->notify(ops_alert);
463 std::cout << std::endl;
464
465 // =========================================================================
466 // Section 9: Custom Notifier Implementation
467 // =========================================================================
468 std::cout << "9. Custom Notifier Implementation" << std::endl;
469 std::cout << " -------------------------------" << std::endl;
470
471 // Statistics notifier demonstration
472 auto stats = std::make_shared<statistics_notifier>("alert_statistics");
473
474 // Send various alerts
475 std::vector<alert> stat_alerts = {
476 create_sample_alert("alert1", alert_severity::critical, alert_state::firing, 90.0),
477 create_sample_alert("alert2", alert_severity::warning, alert_state::pending, 70.0),
478 create_sample_alert("alert3", alert_severity::critical, alert_state::resolved, 40.0),
479 create_sample_alert("alert4", alert_severity::info, alert_state::firing, 50.0),
480 create_sample_alert("alert5", alert_severity::warning, alert_state::firing, 65.0),
481 };
482
483 for (const auto& a : stat_alerts) {
484 stats->notify(a);
485 }
486
487 stats->print_statistics();
488 std::cout << std::endl;
489
490 // =========================================================================
491 // Section 10: Alert Group Notification
492 // =========================================================================
493 std::cout << "10. Alert Group Notification" << std::endl;
494 std::cout << " -------------------------" << std::endl;
495
496 // Create an alert group
497 alert_group group("infrastructure-alerts");
498 group.common_labels.set("environment", "production");
499 group.common_labels.set("datacenter", "us-west-2");
500
501 group.add_alert(create_sample_alert("cpu_server1", alert_severity::critical, alert_state::firing, 95.0));
502 group.add_alert(create_sample_alert("cpu_server2", alert_severity::warning, alert_state::firing, 82.0));
503 group.add_alert(create_sample_alert("cpu_server3", alert_severity::critical, alert_state::firing, 91.0));
504
505 std::cout << " Group: " << group.group_key << std::endl;
506 std::cout << " Alerts: " << group.size() << std::endl;
507 std::cout << " Max severity: " << alert_severity_to_string(group.max_severity()) << std::endl;
508 std::cout << std::endl;
509
510 // Test JSON formatter with group
511 std::cout << " JSON formatted group:" << std::endl;
512 std::cout << " " << json_fmt.format_group(group) << std::endl;
513 std::cout << std::endl;
514
515 // Send to multiple notifiers
516 auto group_stats = std::make_shared<statistics_notifier>("group_stats");
517 group_stats->notify_group(group);
518 group_stats->print_statistics();
519 std::cout << std::endl;
520
521 // =========================================================================
522 // Section 11: Error Handling
523 // =========================================================================
524 std::cout << "11. Error Handling" << std::endl;
525 std::cout << " ---------------" << std::endl;
526
527 // Webhook with failing HTTP sender
528 webhook_config fail_cfg;
529 fail_cfg.url = "https://failing.example.com/alerts";
530 fail_cfg.max_retries = 2;
531 fail_cfg.retry_delay = 100ms;
532
533 auto failing_webhook = std::make_shared<webhook_notifier>(fail_cfg);
534 int retry_count = 0;
535
536 failing_webhook->set_http_sender(
537 [&retry_count](const std::string& /*url*/,
538 const std::string& /*method*/,
539 const std::unordered_map<std::string, std::string>& /*headers*/,
540 const std::string& /*body*/) -> kcenon::common::VoidResult {
541 retry_count++;
542 std::cout << " HTTP attempt " << retry_count << " - simulating failure" << std::endl;
543 return kcenon::common::VoidResult::err(500, "Simulated server error");
544 }
545 );
546
547 std::cout << " Testing webhook with simulated failures:" << std::endl;
548 auto fail_result = failing_webhook->notify(sample);
549 if (!fail_result.is_ok()) {
550 std::cout << " Expected failure after " << retry_count << " attempts: "
551 << fail_result.error().message << std::endl;
552 }
553 std::cout << std::endl;
554
555 // =========================================================================
556 // Cleanup
557 // =========================================================================
558 std::cout << "12. Cleanup" << std::endl;
559 std::cout << " -------" << std::endl;
560
561 // Remove temporary files
562 std::filesystem::remove_all(temp_dir);
563 std::cout << " Removed temporary directory: " << temp_dir << std::endl;
564 std::cout << std::endl;
565
566 std::cout << "=== Alert Notifiers Example Completed ===" << std::endl;
567 std::cout << std::endl;
568 std::cout << "Notifiers demonstrated:" << std::endl;
569 std::cout << " - LogNotifier (built-in logging)" << std::endl;
570 std::cout << " - FileNotifier (file-based alerts)" << std::endl;
571 std::cout << " - WebhookNotifier (HTTP webhooks)" << std::endl;
572 std::cout << " - CallbackNotifier (custom callbacks)" << std::endl;
573 std::cout << " - MultiNotifier (multiple targets)" << std::endl;
574 std::cout << " - BufferedNotifier (batching)" << std::endl;
575 std::cout << " - RoutingNotifier (conditional routing)" << std::endl;
576 std::cout << " - Custom implementations (color console, statistics)" << std::endl;
577 std::cout << " - Alert formatters (JSON, text)" << std::endl;
578
579 return 0;
580}
Alert notification implementations.
Core alert data structures for the monitoring system.
Base class for alert notification handlers.
std::string name() const override
Get notifier name.
common::VoidResult notify_group(const alert_group &group) override
Send a notification for an alert group.
bool is_ready() const override
Check if notifier is ready.
static std::string get_severity_color(alert_severity sev)
common::VoidResult notify(const alert &a) override
Send a notification for an alert.
std::string format_group(const alert_group &group) const override
Format an alert group.
std::string format(const alert &a) const override
Format a single alert.
std::map< alert_severity, size_t > severity_counts_
std::string name() const override
Get notifier name.
common::VoidResult notify_group(const alert_group &group) override
Send a notification for an alert group.
common::VoidResult notify(const alert &a) override
Send a notification for an alert.
bool is_ready() const override
Check if notifier is ready.
Formats alerts as human-readable text.
std::string format(const alert &a) const override
Format a single alert.
alert create_sample_alert(const std::string &name, alert_severity severity, alert_state state, double value, const std::string &team="ops")
alert_severity
Severity levels for alerts.
Definition alert_types.h:35
@ warning
Warning condition, may require attention.
@ critical
Critical condition, immediate attention required.
@ emergency
Emergency condition, system-wide impact.
@ info
Informational, no action required.
constexpr const char * alert_state_to_string(alert_state state) noexcept
Convert alert state to string.
Definition alert_types.h:82
constexpr const char * alert_severity_to_string(alert_severity severity) noexcept
Convert alert severity to string.
Definition alert_types.h:47
alert_state
State machine states for alert lifecycle.
Definition alert_types.h:69
Result pattern type definitions for monitoring system.
std::string description
Detailed description.
std::string summary
Brief description.
Group of related alerts for batch notification.
std::string group_key
Common grouping key.
alert_labels common_labels
Labels shared by all alerts.
void add_alert(alert a)
Add an alert to the group.
std::vector< alert > alerts
Alerts in this group.
size_t size() const
Get count of alerts in the group.
alert_severity max_severity() const
Get highest severity in the group.
void set(const std::string &key, const std::string &value)
Add or update a label.
Core alert data structure.
alert_state state
Current state.
double value
Current metric value.
alert_severity severity
Alert severity level.
std::string rule_name
Name of triggering rule.
std::string name
Alert name/identifier.
alert_labels labels
Identifying labels.
alert_annotations annotations
Descriptive annotations.
Configuration for webhook notifier.
std::chrono::milliseconds retry_delay
Delay between retries.
std::chrono::milliseconds timeout
Request timeout.
bool send_resolved
Send resolved notifications.
size_t max_retries
Maximum retry attempts.
std::string content_type
Content type header.
webhook_config & add_header(const std::string &key, const std::string &value)
Add a custom header.
std::unordered_map< std::string, std::string > headers
Custom headers.