Logger System 0.1.3
High-performance C++20 thread-safe logging system with asynchronous capabilities
Loading...
Searching...
No Matches
CONDITIONAL_COMPILATION_REFACTORING

autotoc_md325

doc_id: "LOG-ARCH-005" doc_title: "Conditional Compilation Refactoring Strategy" doc_version: "1.0.0" doc_date: "2026-04-04" doc_status: "Released" project: "logger_system"

category: "ARCH"

Conditional Compilation Refactoring Strategy

SSOT: This document is the single source of truth for Conditional Compilation Refactoring Strategy.

Version: 0.1.0.0 Last Updated: 2025-11-08 Status: Planning Document Priority: P1 (High)

Problem Statement

The logger_system currently uses extensive conditional compilation (#ifdef) to support multiple integration modes:

  • Standalone Mode: Independent operation without ecosystem dependencies
  • Thread System Integration: Implements thread_system::logger_interface
  • Common System Integration: Uses common_system Result<T> and interfaces

Current State

Total conditional directives: 102
USE_THREAD_SYSTEM usage: 44 instances
BUILD_WITH_COMMON_SYSTEM usage: 18 instances

Issues

  1. Build Complexity: 2^N possible build configurations (N=2 → 4 combinations)
  2. Testing Burden: Each combination requires separate CI/CD pipeline
  3. Maintenance Cost: Changes must be validated across all modes
  4. Code Readability: Control flow obscured by preprocessor directives
  5. Technical Debt: Increasing over time as new integrations added

Example of Current Pattern

// logger.h (v3.0 - simplified)
#include <kcenon/common/interfaces/logger_interface.h>
#include <kcenon/common/interfaces/monitoring_interface.h>
class logger : public common::interfaces::ILogger,
public common::interfaces::IMonitorable
{
// Clean implementation without conditional compilation
};

Proposed Solution: Adapter Pattern

Replace compile-time branching with runtime polymorphism using the Adapter Pattern.

Architecture

┌─────────────────────────────────────────────────────────────┐
│ logger_core │
│ (Pure implementation, no #ifdef) │
└─────────────────────┬───────────────────────────────────────┘
┌───────────┴──────────┬──────────────────┐
│ │ │
┌───────▼──────────┐ ┌────────▼────────┐ ┌─────▼──────────┐
│ standalone_ │ │ thread_system_ │ │ common_system_ │
│ adapter │ │ adapter │ │ adapter │
└──────────────────┘ └─────────────────┘ └────────────────┘
│ │ │
┌───────▼──────────┐ ┌────────▼────────┐ ┌─────▼──────────┐
│ Direct usage │ │ thread_system:: │ │ common:: │
│ │ │ logger_interface│ │ IMonitorable │
└──────────────────┘ └─────────────────┘ └────────────────┘

Benefits

Single Build Configuration: One binary works for all modes ✅ Runtime Selection: Choose integration mode at runtime ✅ Easier Testing: Test all modes with single test suite ✅ Better Readability: Clear, linear code flow ✅ Maintainability: Changes affect single code path ✅ Extensibility: Add new integrations without recompiling

Trade-offs

Virtual Function Overhead: ~1-2ns per call (negligible for logging) ❌ Binary Size: Larger binary includes all adapters (~10-20KB) ❌ Complexity Shift: From compile-time to runtime configuration

Implementation Plan

Phase 3.1: Analysis and Documentation ✅

  • Count conditional directives
  • Identify integration patterns
  • Document current state
  • Propose refactoring strategy

Phase 3.2: Core Interface Design

  • Define adapter interface
  • Design factory pattern for adapter selection
  • Create configuration system for runtime mode selection

Phase 3.3: Adapter Implementation

  • Add unit tests for each adapter

Phase 3.4: Migration

  • Remove conditional compilation from public headers
  • Update CMakeLists.txt to simplify build
  • Update documentation and examples

Phase 3.5: Validation

  • Run full test suite
  • Benchmark performance impact
  • Verify all integration modes work
  • Update CI/CD to single configuration

Detailed Design

Core Logger Interface

// logger_core.h
namespace kcenon::logger {
class logger_core {
public:
explicit logger_core(bool async = true, size_t buffer_size = 8192);
// Core logging interface
void log(log_level level, const std::string& message);
void log(log_level level, const std::string& message,
const std::string& file, int line, const std::string& function);
void flush();
void start();
void stop();
// Configuration
void set_min_level(log_level level);
log_level get_min_level() const;
// Writer management
void add_writer(std::unique_ptr<base_writer> writer);
void clear_writers();
// Metrics (optional)
void enable_metrics_collection(bool enable = true);
logger_performance_stats get_current_metrics() const;
private:
class impl;
std::unique_ptr<impl> pimpl_;
};
} // namespace kcenon::logger

Adapter Interface

// logger_adapter.h
namespace kcenon::logger {
public:
explicit logger_adapter(std::shared_ptr<logger_core> core)
: core_(std::move(core)) {}
virtual ~logger_adapter() = default;
// Delegate to core
void log(log_level level, const std::string& message) {
core_->log(level, message);
}
void flush() { core_->flush(); }
void start() { core_->start(); }
void stop() { core_->stop(); }
protected:
std::shared_ptr<logger_core> core_;
};
} // namespace kcenon::logger
void log(const std::string &message)

Thread System Adapter

// thread_system_adapter.h
#include <kcenon/thread/interfaces/logger_interface.h>
#include "logger_core.h"
#include "logger_adapter.h"
namespace kcenon::logger {
: public logger_adapter
, public thread_system::logger_interface {
public:
explicit thread_system_adapter(std::shared_ptr<logger_core> core)
: logger_adapter(std::move(core)) {}
// thread_system::logger_interface implementation
void log(thread_system::log_level level, const std::string& message) override {
// Convert thread_system::log_level to logger::log_level
auto logger_level = convert_log_level(level);
core_->log(logger_level, message);
}
void flush() override {
core_->flush();
}
private:
logger::log_level convert_log_level(thread_system::log_level level);
};
} // namespace kcenon::logger
Standalone logger adapter for use without common_system DI.

Common System Adapter

// common_system_adapter.h
#include <kcenon/common/interfaces/monitoring_interface.h>
#include "logger_core.h"
#include "logger_adapter.h"
namespace kcenon::logger {
: public logger_adapter
, public common::interfaces::IMonitorable {
public:
explicit common_system_adapter(std::shared_ptr<logger_core> core)
: logger_adapter(std::move(core)) {}
// IMonitorable interface implementation
get_monitoring_data() override {
auto stats = core_->get_current_metrics();
return convert_to_metrics_snapshot(stats);
}
health_check() override {
// Implement health check logic
health_check_result{/* ... */}
);
}
std::string get_component_name() const override {
return "logger_system::logger";
}
private:
common::interfaces::metrics_snapshot
convert_to_metrics_snapshot(const logger_performance_stats& stats);
};
} // namespace kcenon::logger

