Skip to main content

LogManager.ixx File

Central manager for scoped loggers with configurable sinks. More...

Included Headers

#include <string> #include <unordered_map> #include <unordered_set> #include <memory> #include <mutex> #include <vector> #include <cstring> #include <helios.util.log.Logger> #include <helios.util.log.LogSink>

Namespaces Index

namespacehelios
namespaceutil

Utility functions and helper classes. More...

namespacelog

Logging system with self-registering output sinks. More...

Classes Index

classLogManager

LogManager for managing scoped Loggers and global sink configuration. More...

Macro Definitions Index

#defineLOGGING_ENABLED   true

Description

Central manager for scoped loggers with configurable sinks.

Macro Definitions

LOGGING_ENABLED

#define LOGGING_ENABLED   true

Definition at line 20 of file LogManager.ixx.

20#define LOGGING_ENABLED true

File Listing

The file content with the documentation metadata removed is:

1/**
2 * @file LogManager.ixx
3 * @brief Central manager for scoped loggers with configurable sinks.
4 */
5module;
6
7#include <string>
8#include <unordered_map>
9#include <unordered_set>
10#include <memory>
11#include <mutex>
12#include <vector>
13#include <cstring>
14
15export module helios.util.log.LogManager;
16
17import helios.util.log.LogSink;
18import helios.util.log.Logger;
19
20#define LOGGING_ENABLED true
21export namespace helios::util::log {
22
23 /**
24 * @brief LogManager for managing scoped Loggers and global sink configuration.
25 *
26 * The LogManager provides centralized control over logging output destinations.
27 * Sinks register themselves with a unique type identifier, allowing dynamic
28 * enable/disable without compile-time knowledge of all sink types.
29 *
30 * ```cpp
31 * // Register sinks (sinks define their own TYPE_ID)
32 * LogManager::getInstance().registerSink(std::make_shared<ConsoleSink>());
33 * LogManager::getInstance().registerSink(std::make_shared<ImGuiLogSink>(widget));
34 *
35 * // Enable/disable by type identifier
36 * LogManager::getInstance().enableSink("console");
37 * LogManager::getInstance().disableSink("imgui");
38 *
39 * // Check status
40 * if (LogManager::getInstance().isSinkEnabled("console")) { ... }
41 * ```
42 */
43 class LogManager {
44
45 private:
46
47 /**
48 * @brief Flag indicating whether log output should be globally enabled or disabled.
49 */
50 bool loggingEnabled_ = LOGGING_ENABLED;
51
52 /**
53 * @brief Set of currently enabled sink type identifiers.
54 */
55 std::unordered_set<std::string> enabledSinks_;
56
57 /**
58 * @brief Registered sinks (all available sinks, regardless of enabled state).
59 */
60 std::vector<std::shared_ptr<LogSink>> registeredSinks_;
61
62 /**
63 * @brief Unordered map holding unique pointers to the loggers managed
64 * by this class, guaranteed to be not null.
65 */
66 std::unordered_map<std::string, std::unique_ptr<Logger>> loggers_;
67
68 /**
69 * @brief Default logger if a logger for a specific scope was not found.
70 */
71 const std::unique_ptr<Logger> defaultLogger_;
72
73 /**
74 * @brief Mutex providing mutually exclusive access to the loggers map.
75 */
76 mutable std::mutex mapMutex_;
77
78 /**
79 * @brief Mutex for sink access.
80 */
81 mutable std::mutex sinkMutex_;
82
83 /**
84 * @brief Creates the LogManager and registers an unscoped default logger.
85 */
86 LogManager() : defaultLogger_(std::make_unique<Logger>("default")) {}
87
88 /**
89 * @brief Reconfigures all logger sinks based on currently enabled sinks.
90 *
91 * Called internally after sink enable/disable changes.
92 */
93 void updateLoggerSinks() {
94 // Called with sinkMutex_ already held
95 std::lock_guard<std::mutex> mapLock(mapMutex_);
96
97 // Update default logger
98 defaultLogger_->clearSinks();
99 for (const auto& sink : registeredSinks_) {
100 if (sink && enabledSinks_.contains(sink->typeId())) {
101 defaultLogger_->addSink(sink);
102 }
103 }
104
105 // Update all registered loggers
106 for (auto& [scope, logger] : loggers_) {
107 logger->clearSinks();
108 for (const auto& sink : registeredSinks_) {
109 if (sink && enabledSinks_.contains(sink->typeId())) {
110 logger->addSink(sink);
111 }
112 }
113 }
114 }
115
116 public:
117
118 /**
119 * @brief Convenience accessor to obtain a logger for a textual scope via the singleton.
120 *
121 * This static helper forwards to `LogManager::getInstance().logger(scope)` and
122 * returns a reference to the logger registered for the given scope.
123 *
124 * @param scope The textual scope name of the requested logger.
125 *
126 * @return The logger registered with the scope, or the default logger if none was found.
127 */
128 static const Logger& loggerForScope(const std::string& scope) noexcept {
130 }
131
132 ~LogManager() = default;
133
134 /**
135 * Enforce singleton (see Meyer's Singleton)
136 *
137 * @see https://en.wikipedia.org/wiki/Singleton_pattern
138 */
139 LogManager(const LogManager&) = delete;
140 LogManager& operator=(const LogManager&) = delete;
141
142 /**
143 * @brief Returns the LogManager singleton instance.
144 *
145 * @return Reference to the global LogManager instance.
146 */
147 static LogManager& getInstance() noexcept {
148 static LogManager instance;
149
150 return instance;
151 }
152
153 /**
154 * @brief Returns a const reference to the default logger managed with this LogManager.
155 *
156 * @return The default Logger instance.
157 */
158 [[nodiscard]] const Logger& logger() const noexcept {
159 return *defaultLogger_;
160 }
161
162 /**
163 * @brief Returns a const reference to the logger instance for the specified scope.
164 *
165 * Will fall back to the default logger if the scope was not registered yet.
166 * This method is thread safe for map look-ups.
167 *
168 * @param scope The textual scope name of the requested logger.
169 *
170 * @return The logger registered with the scope, or the default logger if
171 * none was found.
172 */
173 [[nodiscard]] const Logger& logger(const std::string& scope) const noexcept {
174 // mapMutex_ is automatically released when going out of scope
175 std::lock_guard<std::mutex> lock(mapMutex_);
176
177 auto log = loggers_.find(scope);
178 if (log != loggers_.end()) {
179 return *(log->second);
180 }
181
182 return *defaultLogger_;
183 }
184
185 /**
186 * @brief Registers a new logger with this manager.
187 *
188 * This method is thread safe for map modifications.
189 *
190 * @param scope The scope requested for the logger to create.
191 *
192 * @return The logger registered with the scope, or the logger already registered
193 * with the LogManager under the given scope.
194 */
195 [[nodiscard]] Logger& registerLogger(const std::string& scope) noexcept {
196 // mapMutex_ is automatically released when going out of scope
197 std::lock_guard<std::mutex> lock(mapMutex_);
198
199 if (auto log = loggers_.find(scope); log != loggers_.end()) {
200 return *(log->second);
201 }
202
203 auto logger = std::make_unique<Logger>(scope);
204 loggers_[scope] = std::move(logger);
205 loggers_[scope]->enable(loggingEnabled_);
206
207 // Configure sinks for new logger
208 {
209 std::lock_guard<std::mutex> sinkLock(sinkMutex_);
210 for (const auto& sink : registeredSinks_) {
211 if (sink && enabledSinks_.contains(sink->typeId())) {
212 loggers_[scope]->addSink(sink);
213 }
214 }
215 }
216
217 return *loggers_[scope];
218 }
219
220 /**
221 * @brief Enables or disables all log output of the Loggers registered with this LogManager.
222 *
223 * @param enable True to enable log output with the registered loggers, otherwise false.
224 */
225 void enableLogging(bool enable) noexcept {
226 std::lock_guard<std::mutex> lock(mapMutex_);
227
228 if (loggingEnabled_ == enable) {
229 return;
230 }
231
232 loggingEnabled_ = enable;
233
234 for (auto& [fst, snd]: loggers_) {
235 snd->enable(enable);
236 }
237 }
238
239 /**
240 * @brief Sets the filter scope for the logger.
241 *
242 * Will do nothing if logging is not enabled. If the logger for the scope does not
243 * exist, it will get implicitly created.
244 *
245 * @param scope The scope to filter. Only log messages with this scope will be logged.
246 */
247 void setScopeFilter(const std::string& scope) noexcept {
248 if (!loggingEnabled_) {
249 return;
250 }
251
252 // Make sure the logger exists first (this acquires mapMutex_ internally)
253 std::ignore = LogManager::getInstance().registerLogger(scope);
254
255 // Now lock and update the filter
256 std::lock_guard<std::mutex> lock(mapMutex_);
257
258 for (auto& [fst, snd] : loggers_) {
259 if (fst == scope) {
260 snd->enable(true);
261 } else {
262 snd->enable(false);
263 }
264 }
265 }
266
267 // ===== Sink Management =====
268
269 /**
270 * @brief Registers a sink and enables it by default.
271 *
272 * The sink is added to the pool and immediately enabled for output.
273 *
274 * @param sink The sink to register.
275 */
276 void registerSink(std::shared_ptr<LogSink> sink) {
277 registerSink(std::move(sink), true);
278 }
279
280 /**
281 * @brief Registers a sink with optional auto-enable.
282 *
283 * @param sink The sink to register.
284 * @param enabled Whether to enable the sink immediately (default: true).
285 */
286 void registerSink(std::shared_ptr<LogSink> sink, bool enabled) {
287 if (!sink) return;
288
289 std::lock_guard<std::mutex> lock(sinkMutex_);
290
291 // Check if sink with same typeId is already registered
292 bool alreadyRegistered = false;
293 for (const auto& existing : registeredSinks_) {
294 if (existing && std::strcmp(existing->typeId(), sink->typeId()) == 0) {
295 alreadyRegistered = true;
296 break;
297 }
298 }
299
300 if (!alreadyRegistered) {
301 registeredSinks_.push_back(sink);
302 }
303
304 if (enabled) {
305 enabledSinks_.insert(sink->typeId());
306 }
307
308 updateLoggerSinks();
309 }
310
311 /**
312 * @brief Enables a sink by its type identifier.
313 *
314 * If a sink with the given typeId is registered, it will be enabled.
315 * If no sink with that typeId is registered, this call has no effect.
316 *
317 * @param typeId The unique type identifier of the sink (e.g., "console", "imgui").
318 */
319 void enableSink(SinkTypeId typeId) {
320 std::lock_guard<std::mutex> lock(sinkMutex_);
321 enabledSinks_.insert(typeId);
322 updateLoggerSinks();
323 }
324
325 /**
326 * @brief Enables a sink, registering it first if necessary.
327 *
328 * If the sink is not yet registered, it will be added to the pool.
329 * The sink is then enabled for output.
330 *
331 * @param sink The sink instance to enable (and register if needed).
332 */
333 void enableSink(std::shared_ptr<LogSink> sink) {
334 if (!sink) return;
335
336 std::lock_guard<std::mutex> lock(sinkMutex_);
337
338 // Check if already registered
339 bool alreadyRegistered = false;
340 for (const auto& existing : registeredSinks_) {
341 if (existing && std::strcmp(existing->typeId(), sink->typeId()) == 0) {
342 alreadyRegistered = true;
343 break;
344 }
345 }
346
347 // Auto-register if not already registered
348 if (!alreadyRegistered) {
349 registeredSinks_.push_back(sink);
350 }
351
352 enabledSinks_.insert(sink->typeId());
353 updateLoggerSinks();
354 }
355
356 /**
357 * @brief Disables a sink by its type identifier.
358 *
359 * @param typeId The unique type identifier of the sink.
360 */
361 void disableSink(SinkTypeId typeId) {
362 std::lock_guard<std::mutex> lock(sinkMutex_);
363 enabledSinks_.erase(typeId);
364 updateLoggerSinks();
365 }
366
367 /**
368 * @brief Disables a sink by instance.
369 *
370 * @param sink The sink instance to disable.
371 */
372 void disableSink(std::shared_ptr<LogSink> sink) {
373 if (!sink) return;
374 disableSink(sink->typeId());
375 }
376
377 /**
378 * @brief Checks if a sink with the given type identifier is currently enabled.
379 *
380 * @param typeId The unique type identifier of the sink.
381 *
382 * @return True if the sink is enabled.
383 */
384 [[nodiscard]] bool isSinkEnabled(SinkTypeId typeId) const noexcept {
385 std::lock_guard<std::mutex> lock(sinkMutex_);
386 return enabledSinks_.contains(typeId);
387 }
388
389 /**
390 * @brief Enables all registered sinks.
391 */
393 std::lock_guard<std::mutex> lock(sinkMutex_);
394 for (const auto& sink : registeredSinks_) {
395 if (sink) {
396 enabledSinks_.insert(sink->typeId());
397 }
398 }
399 updateLoggerSinks();
400 }
401
402 /**
403 * @brief Disables all sinks.
404 */
406 std::lock_guard<std::mutex> lock(sinkMutex_);
407 enabledSinks_.clear();
408 updateLoggerSinks();
409 }
410 };
411
412}

Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.15.0.