|
| 1 | +# Logger Interface Migration Guide |
| 2 | + |
| 3 | +> **Language:** **English** | [한국어](LOGGER_INTERFACE_MIGRATION_GUIDE_KO.md) |
| 4 | +
|
| 5 | +**Version:** 1.0 |
| 6 | +**Date:** 2025-12-06 |
| 7 | +**Applies to:** thread_system v1.x and later |
| 8 | + |
| 9 | +## Overview |
| 10 | + |
| 11 | +This guide helps you migrate from the deprecated `kcenon::thread::logger_interface` to the unified `kcenon::common::interfaces::ILogger` from common_system. The thread_system's local logger interface is now deprecated and will be removed in v2.0. |
| 12 | + |
| 13 | +## Why Migrate? |
| 14 | + |
| 15 | +### Problems with thread_system's logger_interface |
| 16 | + |
| 17 | +1. **Code Duplication**: Duplicates the logging interface already provided by common_system |
| 18 | +2. **Maintenance Burden**: Two interfaces to maintain with diverging features |
| 19 | +3. **Incompatible Types**: Cannot pass thread_system loggers to common_system without adapters |
| 20 | +4. **Inverted Log Levels**: Uses critical=0...trace=5 (opposite of industry standards) |
| 21 | + |
| 22 | +### Benefits of common_system's ILogger |
| 23 | + |
| 24 | +1. **Unified Interface**: Single interface across all systems (thread_system, logger_system, etc.) |
| 25 | +2. **Result-based Error Handling**: Returns `VoidResult` for consistent error handling |
| 26 | +3. **Standard Log Level Ordering**: Uses trace=0...critical=5 (matches spdlog, log4j, syslog) |
| 27 | +4. **Extended Features**: Named loggers, ILoggerRegistry, structured logging support |
| 28 | +5. **Better Integration**: Seamless interoperability between systems |
| 29 | + |
| 30 | +## Deprecation Timeline |
| 31 | + |
| 32 | +| Phase | Version | Status | Description | |
| 33 | +|-------|---------|--------|-------------| |
| 34 | +| Phase 1 | v1.x | **Current** | Deprecation warnings added | |
| 35 | +| Phase 2 | v1.x | **Current** | Migration documentation | |
| 36 | +| Phase 3 | v2.0 | Planned | File removed entirely | |
| 37 | + |
| 38 | +## Migration Steps |
| 39 | + |
| 40 | +### Step 1: Update Include Statements |
| 41 | + |
| 42 | +**Before:** |
| 43 | +```cpp |
| 44 | +#include <kcenon/thread/interfaces/logger_interface.h> |
| 45 | +``` |
| 46 | + |
| 47 | +**After:** |
| 48 | +```cpp |
| 49 | +#include <kcenon/common/interfaces/logger_interface.h> |
| 50 | +``` |
| 51 | + |
| 52 | +### Step 2: Update Namespace and Type References |
| 53 | + |
| 54 | +**Before:** |
| 55 | +```cpp |
| 56 | +using kcenon::thread::logger_interface; |
| 57 | +using kcenon::thread::logger_registry; |
| 58 | +using kcenon::thread::log_level; |
| 59 | + |
| 60 | +class MyLogger : public logger_interface { |
| 61 | + void log(log_level level, const std::string& message) override; |
| 62 | + void log(log_level level, const std::string& message, |
| 63 | + const std::string& file, int line, const std::string& function) override; |
| 64 | + bool is_enabled(log_level level) const override; |
| 65 | + void flush() override; |
| 66 | +}; |
| 67 | +``` |
| 68 | +
|
| 69 | +**After:** |
| 70 | +```cpp |
| 71 | +using kcenon::common::interfaces::ILogger; |
| 72 | +using kcenon::common::interfaces::ILoggerRegistry; |
| 73 | +using kcenon::common::interfaces::log_level; |
| 74 | +
|
| 75 | +class MyLogger : public ILogger { |
| 76 | + void log(log_level level, const std::string& message) override; |
| 77 | + void log(log_level level, const std::string& message, |
| 78 | + const std::string& file, int line, const std::string& function) override; |
| 79 | + bool is_enabled(log_level level) const override; |
| 80 | + void flush() override; |
| 81 | +}; |
| 82 | +``` |
| 83 | + |
| 84 | +### Step 3: Update Log Level Values |
| 85 | + |
| 86 | +The log level ordering is **inverted** between the two interfaces: |
| 87 | + |
| 88 | +| Level | thread::log_level | common::log_level | |
| 89 | +|-------|-------------------|-------------------| |
| 90 | +| trace | 5 | 0 | |
| 91 | +| debug | 4 | 1 | |
| 92 | +| info | 3 | 2 | |
| 93 | +| warning | 2 | 3 | |
| 94 | +| error | 1 | 4 | |
| 95 | +| critical | 0 | 5 | |
| 96 | + |
| 97 | +**Before (thread_system - inverted ordering):** |
| 98 | +```cpp |
| 99 | +// Higher severity = lower value (non-standard) |
| 100 | +if (static_cast<int>(level) <= static_cast<int>(min_level)) { |
| 101 | + // Log the message |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +**After (common_system - standard ordering):** |
| 106 | +```cpp |
| 107 | +// Higher severity = higher value (standard) |
| 108 | +if (level >= min_level) { |
| 109 | + // Log the message |
| 110 | +} |
| 111 | +``` |
| 112 | + |
| 113 | +### Step 4: Update Comparisons |
| 114 | + |
| 115 | +**Before:** |
| 116 | +```cpp |
| 117 | +// thread_system: critical=0, trace=5 |
| 118 | +if (level <= log_level::warning) { |
| 119 | + // This logs warning, error, and critical |
| 120 | +} |
| 121 | +``` |
| 122 | + |
| 123 | +**After:** |
| 124 | +```cpp |
| 125 | +// common_system: trace=0, critical=5 |
| 126 | +if (level >= log_level::warning) { |
| 127 | + // This logs warning, error, and critical |
| 128 | +} |
| 129 | +``` |
| 130 | + |
| 131 | +### Step 5: Update Registry Usage |
| 132 | + |
| 133 | +**Before:** |
| 134 | +```cpp |
| 135 | +// Set global logger |
| 136 | +kcenon::thread::logger_registry::set_logger(my_logger); |
| 137 | + |
| 138 | +// Get global logger |
| 139 | +auto logger = kcenon::thread::logger_registry::get_logger(); |
| 140 | + |
| 141 | +// Clear logger |
| 142 | +kcenon::thread::logger_registry::clear_logger(); |
| 143 | +``` |
| 144 | +
|
| 145 | +**After:** |
| 146 | +```cpp |
| 147 | +// Use ILoggerRegistry for named loggers |
| 148 | +auto& registry = kcenon::common::interfaces::ILoggerRegistry::instance(); |
| 149 | +
|
| 150 | +// Register with a name |
| 151 | +registry.register_logger("my_app", my_logger); |
| 152 | +
|
| 153 | +// Get by name |
| 154 | +auto logger = registry.get_logger("my_app"); |
| 155 | +
|
| 156 | +// Get or create default |
| 157 | +auto default_logger = registry.get_default_logger(); |
| 158 | +``` |
| 159 | + |
| 160 | +### Step 6: Update Macros (if used) |
| 161 | + |
| 162 | +The `THREAD_LOG_*` macros are deprecated. Replace with direct logger calls or your own macros. |
| 163 | + |
| 164 | +**Before:** |
| 165 | +```cpp |
| 166 | +THREAD_LOG_ERROR("Something went wrong"); |
| 167 | +THREAD_LOG_INFO("Operation completed"); |
| 168 | +``` |
| 169 | + |
| 170 | +**After:** |
| 171 | +```cpp |
| 172 | +// Option 1: Direct logger calls |
| 173 | +if (auto logger = get_logger()) { |
| 174 | + logger->log(log_level::error, "Something went wrong", __FILE__, __LINE__, __FUNCTION__); |
| 175 | +} |
| 176 | + |
| 177 | +// Option 2: Define your own macros using ILogger |
| 178 | +#define APP_LOG(level, msg) \ |
| 179 | + do { \ |
| 180 | + if (auto logger = MyApp::get_logger()) { \ |
| 181 | + if (logger->is_enabled(level)) { \ |
| 182 | + logger->log(level, msg, __FILE__, __LINE__, __FUNCTION__); \ |
| 183 | + } \ |
| 184 | + } \ |
| 185 | + } while(0) |
| 186 | +``` |
| 187 | +
|
| 188 | +## Complete Migration Example |
| 189 | +
|
| 190 | +### Before Migration |
| 191 | +
|
| 192 | +```cpp |
| 193 | +#include <kcenon/thread/interfaces/logger_interface.h> |
| 194 | +
|
| 195 | +namespace myapp { |
| 196 | +
|
| 197 | +class ConsoleLogger : public kcenon::thread::logger_interface { |
| 198 | +public: |
| 199 | + void log(kcenon::thread::log_level level, const std::string& message) override { |
| 200 | + if (static_cast<int>(level) <= static_cast<int>(min_level_)) { |
| 201 | + std::cout << "[" << level_to_string(level) << "] " << message << "\n"; |
| 202 | + } |
| 203 | + } |
| 204 | +
|
| 205 | + void log(kcenon::thread::log_level level, const std::string& message, |
| 206 | + const std::string& file, int line, const std::string& function) override { |
| 207 | + if (static_cast<int>(level) <= static_cast<int>(min_level_)) { |
| 208 | + std::cout << "[" << level_to_string(level) << "] " |
| 209 | + << file << ":" << line << " " << message << "\n"; |
| 210 | + } |
| 211 | + } |
| 212 | +
|
| 213 | + bool is_enabled(kcenon::thread::log_level level) const override { |
| 214 | + return static_cast<int>(level) <= static_cast<int>(min_level_); |
| 215 | + } |
| 216 | +
|
| 217 | + void flush() override { |
| 218 | + std::cout.flush(); |
| 219 | + } |
| 220 | +
|
| 221 | +private: |
| 222 | + kcenon::thread::log_level min_level_ = kcenon::thread::log_level::info; |
| 223 | +
|
| 224 | + std::string level_to_string(kcenon::thread::log_level level) { |
| 225 | + switch (level) { |
| 226 | + case kcenon::thread::log_level::critical: return "CRIT"; |
| 227 | + case kcenon::thread::log_level::error: return "ERROR"; |
| 228 | + case kcenon::thread::log_level::warning: return "WARN"; |
| 229 | + case kcenon::thread::log_level::info: return "INFO"; |
| 230 | + case kcenon::thread::log_level::debug: return "DEBUG"; |
| 231 | + case kcenon::thread::log_level::trace: return "TRACE"; |
| 232 | + default: return "???"; |
| 233 | + } |
| 234 | + } |
| 235 | +}; |
| 236 | +
|
| 237 | +void setup() { |
| 238 | + auto logger = std::make_shared<ConsoleLogger>(); |
| 239 | + kcenon::thread::logger_registry::set_logger(logger); |
| 240 | +} |
| 241 | +
|
| 242 | +} // namespace myapp |
| 243 | +``` |
| 244 | + |
| 245 | +### After Migration |
| 246 | + |
| 247 | +```cpp |
| 248 | +#include <kcenon/common/interfaces/logger_interface.h> |
| 249 | + |
| 250 | +namespace myapp { |
| 251 | + |
| 252 | +class ConsoleLogger : public kcenon::common::interfaces::ILogger { |
| 253 | +public: |
| 254 | + void log(kcenon::common::interfaces::log_level level, |
| 255 | + const std::string& message) override { |
| 256 | + if (level >= min_level_) { |
| 257 | + std::cout << "[" << level_to_string(level) << "] " << message << "\n"; |
| 258 | + } |
| 259 | + } |
| 260 | + |
| 261 | + void log(kcenon::common::interfaces::log_level level, |
| 262 | + const std::string& message, |
| 263 | + const std::string& file, int line, const std::string& function) override { |
| 264 | + if (level >= min_level_) { |
| 265 | + std::cout << "[" << level_to_string(level) << "] " |
| 266 | + << file << ":" << line << " " << message << "\n"; |
| 267 | + } |
| 268 | + } |
| 269 | + |
| 270 | + bool is_enabled(kcenon::common::interfaces::log_level level) const override { |
| 271 | + return level >= min_level_; |
| 272 | + } |
| 273 | + |
| 274 | + void flush() override { |
| 275 | + std::cout.flush(); |
| 276 | + } |
| 277 | + |
| 278 | +private: |
| 279 | + kcenon::common::interfaces::log_level min_level_ |
| 280 | + = kcenon::common::interfaces::log_level::info; |
| 281 | + |
| 282 | + std::string level_to_string(kcenon::common::interfaces::log_level level) { |
| 283 | + switch (level) { |
| 284 | + case kcenon::common::interfaces::log_level::trace: return "TRACE"; |
| 285 | + case kcenon::common::interfaces::log_level::debug: return "DEBUG"; |
| 286 | + case kcenon::common::interfaces::log_level::info: return "INFO"; |
| 287 | + case kcenon::common::interfaces::log_level::warning: return "WARN"; |
| 288 | + case kcenon::common::interfaces::log_level::error: return "ERROR"; |
| 289 | + case kcenon::common::interfaces::log_level::critical: return "CRIT"; |
| 290 | + default: return "???"; |
| 291 | + } |
| 292 | + } |
| 293 | +}; |
| 294 | + |
| 295 | +void setup() { |
| 296 | + auto logger = std::make_shared<ConsoleLogger>(); |
| 297 | + auto& registry = kcenon::common::interfaces::ILoggerRegistry::instance(); |
| 298 | + registry.set_default_logger(logger); |
| 299 | +} |
| 300 | + |
| 301 | +} // namespace myapp |
| 302 | +``` |
| 303 | +
|
| 304 | +## FAQ |
| 305 | +
|
| 306 | +### Q: What if I don't have common_system? |
| 307 | +
|
| 308 | +A: If your project doesn't use common_system, you have two options: |
| 309 | +1. Add common_system as a dependency (recommended) |
| 310 | +2. Continue using the deprecated interface until you can migrate |
| 311 | +
|
| 312 | +### Q: Will my code still compile after the deprecation? |
| 313 | +
|
| 314 | +A: Yes, for v1.x releases. You'll see compiler warnings but the code will work. In v2.0, the deprecated interface will be removed and compilation will fail. |
| 315 | +
|
| 316 | +### Q: How do I silence deprecation warnings temporarily? |
| 317 | +
|
| 318 | +A: Use compiler-specific pragmas: |
| 319 | +```cpp |
| 320 | +#if defined(__clang__) || defined(__GNUC__) |
| 321 | +#pragma GCC diagnostic push |
| 322 | +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" |
| 323 | +#endif |
| 324 | +
|
| 325 | +// Your code using deprecated interface |
| 326 | +
|
| 327 | +#if defined(__clang__) || defined(__GNUC__) |
| 328 | +#pragma GCC diagnostic pop |
| 329 | +#endif |
| 330 | +``` |
| 331 | + |
| 332 | +### Q: Is there an adapter for gradual migration? |
| 333 | + |
| 334 | +A: Yes, consider using `logger_system_adapter` which bridges between different logger interfaces. |
| 335 | + |
| 336 | +## Related Documents |
| 337 | + |
| 338 | +- [LOG_LEVEL_MIGRATION_GUIDE.md](LOG_LEVEL_MIGRATION_GUIDE.md) - Log level enum migration |
| 339 | +- [Issue #263](https://github.com/kcenon/thread_system/issues/263) - Deprecation tracking issue |
| 340 | + |
| 341 | +## Revision History |
| 342 | + |
| 343 | +| Version | Date | Changes | |
| 344 | +|---------|------|---------| |
| 345 | +| 1.0 | 2025-12-06 | Initial release | |
0 commit comments