Logger System 0.1.3
High-performance C++20 thread-safe logging system with asynchronous capabilities
Loading...
Searching...
No Matches
log_context_scope.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
5#pragma once
6
37
38#include <algorithm>
39#include <optional>
40#include <string>
41#include <vector>
42
43namespace kcenon::logger {
44
45// Forward declaration
46class logger;
47
76public:
82 static void set(const std::string& key, const std::string& value) {
83 get_storage()[key] = value;
84 get_has_context() = true;
85 }
86
92 static void set(const std::string& key, const char* value) {
93 get_storage()[key] = std::string(value);
94 get_has_context() = true;
95 }
96
102 static void set(const std::string& key, int64_t value) {
103 get_storage()[key] = value;
104 get_has_context() = true;
105 }
106
112 static void set(const std::string& key, double value) {
113 get_storage()[key] = value;
114 get_has_context() = true;
115 }
116
122 static void set(const std::string& key, bool value) {
123 get_storage()[key] = value;
124 get_has_context() = true;
125 }
126
132 static void set(const std::string& key, const log_value& value) {
133 get_storage()[key] = value;
134 get_has_context() = true;
135 }
136
141 static void set_all(const log_fields& fields) {
142 for (const auto& [key, value] : fields) {
143 get_storage()[key] = value;
144 }
145 if (!fields.empty()) {
146 get_has_context() = true;
147 }
148 }
149
154 static void remove(const std::string& key) {
155 get_storage().erase(key);
156 if (get_storage().empty()) {
157 get_has_context() = false;
158 }
159 }
160
165 [[nodiscard]] static log_fields get() {
166 if (get_has_context()) {
167 return get_storage();
168 }
169 return {};
170 }
171
175 static void clear() {
176 get_storage().clear();
177 get_has_context() = false;
178 }
179
184 [[nodiscard]] static bool has_context() {
185 return get_has_context();
186 }
187
193 [[nodiscard]] static std::optional<log_value> get_field(const std::string& key) {
194 if (!get_has_context()) {
195 return std::nullopt;
196 }
197 auto& storage = get_storage();
198 auto it = storage.find(key);
199 if (it != storage.end()) {
200 return it->second;
201 }
202 return std::nullopt;
203 }
204
205private:
207 thread_local log_fields storage;
208 return storage;
209 }
210
211 static bool& get_has_context() {
212 thread_local bool has_ctx = false;
213 return has_ctx;
214 }
215};
216
252public:
261 explicit log_context_scope(const log_fields& fields)
262 : previous_context_(log_context_storage::get()),
263 had_previous_(log_context_storage::has_context()) {
264 for (const auto& [key, value] : fields) {
265 // Track which keys we're adding (to remove them on destruction)
266 if (previous_context_.find(key) == previous_context_.end()) {
267 added_keys_.push_back(key);
268 }
269 log_context_storage::set(key, value);
270 }
271 }
272
281 log_context_scope(logger& log, const log_fields& fields);
282
290 // Remove keys that were added by this scope
291 for (const auto& key : added_keys_) {
292 log_context_storage::remove(key);
293 }
294
295 // Restore overridden values from previous context
296 if (had_previous_) {
297 for (const auto& [key, value] : previous_context_) {
298 // Only restore if the key was overridden (not newly added)
299 if (std::find(added_keys_.begin(), added_keys_.end(), key) == added_keys_.end()) {
300 log_context_storage::set(key, value);
301 }
302 }
303 }
304
305 // Clear logger context if we set it
306 if (logger_ != nullptr) {
307 for (const auto& key : added_keys_) {
308 remove_logger_context(key);
309 }
310 }
311 }
312
313 // Non-copyable, non-movable
318
319private:
320 void remove_logger_context(const std::string& key);
321
324 std::vector<std::string> added_keys_;
325 logger* logger_ = nullptr;
326};
327
348public:
352 scoped_context(const std::string& key, const std::string& value)
353 : key_(key), had_previous_(false) {
354 auto prev = log_context_storage::get_field(key);
355 if (prev) {
356 had_previous_ = true;
357 previous_value_ = *prev;
358 }
359 log_context_storage::set(key, value);
360 }
361
365 scoped_context(const std::string& key, int64_t value)
366 : key_(key), had_previous_(false) {
367 auto prev = log_context_storage::get_field(key);
368 if (prev) {
369 had_previous_ = true;
370 previous_value_ = *prev;
371 }
372 log_context_storage::set(key, value);
373 }
374
378 scoped_context(const std::string& key, int value)
379 : scoped_context(key, static_cast<int64_t>(value)) {}
380
384 scoped_context(const std::string& key, double value)
385 : key_(key), had_previous_(false) {
386 auto prev = log_context_storage::get_field(key);
387 if (prev) {
388 had_previous_ = true;
389 previous_value_ = *prev;
390 }
391 log_context_storage::set(key, value);
392 }
393
397 scoped_context(const std::string& key, bool value)
398 : key_(key), had_previous_(false) {
399 auto prev = log_context_storage::get_field(key);
400 if (prev) {
401 had_previous_ = true;
402 previous_value_ = *prev;
403 }
404 log_context_storage::set(key, value);
405 }
406
417
418 // Non-copyable, non-movable
423
424private:
425 std::string key_;
428};
429
430} // namespace kcenon::logger
RAII guard for structured logging context.
log_context_scope(const log_fields &fields)
Construct scope with initial fields.
std::vector< std::string > added_keys_
log_context_scope(log_context_scope &&)=delete
log_context_scope & operator=(log_context_scope &&)=delete
log_context_scope(const log_context_scope &)=delete
~log_context_scope()
Destructor - restores previous context.
log_context_scope & operator=(const log_context_scope &)=delete
Thread-local storage for structured logging context fields.
static void set(const std::string &key, bool value)
Set a boolean context field for the current thread.
static void set_all(const log_fields &fields)
Set multiple context fields at once.
static void set(const std::string &key, const std::string &value)
Set a string context field for the current thread.
static void set(const std::string &key, double value)
Set a double context field for the current thread.
static void remove(const std::string &key)
Remove a context field for the current thread.
static log_fields get()
Get all context fields for the current thread.
static std::optional< log_value > get_field(const std::string &key)
Get a specific field value if it exists.
static bool has_context()
Check if any context fields are set for the current thread.
static void set(const std::string &key, const char *value)
Set a C-string context field for the current thread.
static void clear()
Clear all context fields for the current thread.
static void set(const std::string &key, int64_t value)
Set an integer context field for the current thread.
static void set(const std::string &key, const log_value &value)
Set a log_value context field for the current thread.
Convenience class for setting a single context field with RAII.
scoped_context(const std::string &key, int64_t value)
Construct with integer value.
scoped_context(const scoped_context &)=delete
~scoped_context()
Destructor - restores or removes the context field.
scoped_context(const std::string &key, const std::string &value)
Construct with string value.
scoped_context & operator=(scoped_context &&)=delete
scoped_context(const std::string &key, bool value)
Construct with boolean value.
scoped_context(const std::string &key, int value)
Construct with int value (converts to int64_t)
scoped_context(scoped_context &&)=delete
scoped_context & operator=(const scoped_context &)=delete
scoped_context(const std::string &key, double value)
Construct with double value.
Data structures for representing log entries and source locations kcenon.
DLL export/import macros for logger_system shared library support.
#define LOGGER_SYSTEM_API
std::unordered_map< std::string, log_value > log_fields
Type alias for structured fields map.
Definition log_entry.h:75
std::variant< std::string, int64_t, double, bool > log_value
Value type for structured logging fields.
Definition log_entry.h:69