Skip to content

feat(interfaces): implement GlobalLoggerRegistry for runtime binding (#174)#181

Merged
kcenon merged 4 commits into
mainfrom
feat/174-global-logger-registry
Dec 5, 2025
Merged

feat(interfaces): implement GlobalLoggerRegistry for runtime binding (#174)#181
kcenon merged 4 commits into
mainfrom
feat/174-global-logger-registry

Conversation

@kcenon

@kcenon kcenon commented Dec 5, 2025

Copy link
Copy Markdown
Owner

Summary

This PR implements GlobalLoggerRegistry, a thread-safe singleton registry for managing logger instances across all subsystems. It resolves the circular dependency between thread_system and logger_system by providing a centralized, decoupled logging registry that can be bound at runtime.

Problem Statement

Currently, these systems have problematic interdependencies:

  • logger_system depends on thread_system for async operations
  • thread_system defines its own logger_interface.h to prevent circular imports

This creates maintenance and extensibility challenges.

Solution

GlobalLoggerRegistry provides:

  • Thread-safe singleton pattern using Meyer's singleton idiom
  • std::shared_mutex for efficient read/write locking
  • Default and named logger management
  • Factory-based lazy initialization for deferred logger creation
  • NullLogger fallback for safe operation when logging is not configured

Changes

New Files

  • include/kcenon/common/interfaces/global_logger_registry.h - Implementation of GlobalLoggerRegistry and NullLogger
  • tests/global_logger_registry_test.cpp - Comprehensive unit tests (35 test cases)

Modified Files

  • include/kcenon/common/common.h - Added logger interface includes to aggregated header
  • tests/CMakeLists.txt - Added new test target
  • docs/API_REFERENCE.md - Added GlobalLoggerRegistry documentation
  • docs/CHANGELOG.md - Added changelog entry

API Overview

// Get the global registry
auto& registry = GlobalLoggerRegistry::instance();

// Register a default logger
registry.set_default_logger(std::make_shared<ConsoleLogger>());

// Register named loggers
registry.register_logger("network", std::make_shared<NetworkLogger>());

// Lazy initialization with factory
registry.register_factory("database", []() {
    return std::make_shared<DatabaseLogger>();
});

// Convenience functions
get_logger()->log(log_level::info, "Message");           // default logger
get_logger("network")->log(log_level::debug, "Message"); // named logger

Test Coverage

35 test cases covering:

  • Singleton behavior
  • Default logger management
  • Named logger registration/retrieval
  • Factory-based lazy initialization
  • NullLogger fallback behavior
  • Thread safety (concurrent registration, retrieval, factory creation, mixed operations)
  • Integration scenarios

All tests pass with no race conditions or deadlocks detected.

Breaking Changes

None. This is a new feature addition.

Impact

This implementation is part of Issue #174 and blocks Phase 2/3 work on the runtime binding migration. Once merged, dependent issues (#175, #176) can proceed.

Closes #174

This commit introduces the GlobalLoggerRegistry class to resolve the
circular dependency between thread_system and logger_system. The registry
provides a centralized, thread-safe mechanism for managing logger instances
across all subsystems.

Key features implemented:
- Thread-safe singleton pattern using Meyer's singleton idiom
- std::shared_mutex for efficient read/write locking
- Default and named logger management
- Factory-based lazy initialization for deferred logger creation
- NullLogger fallback for unregistered logger requests
- Convenience functions (get_registry, get_logger)

The NullLogger class provides a safe no-op implementation that:
- Returns success for all logging operations
- Always reports log level as 'off'
- Ensures code functions silently when logging is not configured

Public API:
- register_logger(name, logger): Register a named logger
- get_logger(name): Retrieve a named logger (returns NullLogger if not found)
- unregister_logger(name): Remove a named logger
- set_default_logger(logger): Set the default logger
- get_default_logger(): Get the default logger (returns NullLogger if not set)
- register_factory(name, factory): Register lazy initialization factory
- set_default_factory(factory): Set factory for default logger
- has_logger(name): Check if logger/factory exists
- has_default_logger(): Check if default logger/factory exists
- clear(): Remove all registered loggers and factories
- size(): Get count of registered named loggers

This implementation is part of Issue #174 and blocks Phase 2/3 work on
the runtime binding migration.

Refs: #174
This commit adds unit tests covering all aspects of the GlobalLoggerRegistry
implementation, ensuring thread safety and correctness.

Test categories implemented:

1. Singleton Tests:
   - Instance returns same reference
   - get_registry() convenience function works correctly

2. Default Logger Tests:
   - Returns NullLogger when no default is set
   - set_default_logger() succeeds with valid logger
   - set_default_logger() fails with null
   - Convenience function get_logger() returns default

3. Named Logger Tests:
   - register_logger() with valid parameters
   - Empty name validation
   - Null logger validation
   - Logger replacement behavior
   - get_logger() retrieval
   - NullLogger fallback for unregistered names
   - unregister_logger() behavior

4. Factory Tests:
   - Lazy initialization pattern
   - Factory called only once
   - Factory validation (empty name, null factory)
   - Factory vs existing logger precedence
   - register_logger() removes associated factory
   - Default factory lazy initialization

5. NullLogger Tests:
   - All operations succeed
   - Same instance returned
   - Always reports disabled

6. Clear and Size Tests:
   - clear() removes all state
   - size() returns correct count

7. Thread Safety Tests (35 test cases total):
   - Concurrent registration (10 threads, 100 loggers each)
   - Concurrent retrieval (10 threads, 1000 retrievals each)
   - Concurrent factory creation (race condition test)
   - Concurrent default logger access
   - Mixed read/write operations

8. Integration Tests:
   - Logging through registry
   - Multiple named loggers

All tests pass with no race conditions or deadlocks detected.

Refs: #174
This commit adds the logger-related headers to the main common.h
aggregated header file for convenient access.

Added includes:
- interfaces/logger_interface.h: Core ILogger interface and log types
- interfaces/global_logger_registry.h: GlobalLoggerRegistry singleton

This allows users to access the logging infrastructure with a single
include of <kcenon/common/common.h> instead of having to include
individual header files.

Refs: #174
This commit updates the documentation to include the new GlobalLoggerRegistry
class and related components.

Changes to API_REFERENCE.md:
- Updated ILogger section with correct header path and complete interface
- Added comprehensive GlobalLoggerRegistry documentation:
  - Singleton access pattern
  - Default logger management methods
  - Named logger management methods
  - Factory support for lazy initialization
  - Utility methods (clear, size, null_logger)
  - Convenience functions
  - Usage examples with code samples
- Added NullLogger documentation

Changes to CHANGELOG.md:
- Added GlobalLoggerRegistry to [Unreleased] section
- Listed key features: thread-safe singleton, named loggers, factory support
- Referenced Issue #174

Refs: #174
@kcenon kcenon merged commit 78e62fb into main Dec 5, 2025
25 checks passed
@kcenon kcenon deleted the feat/174-global-logger-registry branch December 5, 2025 14:00
kcenon added a commit that referenced this pull request Apr 13, 2026
…174) (#181)

* feat(interfaces): implement GlobalLoggerRegistry for runtime binding

This commit introduces the GlobalLoggerRegistry class to resolve the
circular dependency between thread_system and logger_system. The registry
provides a centralized, thread-safe mechanism for managing logger instances
across all subsystems.

Key features implemented:
- Thread-safe singleton pattern using Meyer's singleton idiom
- std::shared_mutex for efficient read/write locking
- Default and named logger management
- Factory-based lazy initialization for deferred logger creation
- NullLogger fallback for unregistered logger requests
- Convenience functions (get_registry, get_logger)

The NullLogger class provides a safe no-op implementation that:
- Returns success for all logging operations
- Always reports log level as 'off'
- Ensures code functions silently when logging is not configured

Public API:
- register_logger(name, logger): Register a named logger
- get_logger(name): Retrieve a named logger (returns NullLogger if not found)
- unregister_logger(name): Remove a named logger
- set_default_logger(logger): Set the default logger
- get_default_logger(): Get the default logger (returns NullLogger if not set)
- register_factory(name, factory): Register lazy initialization factory
- set_default_factory(factory): Set factory for default logger
- has_logger(name): Check if logger/factory exists
- has_default_logger(): Check if default logger/factory exists
- clear(): Remove all registered loggers and factories
- size(): Get count of registered named loggers

This implementation is part of Issue #174 and blocks Phase 2/3 work on
the runtime binding migration.

Refs: #174

* test(interfaces): add comprehensive tests for GlobalLoggerRegistry

This commit adds unit tests covering all aspects of the GlobalLoggerRegistry
implementation, ensuring thread safety and correctness.

Test categories implemented:

1. Singleton Tests:
   - Instance returns same reference
   - get_registry() convenience function works correctly

2. Default Logger Tests:
   - Returns NullLogger when no default is set
   - set_default_logger() succeeds with valid logger
   - set_default_logger() fails with null
   - Convenience function get_logger() returns default

3. Named Logger Tests:
   - register_logger() with valid parameters
   - Empty name validation
   - Null logger validation
   - Logger replacement behavior
   - get_logger() retrieval
   - NullLogger fallback for unregistered names
   - unregister_logger() behavior

4. Factory Tests:
   - Lazy initialization pattern
   - Factory called only once
   - Factory validation (empty name, null factory)
   - Factory vs existing logger precedence
   - register_logger() removes associated factory
   - Default factory lazy initialization

5. NullLogger Tests:
   - All operations succeed
   - Same instance returned
   - Always reports disabled

6. Clear and Size Tests:
   - clear() removes all state
   - size() returns correct count

7. Thread Safety Tests (35 test cases total):
   - Concurrent registration (10 threads, 100 loggers each)
   - Concurrent retrieval (10 threads, 1000 retrievals each)
   - Concurrent factory creation (race condition test)
   - Concurrent default logger access
   - Mixed read/write operations

8. Integration Tests:
   - Logging through registry
   - Multiple named loggers

All tests pass with no race conditions or deadlocks detected.

Refs: #174

* refactor(common): add logger interfaces to aggregated header

This commit adds the logger-related headers to the main common.h
aggregated header file for convenient access.

Added includes:
- interfaces/logger_interface.h: Core ILogger interface and log types
- interfaces/global_logger_registry.h: GlobalLoggerRegistry singleton

This allows users to access the logging infrastructure with a single
include of <kcenon/common/common.h> instead of having to include
individual header files.

Refs: #174

* docs(interfaces): add GlobalLoggerRegistry API documentation

This commit updates the documentation to include the new GlobalLoggerRegistry
class and related components.

Changes to API_REFERENCE.md:
- Updated ILogger section with correct header path and complete interface
- Added comprehensive GlobalLoggerRegistry documentation:
  - Singleton access pattern
  - Default logger management methods
  - Named logger management methods
  - Factory support for lazy initialization
  - Utility methods (clear, size, null_logger)
  - Convenience functions
  - Usage examples with code samples
- Added NullLogger documentation

Changes to CHANGELOG.md:
- Added GlobalLoggerRegistry to [Unreleased] section
- Listed key features: thread-safe singleton, named loggers, factory support
- Referenced Issue #174

Refs: #174
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(logging): implement GlobalLoggerRegistry for runtime binding

1 participant