Logger System 0.1.3
High-performance C++20 thread-safe logging system with asynchronous capabilities
Loading...
Searching...
No Matches
di_pattern_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
15#include <kcenon/common/interfaces/logger_interface.h>
16#include <kcenon/common/interfaces/monitoring_interface.h>
17#include <iostream>
18#include <memory>
19#include <thread>
20#include <chrono>
21#include <mutex>
22#include <unordered_map>
23#include <sstream>
24
25using namespace kcenon::logger;
26namespace ci = kcenon::common::interfaces;
27
28namespace {
29
30kcenon::common::VoidResult make_adapter_error(const std::string& message) {
31 return kcenon::common::VoidResult(kcenon::common::error_info{1, message, "logger_system"});
32}
33
34class logger_interface_adapter : public ci::ILogger {
35public:
36 explicit logger_interface_adapter(std::shared_ptr<logger> logger)
37 : logger_(std::move(logger)) {}
38
39 kcenon::common::VoidResult log(ci::log_level level, const std::string& message) override {
40 if (!logger_) {
41 return make_adapter_error("Logger not initialized");
42 }
43 return logger_->log(level, message);
44 }
45
46 kcenon::common::VoidResult log(ci::log_level level,
47 std::string_view message,
48 const kcenon::common::source_location& loc) override {
49 if (!logger_) {
50 return make_adapter_error("Logger not initialized");
51 }
52 return logger_->log(level, message, loc);
53 }
54
55 kcenon::common::VoidResult log(const ci::log_entry& entry) override {
56 if (!logger_) {
57 return make_adapter_error("Logger not initialized");
58 }
59 return logger_->log(entry);
60 }
61
62 bool is_enabled(ci::log_level level) const override {
63 return logger_ && logger_->is_enabled(level);
64 }
65
66 kcenon::common::VoidResult set_level(ci::log_level level) override {
67 if (!logger_) {
68 return make_adapter_error("Logger not initialized");
69 }
70 return logger_->set_level(level);
71 }
72
73 ci::log_level get_level() const override {
74 if (!logger_) {
75 return ci::log_level::off;
76 }
77 return logger_->get_level();
78 }
79
80 kcenon::common::VoidResult flush() override {
81 if (!logger_) {
82 return make_adapter_error("Logger not initialized");
83 }
84 logger_->flush();
85 return kcenon::common::ok();
86 }
87
88private:
89 std::shared_ptr<logger> logger_;
90};
91
92} // namespace
93
100class example_monitor : public ci::IMonitor {
101private:
102 std::unordered_map<std::string, double> metrics_;
103 mutable std::mutex metrics_mutex_;
104 size_t metric_count_ = 0;
105
106public:
108 const std::string& name,
109 double value) override
110 {
111 std::lock_guard<std::mutex> lock(metrics_mutex_);
112 metrics_[name] = value;
114
115 std::cout << "[Monitor] Recorded metric: " << name
116 << " = " << value << std::endl;
117
118 return kcenon::common::ok();
119 }
120
122 const std::string& name,
123 double value,
124 const std::unordered_map<std::string, std::string>& tags) override
125 {
126 std::lock_guard<std::mutex> lock(metrics_mutex_);
127
128 // Create tagged metric name
129 std::string tagged_name = name;
130 for (const auto& [key, val] : tags) {
131 tagged_name += "." + key + ":" + val;
132 }
133
134 metrics_[tagged_name] = value;
136
137 std::cout << "[Monitor] Recorded tagged metric: " << tagged_name
138 << " = " << value << std::endl;
139
140 return kcenon::common::ok();
141 }
142
144 std::lock_guard<std::mutex> lock(metrics_mutex_);
145
146 ci::metrics_snapshot snapshot;
147 snapshot.source_id = "example_monitor";
148 snapshot.capture_time = std::chrono::system_clock::now();
149
150 for (const auto& [name, value] : metrics_) {
151 snapshot.add_metric(name, value);
152 }
153
154 return kcenon::common::Result<ci::metrics_snapshot>::ok(std::move(snapshot));
155 }
156
158 ci::health_check_result result;
159 result.timestamp = std::chrono::system_clock::now();
160 result.status = ci::health_status::healthy;
161 result.message = "Example monitor operational";
162 result.metadata["metrics_count"] = std::to_string(metric_count_);
163
165 }
166
168 std::lock_guard<std::mutex> lock(metrics_mutex_);
169 metrics_.clear();
170 metric_count_ = 0;
171
172 std::cout << "[Monitor] Metrics reset" << std::endl;
173 return kcenon::common::ok();
174 }
175
176 size_t get_metric_count() const {
177 std::lock_guard<std::mutex> lock(metrics_mutex_);
178 return metric_count_;
179 }
180};
181
186 std::cout << "\n=== Example 1: Basic Dependency Injection ===" << std::endl;
187
188 // Step 1: Create monitor instance (can be any IMonitor implementation)
189 auto monitor = std::make_shared<example_monitor>();
190
191 // Step 2: Inject monitor into logger via builder pattern
192 auto logger_result = logger_builder()
193 .with_async(false)
194 .with_monitoring(monitor) // DI: Inject monitor interface
195 .with_min_level(ci::log_level::debug)
196 .build();
197
198 if (!logger_result) {
199 std::cerr << "Failed to create logger: "
200 << logger_result.error_message() << std::endl;
201 return;
202 }
203
204 auto logger_instance = std::shared_ptr<logger>(std::move(logger_result.value()));
205
206 // Step 3: Use logger - metrics are automatically recorded to monitor
207 logger_instance->log(ci::log_level::info, "Application started");
208 logger_instance->log(ci::log_level::debug, "Debug message");
209 logger_instance->log(ci::log_level::warning, "Warning message");
210
211 // Step 4: Verify metrics were recorded
212 std::cout << "\nMonitor collected " << monitor->get_metric_count()
213 << " metrics" << std::endl;
214
215 // Step 5: Get health status from logger (IMonitorable interface)
216 // TODO: Implement health_check() in logger class (Phase 2.2)
217 // auto health = logger_instance->health_check();
218 // if (kcenon::common::is_ok(health)) {
219 // std::cout << "Logger health: "
220 // << ci::to_string(kcenon::common::get_value(health).status) << std::endl;
221 // }
222 std::cout << "Logger health: [health_check() not yet implemented]" << std::endl;
223}
224
229 std::cout << "\n=== Example 2: Optional Monitor (No Monitor) ===" << std::endl;
230
231 // Build logger without monitor - demonstrates optional dependency
232 auto logger_result = logger_builder()
233 .with_async(false)
234 .with_min_level(ci::log_level::info)
235 .build();
236
237 if (!logger_result) {
238 std::cerr << "Failed to create logger" << std::endl;
239 return;
240 }
241
242 auto logger_instance = std::shared_ptr<logger>(std::move(logger_result.value()));
243
244 // Logger works fine without monitor
245 logger_instance->log(ci::log_level::info, "Operating without monitor");
246 logger_instance->log(ci::log_level::warning, "Warning without monitoring");
247
248 std::cout << "Logger operates successfully without monitor (DI optional)" << std::endl;
249}
250
255 std::cout << "\n=== Example 3: Runtime Monitor Injection ===" << std::endl;
256
257 // Create logger without monitor initially
258 auto logger_result = logger_builder()
259 .with_async(false)
260 .build();
261
262 if (!logger_result) {
263 std::cerr << "Failed to create logger" << std::endl;
264 return;
265 }
266
267 auto logger_instance = std::shared_ptr<logger>(std::move(logger_result.value()));
268
269 std::cout << "Phase 1: Operating without monitor" << std::endl;
270 logger_instance->log(ci::log_level::info, "Message 1 - no monitoring");
271
272 // Inject monitor at runtime
273 // TODO: Implement set_monitor() in logger class (Phase 2.2)
274 auto monitor = std::make_unique<example_monitor>();
275 auto* monitor_ref = monitor.get();
276 // logger_instance->set_monitor(std::move(monitor));
277
278 std::cout << "\nPhase 2: Monitor injected at runtime [set_monitor() not yet implemented]" << std::endl;
279 logger_instance->log(ci::log_level::info, "Message 2 - with monitoring");
280 logger_instance->log(ci::log_level::info, "Message 3 - with monitoring");
281
282 std::cout << "\nMonitor recorded " << monitor_ref->get_metric_count()
283 << " metrics (only from Phase 2)" << std::endl;
284}
285
290 std::cout << "\n=== Example 4: Monitor Swapping ===" << std::endl;
291
292 auto logger_result = logger_builder()
293 .with_async(false)
294 .build();
295
296 if (!logger_result) return;
297 auto logger_instance = std::shared_ptr<logger>(std::move(logger_result.value()));
298
299 // TODO: Implement set_monitor() in logger class (Phase 2.2)
300 auto monitor1 = std::make_unique<example_monitor>();
301 auto* monitor1_ref = monitor1.get();
302 // logger_instance->set_monitor(std::move(monitor1));
303
304 std::cout << "Using Monitor 1 [set_monitor() not yet implemented]" << std::endl;
305 logger_instance->log(ci::log_level::info, "Message to Monitor 1");
306 logger_instance->log(ci::log_level::info, "Another message to Monitor 1");
307
308 size_t monitor1_metrics = monitor1_ref->get_metric_count();
309
310 // Swap monitors at runtime
311 auto monitor2 = std::make_unique<example_monitor>();
312 auto* monitor2_ref = monitor2.get();
313 // logger_instance->set_monitor(std::move(monitor2));
314
315 std::cout << "\nSwapped to Monitor 2 [set_monitor() not yet implemented]" << std::endl;
316 logger_instance->log(ci::log_level::info, "Message to Monitor 2");
317
318 size_t monitor2_metrics = monitor2_ref->get_metric_count();
319
320 std::cout << "\nMonitor 1 metrics before swap: " << monitor1_metrics << std::endl;
321 std::cout << "Monitor 2 metrics after swap: " << monitor2_metrics << std::endl;
322}
323
327void use_logger_via_interface(std::shared_ptr<ci::ILogger> logger) {
328 // This function only depends on ILogger interface
329 // Can work with any logger implementation
330
331 // Use std::string explicitly to avoid ambiguity between log overloads
332 logger->log(ci::log_level::info, std::string("Used via interface - loose coupling!"));
333
334 // Check if logger supports monitoring (IMonitorable)
335 if (auto monitorable = std::dynamic_pointer_cast<ci::IMonitorable>(logger)) {
336 auto data = monitorable->get_monitoring_data();
337 if (kcenon::common::is_ok(data)) {
338 std::cout << "Logger provides monitoring data from "
339 << kcenon::common::get_value(data).source_id << std::endl;
340 }
341 }
342}
343
345 std::cout << "\n=== Example 5: Interface-Based Usage ===" << std::endl;
346
347 auto monitor = std::make_shared<example_monitor>();
348 auto logger_result = logger_builder()
349 .with_async(false)
350 .with_monitoring(monitor)
351 .build();
352
353 if (!logger_result) return;
354
355 // Use logger through ILogger interface pointer
356 auto logger_instance = std::shared_ptr<logger>(std::move(logger_result.value()));
357 auto logger_adapter = std::make_shared<logger_interface_adapter>(logger_instance);
358 std::shared_ptr<ci::ILogger> logger_interface = logger_adapter;
359
360 use_logger_via_interface(logger_interface);
361
362 std::cout << "Successfully used logger through interface abstraction" << std::endl;
363}
364
365int main() {
366 std::cout << "==================================================" << std::endl;
367 std::cout << "Logger System - Dependency Injection Pattern Demo" << std::endl;
368 std::cout << "Phase 4: DI Pattern Implementation Examples" << std::endl;
369 std::cout << "==================================================" << std::endl;
370
371 try {
377
378 std::cout << "\n==================================================" << std::endl;
379 std::cout << "All DI pattern examples completed successfully!" << std::endl;
380 std::cout << "==================================================" << std::endl;
381
382 } catch (const std::exception& e) {
383 std::cerr << "Error: " << e.what() << std::endl;
384 return 1;
385 }
386
387 return 0;
388}
Example monitor implementation demonstrating IMonitor interface.
kcenon::common::Result< ci::health_check_result > check_health() override
kcenon::common::VoidResult reset() override
kcenon::common::VoidResult record_metric(const std::string &name, double value, const std::unordered_map< std::string, std::string > &tags) override
std::unordered_map< std::string, double > metrics_
kcenon::common::Result< ci::metrics_snapshot > get_metrics() override
size_t get_metric_count() const
kcenon::common::VoidResult record_metric(const std::string &name, double value) override
Builder pattern for logger construction with validation.
logger_builder & with_monitoring(std::shared_ptr< common::interfaces::IMonitor > monitor)
Set monitoring interface (Phase 2.2.4)
logger_builder & with_min_level(log_level level)
logger_builder & with_async(bool async=true)
result< std::unique_ptr< logger > > build()
void example_2_optional_monitor()
Example demonstrating monitor-less operation.
void example_4_monitor_swapping()
Example demonstrating monitor swapping.
void example_5_interface_based_usage()
void example_1_basic_di_pattern()
Example demonstrating DI pattern with logger and monitor.
void example_3_runtime_monitor_injection()
Example demonstrating runtime monitor injection.
void use_logger_via_interface(std::shared_ptr< ci::ILogger > logger)
Example demonstrating ILogger interface usage.
int main()
Builder pattern implementation for flexible logger configuration kcenon.
bool is_ok(const Result< T > &result)
VoidResult ok()
T & get_value(Result< T > &result)