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

Demonstrates how logger_system and monitoring_system can integrate through dependency injection WITHOUT compile-time circular dependency.

Key Points:

  1. Both systems compile independently
  2. Integration happens at runtime via interfaces
  3. No concrete class dependencies
  4. Either system can work standalone or together
// BSD 3-Clause License
// Copyright (c) 2021-2025, šŸ€ā˜€šŸŒ•šŸŒ„ 🌊
// See the LICENSE file in the project root for full license information.
#include <kcenon/common/interfaces/logger_interface.h>
#include <kcenon/common/interfaces/monitoring_interface.h>
#include <iostream>
#include <memory>
#include <chrono>
#include <thread>
using namespace kcenon::monitoring;
using namespace common::interfaces;
class console_logger : public ILogger, public IMonitorable {
private:
std::atomic<int> message_count_{0};
log_level min_level_{log_level::info};
std::shared_ptr<IMonitor> monitor_;
public:
common::VoidResult log(log_level level, const std::string& message) override {
if (level < min_level_) {
return std::monostate{};
}
auto now = std::chrono::system_clock::now();
auto time_t = std::chrono::system_clock::to_time_t(now);
std::tm tm{};
#ifdef _MSC_VER
localtime_s(&tm, &time_t);
#else
localtime_r(&time_t, &tm);
#endif
std::cout << "[" << std::put_time(&tm, "%Y-%m-%d %H:%M:%S") << "] "
<< "[" << to_string(level) << "] "
<< message << std::endl;
// If monitor is injected, record logging metrics
if (monitor_) {
monitor_->record_metric("messages_logged", message_count_.load());
}
return std::monostate{};
}
common::VoidResult log(log_level level, const std::string& message,
const std::string& file, int line,
const std::string& function) override {
std::string detailed_message = message + " [" + file + ":" +
std::to_string(line) + " in " + function + "]";
return log(level, detailed_message);
}
common::VoidResult log(const log_entry& entry) override {
return log(entry.level, entry.message, entry.file, entry.line, entry.function);
}
bool is_enabled(log_level level) const override {
return level >= min_level_;
}
common::VoidResult set_level(log_level level) override {
min_level_ = level;
return std::monostate{};
}
log_level get_level() const override {
return min_level_;
}
common::VoidResult flush() override {
std::cout.flush();
return std::monostate{};
}
// IMonitorable implementation
common::Result<metrics_snapshot> get_monitoring_data() override {
metrics_snapshot snapshot;
snapshot.source_id = "console_logger";
snapshot.add_metric("total_messages", message_count_.load());
snapshot.add_metric("is_enabled", is_enabled(log_level::info) ? 1.0 : 0.0);
return snapshot;
}
common::Result<health_check_result> health_check() override {
result.status = health_status::healthy;
result.message = "Console logger operational";
result.metadata["message_count"] = std::to_string(message_count_.load());
return result;
}
std::string get_component_name() const override {
return "console_logger";
}
// Allow monitor injection
void set_monitor(std::shared_ptr<IMonitor> monitor) {
monitor_ = std::move(monitor);
}
int get_message_count() const {
return message_count_.load();
}
};
std::cout << "\n=== Scenario 1: Standalone Systems ===" << std::endl;
std::cout << "Both systems work independently without each other.\n" << std::endl;
// Logger works alone
auto logger = std::make_shared<console_logger>();
logger->log(log_level::info, "Logger operating standalone");
std::cout << "āœ“ Logger works without monitor\n" << std::endl;
// Monitor works alone
auto monitor = std::make_shared<performance_monitor>();
auto result = monitor->record_metric("standalone_metric", 42.0);
if (std::holds_alternative<std::monostate>(result)) {
std::cout << "āœ“ Monitor works without logger\n" << std::endl;
}
}
std::cout << "\n=== Scenario 2: Logger with Monitor ===" << std::endl;
std::cout << "Logger receives monitor via DI for metrics collection.\n" << std::endl;
auto monitor = std::make_shared<performance_monitor>();
auto logger = std::make_shared<console_logger>();
// Inject monitor into logger
logger->set_monitor(monitor);
// Log some messages
logger->log(log_level::info, "First message with monitoring");
logger->log(log_level::warning, "Second message with monitoring");
logger->log(log_level::error, "Third message with monitoring");
// Check monitor collected metrics
auto metrics_result = monitor->get_metrics();
if (std::holds_alternative<metrics_snapshot>(metrics_result)) {
auto& snapshot = std::get<metrics_snapshot>(metrics_result);
std::cout << "\nāœ“ Monitor collected " << snapshot.metrics.size()
<< " metrics from logger" << std::endl;
for (const auto& metric : snapshot.metrics) {
std::cout << " - " << metric.name << ": " << metric.value << std::endl;
}
}
}
std::cout << "\n\n=== Scenario 3: Monitor with Logger ===" << std::endl;
std::cout << "Monitor can report to logger (via adapter pattern).\n" << std::endl;
auto logger = std::make_shared<console_logger>();
auto monitor = std::make_shared<performance_monitor>();
// Monitor records metrics
monitor->record_metric("cpu_usage", 45.5);
monitor->record_metric("memory_usage", 512.0);
// Check health and log result
auto health_result = monitor->check_health();
if (std::holds_alternative<health_check_result>(health_result)) {
auto& health = std::get<health_check_result>(health_result);
std::string health_msg = "Monitor health: " + to_string(health.status);
logger->log(log_level::info, health_msg);
}
std::cout << "\nāœ“ Monitor can report status to logger" << std::endl;
}
std::cout << "\n\n=== Scenario 4: Bidirectional DI (No Circular Dependency!) ===" << std::endl;
std::cout << "Both systems integrated at RUNTIME without compile-time circular dependency.\n" << std::endl;
// Create both systems
auto logger = std::make_shared<console_logger>();
auto monitor = std::make_shared<performance_monitor>();
// Bidirectional injection
logger->set_monitor(monitor);
// Note: In real logger_system, logger would also be injected into adapter
std::cout << "\nāœ“ Bidirectional dependency injection complete" << std::endl;
std::cout << " Logger -> uses Monitor for metrics" << std::endl;
std::cout << " Monitor <- logs status via Logger\n" << std::endl;
// Simulate application workload
std::cout << "\nSimulating application workload..." << std::endl;
for (int i = 0; i < 10; ++i) {
// Log activity
logger->log(log_level::info, "Processing request " + std::to_string(i));
// Record performance metrics
monitor->record_metric("requests_processed", i + 1);
monitor->record_metric("response_time_ms", 50.0 + (i * 5));
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
// Check both systems' health
std::cout << "\n=== System Health Check ===" << std::endl;
auto logger_health = logger->health_check();
if (std::holds_alternative<health_check_result>(logger_health)) {
auto& health = std::get<health_check_result>(logger_health);
std::cout << "Logger Status: " << to_string(health.status)
<< " - " << health.message << std::endl;
std::cout << " Messages logged: " << logger->get_message_count() << std::endl;
}
auto monitor_health = monitor->check_health();
if (std::holds_alternative<health_check_result>(monitor_health)) {
auto& health = std::get<health_check_result>(monitor_health);
std::cout << "Monitor Status: " << to_string(health.status)
<< " - " << health.message << std::endl;
}
// Get comprehensive metrics
std::cout << "\n=== Collected Metrics ===" << std::endl;
auto logger_metrics = logger->get_monitoring_data();
if (std::holds_alternative<metrics_snapshot>(logger_metrics)) {
auto& snapshot = std::get<metrics_snapshot>(logger_metrics);
std::cout << "Logger Metrics:" << std::endl;
for (const auto& metric : snapshot.metrics) {
std::cout << " " << metric.name << ": " << metric.value << std::endl;
}
}
auto monitor_metrics = monitor->get_metrics();
if (std::holds_alternative<metrics_snapshot>(monitor_metrics)) {
auto& snapshot = std::get<metrics_snapshot>(monitor_metrics);
std::cout << "\nMonitor Metrics:" << std::endl;
for (const auto& metric : snapshot.metrics) {
std::cout << " " << metric.name << ": " << metric.value << std::endl;
}
}
std::cout << "\nāœ“ Both systems fully operational and integrated!" << std::endl;
}
std::cout << "\n\n=== Scenario 5: Runtime Flexibility ===" << std::endl;
std::cout << "Dependencies can be changed at runtime.\n" << std::endl;
auto logger = std::make_shared<console_logger>();
auto monitor1 = std::make_shared<performance_monitor>();
auto monitor2 = std::make_shared<performance_monitor>();
// Start with first monitor
logger->set_monitor(monitor1);
logger->log(log_level::info, "Using monitor 1");
// Switch to second monitor
logger->set_monitor(monitor2);
logger->log(log_level::info, "Switched to monitor 2");
// Remove monitor completely
logger->set_monitor(nullptr);
logger->log(log_level::info, "Operating without monitor");
std::cout << "\nāœ“ Runtime dependency changes work seamlessly" << std::endl;
}
int main() {
std::cout << "╔═══════════════════════════════════════════════════════════╗" << std::endl;
std::cout << "ā•‘ Phase 4: Bidirectional DI Example ā•‘" << std::endl;
std::cout << "ā•‘ Demonstrating Circular Dependency Resolution ā•‘" << std::endl;
std::cout << "ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•" << std::endl;
try {
std::cout << "\n\n╔═══════════════════════════════════════════════════════════╗" << std::endl;
std::cout << "ā•‘ āœ“ ALL SCENARIOS PASSED ā•‘" << std::endl;
std::cout << "ā•‘ ā•‘" << std::endl;
std::cout << "ā•‘ Key Achievement: ā•‘" << std::endl;
std::cout << "ā•‘ • NO compile-time circular dependency ā•‘" << std::endl;
std::cout << "ā•‘ • Runtime bidirectional integration works ā•‘" << std::endl;
std::cout << "ā•‘ • Both systems can operate standalone ā•‘" << std::endl;
std::cout << "ā•‘ • Pure interface-based design ā•‘" << std::endl;
std::cout << "ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•" << std::endl;
return 0;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
}
void demo_standalone_systems()
Scenario 1: Standalone Systems.
void demo_runtime_flexibility()
Scenario 5: Runtime Flexibility.
void demo_bidirectional_integration()
Scenario 4: Bidirectional DI (THE KEY DEMO!)
void demo_logger_with_monitor()
Scenario 2: Logger with Monitor Injection.
void demo_monitor_with_logger()
Scenario 3: Monitor with Logger Injection (via adapter)
Simple console logger implementing ILogger interface.
common::Result< health_check_result > health_check() override
bool is_enabled(log_level level) const override
std::atomic< int > message_count_
common::VoidResult log(log_level level, const std::string &message) override
common::Result< metrics_snapshot > get_monitoring_data() override
std::string get_component_name() const override
common::VoidResult set_level(log_level level) override
common::VoidResult flush() override
std::shared_ptr< IMonitor > monitor_
log_level get_level() const override
void set_monitor(std::shared_ptr< IMonitor > monitor)
auto to_string(plugin_load_error error) -> std::string
Convert plugin_load_error to string.
Performance monitoring and profiling implementation.
Result of a health check operation.
std::unordered_map< std::string, std::string > metadata
Basic metric structure for interface compatibility.
std::variant< double, int64_t, std::string > value
Complete snapshot of metrics at a point in time.
void add_metric(const std::string &name, double value)
Add a metric to the snapshot.