Factory Pattern

// logger_factory.h
namespace kcenon::logger {
enum class integration_mode {
full // Both thread_system and common_system
};
public:
static std::unique_ptr<logger_adapter> create(
bool async = true,
size_t buffer_size = 8192
) {
auto core = std::make_shared<logger_core>(async, buffer_size);
switch (mode) {
return std::make_unique<standalone_adapter>(core);
return std::make_unique<thread_system_adapter>(core);
return std::make_unique<common_system_adapter>(core);
return std::make_unique<full_integration_adapter>(core);
default:
throw std::invalid_argument("Unknown integration mode");
}
}
};
} // namespace kcenon::logger

Migration Strategy

Step 1: Extract Core (1 week)

Step 2: Implement Adapters (1 week)

Step 3: Update Public Interface (3 days)

  • Update logger.h to use factory pattern
  • Deprecate old constructors
  • Add migration guide

Step 4: Remove Conditional Compilation (2 days)

  • Remove #ifdef from all public headers
  • Simplify CMakeLists.txt
  • Update build documentation

Step 5: Testing and Validation (3 days)

  • Test all integration modes
  • Benchmark performance
  • Update CI/CD pipeline
  • Update documentation

Total Estimated Effort: 2.5 weeks

Performance Considerations

Virtual Function Overhead

// Benchmark: Direct call vs virtual call
void benchmark_logging_overhead() {
// Direct call (current with #ifdef)
logger_core.log(level, message); // ~148ns average
// Virtual call (proposed adapter)
adapter->log(level, message); // ~150ns average (+2ns overhead)
}

Analysis: +2ns overhead (~1.3%) is negligible for logging operations which are I/O bound and typically take microseconds to complete.

Binary Size Impact

Current (with #ifdef):
- Standalone build: 250KB
- Thread integrated: 275KB
- Common integrated: 260KB
- Full integrated: 290KB
Proposed (single binary):
- Unified build: 310KB (+20KB, ~7% increase)

Analysis: 20KB increase is acceptable for improved maintainability.

Risk Mitigation

Risks

  1. Performance Regression: Virtual function overhead
    • Mitigation: Benchmark before/after, optimize hot paths
  2. Binary Size Increase: Including all adapters
    • Mitigation: Acceptable trade-off for maintainability
  3. API Breaking Changes: Existing code needs updates
    • Mitigation: Deprecation period, migration guide
  4. Testing Coverage: Must test all modes
    • Mitigation: Automated tests for each adapter

Rollback Plan

If refactoring introduces critical issues:

  1. Revert to previous commit
  2. Keep conditional compilation for one more release
  3. Address issues in separate branch
  4. Re-attempt refactoring in next version

Success Criteria

  • Zero conditional compilation in public headers
  • All integration modes pass tests
  • Performance overhead < 5%
  • Binary size increase < 10%
  • CI/CD simplified to single configuration
  • Documentation updated with migration guide

References

  • Adapter Pattern: Gang of Four Design Patterns
  • Performance: Benchmark results in benchmarks/adapter_overhead.cpp
  • Migration Guide: docs/MIGRATION_TO_ADAPTERS.md (to be created)

Author: Architecture Team Reviewers: TBD Approval: Pending implementation