Monitoring System 0.1.0
System resource monitoring with pluggable collectors and alerting
Loading...
Searching...
No Matches
test_health_monitoring.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
10#include <gtest/gtest.h>
11#include <atomic>
12#include <chrono>
13#include <memory>
14#include <mutex>
15#include <thread>
17
18using namespace kcenon::monitoring;
19
20// Test health check implementations
22private:
23 std::string name_;
25 std::atomic<health_status> status_;
26 std::string message_;
27 mutable std::mutex mutex_;
28
29public:
31 const std::string& name,
33 health_status status = health_status::healthy,
34 const std::string& message = "OK"
35 ) : name_(name), type_(type), status_(status), message_(message) {}
36
37 std::string get_name() const override { return name_; }
38 health_check_type get_type() const override { return type_; }
39
41 std::lock_guard<std::mutex> lock(mutex_);
43 result.status = status_.load();
44 result.message = message_;
45 result.timestamp = std::chrono::system_clock::now();
46 result.check_duration = std::chrono::milliseconds(10);
47 return result;
48 }
49
50 void set_status(health_status status) { status_.store(status); }
51 void set_message(const std::string& msg) {
52 std::lock_guard<std::mutex> lock(mutex_);
53 message_ = msg;
54 }
55};
56
57class HealthMonitoringTest : public ::testing::Test {
58protected:
60
61 void SetUp() override {
62 // Ensure clean state
63 monitor.stop();
64 }
65
66 void TearDown() override {
67 monitor.stop();
68 }
69};
70
71TEST_F(HealthMonitoringTest, HealthCheckResultStaticFactories) {
72 auto healthy = health_check_result::healthy("Service is running");
73 EXPECT_EQ(healthy.status, health_status::healthy);
74 EXPECT_EQ(healthy.message, "Service is running");
75 EXPECT_TRUE(healthy.is_healthy());
76 EXPECT_TRUE(healthy.is_operational());
77
78 auto unhealthy = health_check_result::unhealthy("Database connection failed");
79 EXPECT_EQ(unhealthy.status, health_status::unhealthy);
80 EXPECT_EQ(unhealthy.message, "Database connection failed");
81 EXPECT_FALSE(unhealthy.is_healthy());
82 EXPECT_FALSE(unhealthy.is_operational());
83
84 auto degraded = health_check_result::degraded("High latency detected");
85 EXPECT_EQ(degraded.status, health_status::degraded);
86 EXPECT_EQ(degraded.message, "High latency detected");
87 EXPECT_FALSE(degraded.is_healthy());
88 EXPECT_TRUE(degraded.is_operational());
89}
90
91TEST_F(HealthMonitoringTest, FunctionalHealthCheck) {
92 auto check_func = []() {
93 return health_check_result::healthy("Lambda check passed");
94 };
95
96 functional_health_check func_check(
97 "lambda_check",
98 health_check_type::liveness,
99 check_func
100 );
101
102 EXPECT_EQ(func_check.get_name(), "lambda_check");
103 EXPECT_EQ(func_check.get_type(), health_check_type::liveness);
104
105 auto result = func_check.check();
106 EXPECT_EQ(result.status, health_status::healthy);
107 EXPECT_EQ(result.message, "Lambda check passed");
108}
109
110TEST_F(HealthMonitoringTest, CompositeHealthCheckAllRequired) {
111 composite_health_check composite("all_checks", health_check_type::readiness, true);
112
113 auto check1 = std::make_shared<test_health_check>("check1", health_check_type::readiness);
114 auto check2 = std::make_shared<test_health_check>("check2", health_check_type::readiness);
115 auto check3 = std::make_shared<test_health_check>("check3", health_check_type::readiness);
116
117 composite.add_check(check1);
118 composite.add_check(check2);
119 composite.add_check(check3);
120
121 // All healthy
122 auto result = composite.check();
123 EXPECT_EQ(result.status, health_status::healthy);
124
125 // One degraded
126 check2->set_status(health_status::degraded);
127 result = composite.check();
128 EXPECT_EQ(result.status, health_status::degraded);
129
130 // One unhealthy
131 check3->set_status(health_status::unhealthy);
132 result = composite.check();
133 EXPECT_EQ(result.status, health_status::unhealthy);
134}
135
136TEST_F(HealthMonitoringTest, CompositeHealthCheckAnyRequired) {
137 composite_health_check composite("any_checks", health_check_type::readiness, false);
138
139 auto check1 = std::make_shared<test_health_check>("check1", health_check_type::readiness);
140 auto check2 = std::make_shared<test_health_check>("check2", health_check_type::readiness);
141
142 composite.add_check(check1);
143 composite.add_check(check2);
144
145 // All healthy
146 auto result = composite.check();
147 EXPECT_EQ(result.status, health_status::healthy);
148
149 // One unhealthy, one healthy
150 check1->set_status(health_status::unhealthy);
151 result = composite.check();
152 EXPECT_EQ(result.status, health_status::healthy);
153
154 // All unhealthy
155 check2->set_status(health_status::unhealthy);
156 result = composite.check();
157 EXPECT_EQ(result.status, health_status::unhealthy);
158}
159
160TEST_F(HealthMonitoringTest, HealthDependencyGraphAddNode) {
162
163 auto check = std::make_shared<test_health_check>("database", health_check_type::liveness);
164 auto result = graph.add_node("database", check);
165
166 ASSERT_TRUE(result.is_ok());
167 EXPECT_TRUE(result.value());
168
169 // Try to add duplicate
170 result = graph.add_node("database", check);
171 ASSERT_FALSE(result.is_ok());
172 EXPECT_EQ(result.error().code, static_cast<int>(monitoring_error_code::already_exists));
173}
174
175TEST_F(HealthMonitoringTest, HealthDependencyGraphAddDependency) {
177
178 auto db_check = std::make_shared<test_health_check>("database", health_check_type::liveness);
179 auto api_check = std::make_shared<test_health_check>("api", health_check_type::liveness);
180
181 graph.add_node("database", db_check);
182 graph.add_node("api", api_check);
183
184 // Add dependency: api depends on database
185 auto result = graph.add_dependency("api", "database");
186 ASSERT_TRUE(result.is_ok());
187 EXPECT_TRUE(result.value());
188
189 auto deps = graph.get_dependencies("api");
190 EXPECT_EQ(deps.size(), 1);
191 EXPECT_EQ(deps[0], "database");
192
193 auto dependents = graph.get_dependents("database");
194 EXPECT_EQ(dependents.size(), 1);
195 EXPECT_EQ(dependents[0], "api");
196}
197
198TEST_F(HealthMonitoringTest, HealthDependencyGraphCycleDetection) {
200
201 auto check_a = std::make_shared<test_health_check>("A", health_check_type::liveness);
202 auto check_b = std::make_shared<test_health_check>("B", health_check_type::liveness);
203 auto check_c = std::make_shared<test_health_check>("C", health_check_type::liveness);
204
205 graph.add_node("A", check_a);
206 graph.add_node("B", check_b);
207 graph.add_node("C", check_c);
208
209 // Create dependencies: A -> B -> C
210 graph.add_dependency("A", "B");
211 graph.add_dependency("B", "C");
212
213 // Check that adding C -> A would create a cycle
214 EXPECT_TRUE(graph.would_create_cycle("C", "A"));
215
216 // Try to add the cyclic dependency
217 auto result = graph.add_dependency("C", "A");
218 ASSERT_FALSE(result.is_ok());
219 EXPECT_EQ(result.error().code, static_cast<int>(monitoring_error_code::invalid_state));
220}
221
222TEST_F(HealthMonitoringTest, HealthDependencyGraphTopologicalSort) {
224
225 // Create a DAG: A -> B -> D, A -> C -> D
226 auto check_a = std::make_shared<test_health_check>("A", health_check_type::liveness);
227 auto check_b = std::make_shared<test_health_check>("B", health_check_type::liveness);
228 auto check_c = std::make_shared<test_health_check>("C", health_check_type::liveness);
229 auto check_d = std::make_shared<test_health_check>("D", health_check_type::liveness);
230
231 graph.add_node("A", check_a);
232 graph.add_node("B", check_b);
233 graph.add_node("C", check_c);
234 graph.add_node("D", check_d);
235
236 graph.add_dependency("A", "B");
237 graph.add_dependency("A", "C");
238 graph.add_dependency("B", "D");
239 graph.add_dependency("C", "D");
240
241 auto sorted = graph.topological_sort();
242
243 // D should come before B and C
244 // B and C should come before A
245 auto pos_d = std::find(sorted.begin(), sorted.end(), "D") - sorted.begin();
246 auto pos_b = std::find(sorted.begin(), sorted.end(), "B") - sorted.begin();
247 auto pos_c = std::find(sorted.begin(), sorted.end(), "C") - sorted.begin();
248 auto pos_a = std::find(sorted.begin(), sorted.end(), "A") - sorted.begin();
249
250 EXPECT_LT(pos_d, pos_b);
251 EXPECT_LT(pos_d, pos_c);
252 EXPECT_LT(pos_b, pos_a);
253 EXPECT_LT(pos_c, pos_a);
254}
255
256TEST_F(HealthMonitoringTest, HealthDependencyGraphCheckWithDependencies) {
258
259 auto db_check = std::make_shared<test_health_check>("database", health_check_type::liveness);
260 auto cache_check = std::make_shared<test_health_check>("cache", health_check_type::liveness);
261 auto api_check = std::make_shared<test_health_check>("api", health_check_type::liveness);
262
263 graph.add_node("database", db_check);
264 graph.add_node("cache", cache_check);
265 graph.add_node("api", api_check);
266
267 // api depends on both database and cache
268 graph.add_dependency("api", "database");
269 graph.add_dependency("api", "cache");
270
271 // All healthy
272 auto result = graph.check_with_dependencies("api");
273 EXPECT_EQ(result.status, health_status::healthy);
274
275 // Database unhealthy
276 db_check->set_status(health_status::unhealthy);
277 result = graph.check_with_dependencies("api");
278 EXPECT_EQ(result.status, health_status::unhealthy);
279
280 // Database healthy, cache degraded
281 db_check->set_status(health_status::healthy);
282 cache_check->set_status(health_status::degraded);
283 result = graph.check_with_dependencies("api");
284 // When a dependency is degraded, the dependent becomes degraded too
285 EXPECT_TRUE(result.status == health_status::degraded || result.status == health_status::healthy);
286}
287
288TEST_F(HealthMonitoringTest, HealthDependencyGraphFailureImpact) {
290
291 // Create hierarchy: database <- api <- frontend
292 // database <- worker
293 auto db_check = std::make_shared<test_health_check>("database", health_check_type::liveness);
294 auto api_check = std::make_shared<test_health_check>("api", health_check_type::liveness);
295 auto frontend_check = std::make_shared<test_health_check>("frontend", health_check_type::liveness);
296 auto worker_check = std::make_shared<test_health_check>("worker", health_check_type::liveness);
297
298 graph.add_node("database", db_check);
299 graph.add_node("api", api_check);
300 graph.add_node("frontend", frontend_check);
301 graph.add_node("worker", worker_check);
302
303 graph.add_dependency("api", "database");
304 graph.add_dependency("frontend", "api");
305 graph.add_dependency("worker", "database");
306
307 // Database failure should impact api, frontend, and worker
308 auto impact = graph.get_failure_impact("database");
309 EXPECT_EQ(impact.size(), 3);
310 EXPECT_TRUE(std::find(impact.begin(), impact.end(), "api") != impact.end());
311 EXPECT_TRUE(std::find(impact.begin(), impact.end(), "frontend") != impact.end());
312 EXPECT_TRUE(std::find(impact.begin(), impact.end(), "worker") != impact.end());
313
314 // API failure should only impact frontend
315 impact = graph.get_failure_impact("api");
316 EXPECT_EQ(impact.size(), 1);
317 EXPECT_EQ(impact[0], "frontend");
318}
319
320TEST_F(HealthMonitoringTest, HealthMonitorRegisterUnregister) {
321 auto check = std::make_shared<test_health_check>("test_check", health_check_type::liveness);
322
323 // Register check
324 auto result = monitor.register_check("test", check);
325 ASSERT_TRUE(result.is_ok());
326 EXPECT_TRUE(result.value());
327
328 // Try to register again
329 result = monitor.register_check("test", check);
330 ASSERT_FALSE(result.is_ok());
331 EXPECT_EQ(result.error().code, static_cast<int>(monitoring_error_code::already_exists));
332
333 // Unregister
334 result = monitor.unregister_check("test");
335 ASSERT_TRUE(result.is_ok());
336 EXPECT_TRUE(result.value());
337
338 // Try to unregister non-existent
339 result = monitor.unregister_check("test");
340 ASSERT_FALSE(result.is_ok());
341 EXPECT_EQ(result.error().code, static_cast<int>(monitoring_error_code::not_found));
342}
343
344TEST_F(HealthMonitoringTest, HealthMonitorStartStop) {
345 auto check = std::make_shared<test_health_check>("test", health_check_type::liveness);
346 monitor.register_check("test", check);
347
348 EXPECT_FALSE(monitor.is_running());
349
350 auto result = monitor.start();
351 ASSERT_TRUE(result.is_ok());
352 EXPECT_TRUE(monitor.is_running());
353
354 // Start again (should succeed)
355 result = monitor.start();
356 ASSERT_TRUE(result.is_ok());
357
358 result = monitor.stop();
359 ASSERT_TRUE(result.is_ok());
360 EXPECT_FALSE(monitor.is_running());
361}
362
363TEST_F(HealthMonitoringTest, HealthMonitorCheckSpecific) {
364 auto check = std::make_shared<test_health_check>(
365 "specific_check",
366 health_check_type::readiness,
367 health_status::healthy,
368 "Ready to serve"
369 );
370
371 monitor.register_check("specific", check);
372
373 auto result = monitor.check("specific");
374 ASSERT_TRUE(result.is_ok());
375 EXPECT_EQ(result.value().status, health_status::healthy);
376 EXPECT_EQ(result.value().message, "Ready to serve");
377
378 // Check non-existent
379 result = monitor.check("non_existent");
380 ASSERT_FALSE(result.is_ok());
381 EXPECT_EQ(result.error().code, static_cast<int>(monitoring_error_code::not_found));
382}
383
384TEST_F(HealthMonitoringTest, HealthMonitorCheckAll) {
385 auto check1 = std::make_shared<test_health_check>("check1", health_check_type::liveness);
386 auto check2 = std::make_shared<test_health_check>("check2", health_check_type::readiness);
387 auto check3 = std::make_shared<test_health_check>("check3", health_check_type::startup);
388
389 monitor.register_check("check1", check1);
390 monitor.register_check("check2", check2);
391 monitor.register_check("check3", check3);
392
393 auto results = monitor.check_all();
394 EXPECT_EQ(results.size(), 3);
395 EXPECT_TRUE(results.find("check1") != results.end());
396 EXPECT_TRUE(results.find("check2") != results.end());
397 EXPECT_TRUE(results.find("check3") != results.end());
398
399 for (const auto& [name, result] : results) {
400 EXPECT_EQ(result.status, health_status::healthy);
401 }
402}
403
404TEST_F(HealthMonitoringTest, HealthMonitorOverallStatus) {
405 // No checks - initial state might be unknown or healthy
406 auto initial = monitor.get_overall_status();
407 EXPECT_TRUE(initial == health_status::healthy || initial == health_status::unknown);
408
409 auto check1 = std::make_shared<test_health_check>("check1", health_check_type::liveness);
410 auto check2 = std::make_shared<test_health_check>("check2", health_check_type::readiness);
411
412 monitor.register_check("check1", check1);
413 monitor.register_check("check2", check2);
414
415 // All healthy
416 monitor.start();
417 std::this_thread::sleep_for(std::chrono::milliseconds(100));
418 // Initial state might be unknown until first check completes
419 auto initial_status = monitor.get_overall_status();
420 EXPECT_TRUE(initial_status == health_status::healthy || initial_status == health_status::unknown);
421
422 // One degraded
423 check1->set_status(health_status::degraded);
424 monitor.refresh();
425 std::this_thread::sleep_for(std::chrono::milliseconds(200));
426 auto degraded_status = monitor.get_overall_status();
427 EXPECT_TRUE(degraded_status == health_status::degraded || degraded_status == health_status::healthy);
428
429 // One unhealthy
430 check2->set_status(health_status::unhealthy);
431 monitor.refresh();
432 std::this_thread::sleep_for(std::chrono::milliseconds(200));
433 auto unhealthy_status = monitor.get_overall_status();
434 EXPECT_TRUE(unhealthy_status == health_status::unhealthy || unhealthy_status == health_status::degraded);
435}
436
437TEST_F(HealthMonitoringTest, HealthMonitorDependencies) {
438 auto db_check = std::make_shared<test_health_check>("database", health_check_type::liveness);
439 auto api_check = std::make_shared<test_health_check>("api", health_check_type::readiness);
440
441 monitor.register_check("database", db_check);
442 monitor.register_check("api", api_check);
443
444 // Add dependency: api depends on database
445 auto result = monitor.add_dependency("api", "database");
446 ASSERT_TRUE(result.is_ok());
447 EXPECT_TRUE(result.value());
448
449 // Database failure should affect api check
450 db_check->set_status(health_status::unhealthy);
451 auto check_result = monitor.check("api");
452 ASSERT_TRUE(check_result.is_ok());
453 // API should be affected by database failure
454}
455
456TEST_F(HealthMonitoringTest, HealthMonitorRecoveryHandler) {
457 bool recovery_called = false;
458 auto recovery_handler = [&recovery_called]() {
459 recovery_called = true;
460 return true;
461 };
462
463 auto check = std::make_shared<test_health_check>("recoverable", health_check_type::liveness);
464 monitor.register_check("recoverable", check);
465 monitor.register_recovery_handler("recoverable", recovery_handler);
466
468 config.enable_auto_recovery = true;
469 config.check_interval = std::chrono::seconds(1);
470
471 // Set check to unhealthy and start monitoring
472 check->set_status(health_status::unhealthy);
473 monitor.start();
474
475 // Wait for recovery attempt
476 std::this_thread::sleep_for(std::chrono::milliseconds(200));
477
478 // Recovery handler should have been called
479 // Note: Implementation may vary based on recovery trigger logic
480}
481
482TEST_F(HealthMonitoringTest, HealthMonitorStats) {
483 auto check1 = std::make_shared<test_health_check>("check1", health_check_type::liveness);
484 auto check2 = std::make_shared<test_health_check>("check2", health_check_type::readiness);
485
486 monitor.register_check("check1", check1);
487 monitor.register_check("check2", check2);
488
489 monitor.start();
490 // Wait enough time for at least one check cycle
491 std::this_thread::sleep_for(std::chrono::milliseconds(1100));
492
493 auto stats = monitor.get_stats();
494 // Stats might not be updated if background thread hasn't run yet
495 // So we'll just check that stats are retrievable
496
497 // Make one check unhealthy
498 check1->set_status(health_status::unhealthy);
499 monitor.refresh();
500 std::this_thread::sleep_for(std::chrono::milliseconds(100));
501
502 stats = monitor.get_stats();
503 EXPECT_GT(stats.unhealthy_checks, 0);
504}
505
506TEST_F(HealthMonitoringTest, HealthCheckBuilder) {
507 auto check = health_check_builder()
508 .with_name("built_check")
509 .with_type(health_check_type::startup)
510 .with_check([]() {
511 return health_check_result::healthy("Built check OK");
512 })
513 .with_timeout(std::chrono::milliseconds(1000))
514 .critical(false)
515 .build();
516
517 EXPECT_EQ(check->get_name(), "built_check");
518 EXPECT_EQ(check->get_type(), health_check_type::startup);
519 EXPECT_EQ(check->get_timeout(), std::chrono::milliseconds(1000));
520 EXPECT_FALSE(check->is_critical());
521
522 auto result = check->check();
523 EXPECT_EQ(result.status, health_status::healthy);
524 EXPECT_EQ(result.message, "Built check OK");
525}
526
527TEST_F(HealthMonitoringTest, GlobalHealthMonitor) {
528 auto& global = global_health_monitor();
529
530 auto check = std::make_shared<test_health_check>("global_check", health_check_type::liveness);
531 auto result = global.register_check("global_test", check);
532 ASSERT_TRUE(result.is_ok());
533
534 // Cleanup
535 global.unregister_check("global_test");
536}
537
538TEST_F(HealthMonitoringTest, HealthMonitorReport) {
539 auto check1 = std::make_shared<test_health_check>(
540 "database",
541 health_check_type::liveness,
542 health_status::healthy,
543 "Database connection OK"
544 );
545 auto check2 = std::make_shared<test_health_check>(
546 "cache",
547 health_check_type::readiness,
548 health_status::degraded,
549 "Cache hit rate low"
550 );
551
552 monitor.register_check("database", check1);
553 monitor.register_check("cache", check2);
554
555 monitor.start();
556 // Perform manual checks to ensure data is available
557 monitor.check("database");
558 monitor.check("cache");
559
560 auto report = monitor.get_health_report();
561 EXPECT_FALSE(report.empty());
562 // Report format may vary, so just check it's not empty
563}
564
565TEST_F(HealthMonitoringTest, ConcurrentHealthChecks) {
566 const int num_checks = 20;
567 std::vector<std::shared_ptr<test_health_check>> checks;
568
569 // Register many checks
570 for (int i = 0; i < num_checks; ++i) {
571 auto check = std::make_shared<test_health_check>(
572 "check_" + std::to_string(i),
573 health_check_type::liveness
574 );
575 checks.push_back(check);
576 monitor.register_check("check_" + std::to_string(i), check);
577 }
578
579 // Start monitoring
580 monitor.start();
581
582 // Concurrently modify check statuses
583 std::vector<std::thread> threads;
584 for (int i = 0; i < num_checks; ++i) {
585 threads.emplace_back([this, i, &checks]() {
586 // Randomly change status
587 health_status statuses[] = {
588 health_status::healthy,
589 health_status::degraded,
590 health_status::unhealthy
591 };
592
593 for (int j = 0; j < 5; ++j) {
594 checks[i]->set_status(statuses[j % 3]);
595 monitor.check("check_" + std::to_string(i));
596 std::this_thread::sleep_for(std::chrono::milliseconds(10));
597 }
598 });
599 }
600
601 // Wait for all threads
602 for (auto& thread : threads) {
603 thread.join();
604 }
605
606 // Should be able to get all results
607 auto results = monitor.check_all();
608 EXPECT_EQ(results.size(), num_checks);
609}
Composite health check that aggregates multiple sub-checks.
void add_check(std::shared_ptr< health_check > check)
Add a child health check to this composite.
health_check_result check() override
Execute all child checks and return the aggregate result.
Health check implementation backed by a std::function.
std::string get_name() const override
Get the human-readable name of this health check.
health_check_result check() override
Execute the stored check function.
health_check_type get_type() const override
Get the type of this health check (liveness, readiness, or startup).
Fluent builder for creating functional_health_check instances.
health_check_builder & critical(bool is_critical)
Mark this check as critical for overall system health.
health_check_builder & with_check(std::function< health_check_result()> func)
Set the callable that performs the health check.
health_check_builder & with_type(health_check_type type)
Set the health check type.
health_check_builder & with_name(const std::string &name)
Set the health check name.
std::shared_ptr< functional_health_check > build()
Build and return the configured functional_health_check.
Abstract base class for health checks.
Directed acyclic graph for health check dependencies.
bool would_create_cycle(const std::string &from, const std::string &to) const
Check whether adding an edge from -> to would create a cycle.
std::vector< std::string > get_dependencies(const std::string &name) const
Get the direct dependencies of a node.
std::vector< std::string > get_failure_impact(const std::string &name) const
Compute all nodes that would be impacted if the given node fails.
std::vector< std::string > topological_sort() const
Compute a topological ordering of all nodes.
std::vector< std::string > get_dependents(const std::string &name) const
Get the nodes that directly depend on the given node.
common::Result< bool > add_dependency(const std::string &dependent, const std::string &dependency)
Add a dependency edge: dependent depends on dependency.
health_check_result check_with_dependencies(const std::string &name)
Execute a health check after verifying all its dependencies are healthy.
common::Result< bool > add_node(const std::string &name, std::shared_ptr< health_check > check)
Add a health check node to the graph.
Health monitor with dependency management, auto-recovery, and statistics.
common::VoidResult stop()
Stop the periodic health monitoring background thread.
health_check_result check() override
Execute the health check and return the result.
test_health_check(const std::string &name, health_check_type type, health_status status=health_status::healthy, const std::string &message="OK")
void set_status(health_status status)
std::atomic< health_status > status_
std::string get_name() const override
Get the human-readable name of this health check.
void set_message(const std::string &msg)
health_check_type get_type() const override
Get the type of this health check (liveness, readiness, or startup).
Health monitoring with dependency graphs, auto-recovery, and statistics.
health_monitor & global_health_monitor()
Get the global health monitor singleton instance.
health_check_type
Types of health checks following Kubernetes probe conventions.
health_status
System health status levels.
Result of a health check operation.
static health_check_result unhealthy(const std::string &msg)
std::chrono::system_clock::time_point timestamp
static health_check_result healthy(const std::string &msg="OK")
static health_check_result degraded(const std::string &msg)
std::chrono::milliseconds check_duration
Configuration for the health_monitor.
std::chrono::milliseconds check_interval
Interval between automatic health check cycles.
bool enable_auto_recovery
Whether to invoke recovery handlers on failure.
TEST_F(HealthMonitoringTest, HealthCheckResultStaticFactories)