Common System 0.2.0
Common interfaces and patterns for system integration
Loading...
Searching...
No Matches
global_logger_registry.h
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
24#pragma once
25
26#include "logger_interface.h"
27#include "registry_audit_log.h"
28
29#include <atomic>
30#include <functional>
31#include <memory>
32#include <mutex>
33#include <shared_mutex>
34#include <string>
35#include <unordered_map>
36
37namespace kcenon::common {
38namespace interfaces {
39
52class NullLogger : public ILogger {
53public:
54 NullLogger() = default;
55 ~NullLogger() override = default;
56
57 VoidResult log(log_level /*level*/, const std::string& /*message*/) override {
58 return VoidResult::ok({});
59 }
60
66 std::string_view /*message*/,
67 const source_location& /*loc*/ = source_location::current()) override {
68 return VoidResult::ok({});
69 }
70
71 VoidResult log(const log_entry& /*entry*/) override {
72 return VoidResult::ok({});
73 }
74
75 bool is_enabled(log_level /*level*/) const override {
76 return false;
77 }
78
79 VoidResult set_level(log_level /*level*/) override {
80 return VoidResult::ok({});
81 }
82
83 log_level get_level() const override {
84 return log_level::off;
85 }
86
87 VoidResult flush() override {
88 return VoidResult::ok({});
89 }
90};
91
129public:
145
146 // Delete copy and move operations
151
152 // ===== ILoggerRegistry Implementation =====
153
164 VoidResult register_logger(const std::string& name,
165 std::shared_ptr<ILogger> logger) override;
166
179 std::shared_ptr<ILogger> get_logger(const std::string& name) override;
180
190 VoidResult unregister_logger(const std::string& name) override;
191
200 std::shared_ptr<ILogger> get_default_logger() override;
201
211 VoidResult set_default_logger(std::shared_ptr<ILogger> logger) override;
212
213 // ===== Extended API for Factory Support =====
214
226 VoidResult register_factory(const std::string& name, LoggerFactory factory);
227
239
248 bool has_logger(const std::string& name) const;
249
257 bool has_default_logger() const;
258
265 void clear();
266
275 size_t size() const;
276
277 // ===== Security Controls =====
278
293 void freeze();
294
300 bool is_frozen() const;
301
315 static std::shared_ptr<ILogger> null_logger();
316
317private:
320
331 std::shared_ptr<ILogger> create_from_factory(const std::string& name);
332
338 std::shared_ptr<ILogger> create_default_from_factory();
339
340 mutable std::shared_mutex mutex_;
341 std::unordered_map<std::string, std::shared_ptr<ILogger>> loggers_;
342 std::unordered_map<std::string, LoggerFactory> factories_;
343 std::shared_ptr<ILogger> default_logger_;
345 std::atomic<bool> frozen_{false};
346};
347
348// ============================================================================
349// GlobalLoggerRegistry Implementation
350// ============================================================================
351
353 // Intentionally leak to avoid static destruction order issues.
354 // Registry may be accessed during other singletons' destruction.
356 return *instance;
357}
358
359inline std::shared_ptr<ILogger> GlobalLoggerRegistry::null_logger() {
360 // Intentionally leak to avoid static destruction order issues.
361 // NullLogger may be accessed during other singletons' destruction.
362 static auto* null_logger_ptr =
363 new std::shared_ptr<NullLogger>(std::make_shared<NullLogger>());
364 return *null_logger_ptr;
365}
366
368 const std::string& name,
369 std::shared_ptr<ILogger> logger) {
370
371 if (is_frozen()) {
375 "Registry is frozen"));
378 "Cannot register logger: registry is frozen",
379 "interfaces::GlobalLoggerRegistry"
380 );
381 }
382
383 if (name.empty()) {
387 "Logger name cannot be empty"));
390 "Logger name cannot be empty",
391 "interfaces::GlobalLoggerRegistry"
392 );
393 }
394
395 if (!logger) {
399 "Logger instance cannot be null"));
402 "Logger instance cannot be null",
403 "interfaces::GlobalLoggerRegistry"
404 );
405 }
406
407 std::unique_lock lock(mutex_);
408 loggers_[name] = std::move(logger);
409 // Remove factory if one exists for this name (logger takes precedence)
410 factories_.erase(name);
411
415
416 return VoidResult::ok({});
417}
418
419inline std::shared_ptr<ILogger> GlobalLoggerRegistry::get_logger(const std::string& name) {
420 // First, try to get existing logger with read lock
421 {
422 std::shared_lock lock(mutex_);
423 auto it = loggers_.find(name);
424 if (it != loggers_.end()) {
425 return it->second;
426 }
427 }
428
429 // Try to create from factory
430 auto logger = create_from_factory(name);
431 if (logger) {
432 return logger;
433 }
434
435 // Return NullLogger as fallback
436 return null_logger();
437}
438
440 if (is_frozen()) {
444 "Registry is frozen"));
447 "Cannot unregister logger: registry is frozen",
448 "interfaces::GlobalLoggerRegistry"
449 );
450 }
451
452 std::unique_lock lock(mutex_);
453 loggers_.erase(name);
454 factories_.erase(name);
455
459
460 return VoidResult::ok({});
461}
462
463inline std::shared_ptr<ILogger> GlobalLoggerRegistry::get_default_logger() {
464 // First, try to get existing default logger with read lock
465 {
466 std::shared_lock lock(mutex_);
467 if (default_logger_) {
468 return default_logger_;
469 }
470 }
471
472 // Try to create from factory
473 auto logger = create_default_from_factory();
474 if (logger) {
475 return logger;
476 }
477
478 // Return NullLogger as fallback
479 return null_logger();
480}
481
482inline VoidResult GlobalLoggerRegistry::set_default_logger(std::shared_ptr<ILogger> logger) {
483 if (is_frozen()) {
487 "Registry is frozen"));
490 "Cannot set default logger: registry is frozen",
491 "interfaces::GlobalLoggerRegistry"
492 );
493 }
494
495 if (!logger) {
499 "Default logger instance cannot be null"));
502 "Default logger instance cannot be null",
503 "interfaces::GlobalLoggerRegistry"
504 );
505 }
506
507 std::unique_lock lock(mutex_);
508 default_logger_ = std::move(logger);
509 // Clear factory since we have a concrete instance
510 default_factory_ = nullptr;
511
515
516 return VoidResult::ok({});
517}
518
520 const std::string& name,
521 LoggerFactory factory) {
522
523 if (is_frozen()) {
527 "Registry is frozen"));
530 "Cannot register factory: registry is frozen",
531 "interfaces::GlobalLoggerRegistry"
532 );
533 }
534
535 if (name.empty()) {
539 "Logger name cannot be empty"));
542 "Logger name cannot be empty",
543 "interfaces::GlobalLoggerRegistry"
544 );
545 }
546
547 if (!factory) {
551 "Factory function cannot be null"));
554 "Factory function cannot be null",
555 "interfaces::GlobalLoggerRegistry"
556 );
557 }
558
559 std::unique_lock lock(mutex_);
560
561 // Only register factory if no logger already exists
562 if (loggers_.find(name) != loggers_.end()) {
566 "Logger already registered with name: " + name));
569 "Logger already registered with name: " + name,
570 "interfaces::GlobalLoggerRegistry"
571 );
572 }
573
574 factories_[name] = std::move(factory);
575
579
580 return VoidResult::ok({});
581}
582
584 if (is_frozen()) {
588 "Registry is frozen"));
591 "Cannot set default factory: registry is frozen",
592 "interfaces::GlobalLoggerRegistry"
593 );
594 }
595
596 if (!factory) {
600 "Factory function cannot be null"));
603 "Factory function cannot be null",
604 "interfaces::GlobalLoggerRegistry"
605 );
606 }
607
608 std::unique_lock lock(mutex_);
609
610 // Only set factory if no default logger exists
611 if (default_logger_) {
615 "Default logger already registered"));
618 "Default logger already registered",
619 "interfaces::GlobalLoggerRegistry"
620 );
621 }
622
623 default_factory_ = std::move(factory);
624
628
629 return VoidResult::ok({});
630}
631
632inline bool GlobalLoggerRegistry::has_logger(const std::string& name) const {
633 std::shared_lock lock(mutex_);
634 return loggers_.find(name) != loggers_.end() ||
635 factories_.find(name) != factories_.end();
636}
637
639 std::shared_lock lock(mutex_);
640 return default_logger_ != nullptr || default_factory_ != nullptr;
641}
642
644 if (is_frozen()) {
648 "Registry is frozen"));
649 // Silently ignore clear when frozen to maintain API compatibility
650 return;
651 }
652
653 std::unique_lock lock(mutex_);
654 loggers_.clear();
655 factories_.clear();
656 default_logger_.reset();
657 default_factory_ = nullptr;
658
662}
663
664inline size_t GlobalLoggerRegistry::size() const {
665 std::shared_lock lock(mutex_);
666 return loggers_.size() + factories_.size();
667}
668
670 frozen_.store(true, std::memory_order_release);
671
675}
676
678 return frozen_.load(std::memory_order_acquire);
679}
680
681inline std::shared_ptr<ILogger> GlobalLoggerRegistry::create_from_factory(
682 const std::string& name) {
683
684 std::unique_lock lock(mutex_);
685
686 // Check again under write lock
687 auto logger_it = loggers_.find(name);
688 if (logger_it != loggers_.end()) {
689 return logger_it->second;
690 }
691
692 auto factory_it = factories_.find(name);
693 if (factory_it == factories_.end()) {
694 return nullptr;
695 }
696
697 // Create logger from factory
698 auto logger = factory_it->second();
699 if (logger) {
700 loggers_[name] = logger;
701 factories_.erase(factory_it);
702 }
703
704 return logger;
705}
706
707inline std::shared_ptr<ILogger> GlobalLoggerRegistry::create_default_from_factory() {
708 std::unique_lock lock(mutex_);
709
710 // Check again under write lock
711 if (default_logger_) {
712 return default_logger_;
713 }
714
715 if (!default_factory_) {
716 return nullptr;
717 }
718
719 // Create logger from factory
720 auto logger = default_factory_();
721 if (logger) {
722 default_logger_ = logger;
723 default_factory_ = nullptr;
724 }
725
726 return logger;
727}
728
729// ============================================================================
730// Convenience Functions
731// ============================================================================
732
743
752inline std::shared_ptr<ILogger> get_logger() {
754}
755
765inline std::shared_ptr<ILogger> get_logger(const std::string& name) {
767}
768
769} // namespace interfaces
770} // namespace kcenon::common
Result type for error handling with member function support.
Definition core.cppm:165
static Result< T > ok(U &&value)
Create a successful result with value (static factory)
Definition core.h:223
Thread-safe singleton registry for managing logger instances.
static GlobalLoggerRegistry & instance()
Get the singleton instance of GlobalLoggerRegistry.
bool has_logger(const std::string &name) const
Check if a logger is registered.
void freeze()
Freeze the registry to prevent further modifications.
std::shared_ptr< ILogger > create_from_factory(const std::string &name)
Create logger from factory if available.
std::shared_ptr< ILogger > create_default_from_factory()
Create default logger from factory if available.
VoidResult unregister_logger(const std::string &name) override
Remove a logger by name.
void clear()
Clear all registered loggers and factories.
GlobalLoggerRegistry(const GlobalLoggerRegistry &)=delete
bool is_frozen() const
Check if the registry is frozen.
VoidResult set_default_factory(LoggerFactory factory)
Set a factory for the default logger.
size_t size() const
Get the number of registered loggers.
static std::shared_ptr< ILogger > null_logger()
Get the shared NullLogger instance.
VoidResult register_factory(const std::string &name, LoggerFactory factory)
Register a factory for lazy logger creation.
GlobalLoggerRegistry & operator=(const GlobalLoggerRegistry &)=delete
GlobalLoggerRegistry & operator=(GlobalLoggerRegistry &&)=delete
VoidResult set_default_logger(std::shared_ptr< ILogger > logger) override
Set the default logger.
GlobalLoggerRegistry(GlobalLoggerRegistry &&)=delete
VoidResult register_logger(const std::string &name, std::shared_ptr< ILogger > logger) override
Register a logger with a name.
std::shared_ptr< ILogger > get_logger(const std::string &name) override
Get a logger by name.
std::unordered_map< std::string, std::shared_ptr< ILogger > > loggers_
std::unordered_map< std::string, LoggerFactory > factories_
std::shared_ptr< ILogger > get_default_logger() override
Get the default logger.
bool has_default_logger() const
Check if a default logger is available.
Phase 2: Global logger registry interface.
Definition logger.cppm:135
Standard interface for logging implementations.
Definition logger.cppm:95
A no-op logger implementation for fallback scenarios.
VoidResult set_level(log_level) override
Set the minimum log level.
VoidResult flush() override
Flush any buffered log messages.
VoidResult log(log_level, std::string_view, const source_location &=source_location::current()) override
Log with source_location (no-op)
VoidResult log(log_level, const std::string &) override
Log a message with specified level.
log_level get_level() const override
Get the current minimum log level.
VoidResult log(const log_entry &) override
Log a structured entry.
bool is_enabled(log_level) const override
Check if logging is enabled for the specified level.
static void log_event(const registry_event &event)
Log a registry event.
Standard logger interface for all systems.
constexpr int REGISTRY_FROZEN
Definition compat.h:41
constexpr int ALREADY_EXISTS
Definition compat.h:37
constexpr int INVALID_ARGUMENT
Definition compat.h:31
@ freeze_logger_registry
Freeze logger registry.
@ set_default_factory
Default factory set (logger)
@ register_factory
Factory registration (logger)
GlobalLoggerRegistry & get_registry()
Get the global logger registry instance.
std::shared_ptr< ILogger > get_logger()
Get the default logger from the global registry.
std::function< std::shared_ptr< ILogger >()> LoggerFactory
Factory function type for creating logger instances.
log_level
Standard log levels.
Core interfaces.
Definition adapter.h:21
Result< T > make_error(int code, const std::string &message, const std::string &module="")
Create an error result with code and message.
Definition utilities.h:91
Audit logging for registry operations.
Standard log entry structure.
Definition logger.cppm:65
Represents a single audit event for registry mutations.
C++17-compatible source_location implementation using compiler builtins.
Definition utils.cppm:54
static constexpr source_location current(const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE()) noexcept