utility 2026.1.9
A comprehensive C++ utilities library tailored for the development of modern desktop and extended reality (XR) applications.
Loading...
Searching...
No Matches
logger.hpp
Go to the documentation of this file.
1/*
2 Copyright (c) 2026 ETIB Corporation
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in
6 the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 of the Software, and to permit persons to whom the Software is furnished to do
9 so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 */
22
36#pragma once
37
38#include <atomic>
39#include <limits>
40#include <memory>
41#include <mutex>
42#include <source_location>
43#include <sstream>
44#include <string>
45#include <type_traits>
46#include <utility>
47
48namespace utility::logging
49{
50
54 enum class LogLevel {
60 };
61
68 enum class FlushPolicy {
69 BUFFERED,
70 ALWAYS,
71 NEVER
72 };
73
77 struct LogRecord {
79 std::string message;
80 std::string timestamp;
81 std::string
83 std::string file;
84 int line = 0;
85 std::string function;
86 };
87
95 class Logger
96 {
97 private:
98 std::string _name;
99 std::atomic<LogLevel> _minLevel {
101 };
102 std::atomic<FlushPolicy> _flushPolicy {
103 FlushPolicy::BUFFERED
104 };
105
115 bool isEnabled(LogLevel level) const noexcept
116 {
117 return levelValue(level)
118 >= levelValue(_minLevel.load(std::memory_order_relaxed));
119 }
120
121 protected:
122 mutable std::mutex _mutex;
123
124 public:
132 {
133 private:
134 Logger *_logger;
135 LogLevel _level;
136 std::source_location _location;
137 std::unique_ptr<std::ostringstream>
138 _stream;
139 bool _active;
140
145 std::ostringstream &ensureStream(void)
146 {
147 if (!_stream) {
148 _stream = std::make_unique<std::ostringstream>();
149 }
150 return *_stream;
151 }
152
153 public:
161 LogMessage(Logger *logger, LogLevel level, std::source_location loc,
162 bool active)
163 : _logger(logger)
164 , _level(level)
165 , _location(loc)
166 , _active(active)
167 {
168 }
169
176 template<typename T> LogMessage &operator<<(T &&value)
177 {
178 if (_active) {
179 ensureStream() << std::forward<T>(value);
180 }
181 return *this;
182 }
183
189 LogMessage &operator<<(std::ostream &(*manip)(std::ostream &))
190 {
191 if (_active) {
192 ensureStream() << manip;
193 }
194 return *this;
195 }
196
206 {
207 if (!_active || !_logger || !_stream) {
208 return;
209 }
210 // `str() &&` (C++20) moves the buffer out of the stream, avoiding
211 // a copy; the stream is not read again afterwards. On toolchains
212 // without the rvalue overload this falls back to a copy.
213 LogRecord record {
214 .level = _level,
215 .message = std::move(*_stream).str(),
216 .timestamp = Logger::getTimestamp(),
217 .loggerName = _logger->getName(),
218 };
219 if (_level == LogLevel::DEBUG_LEVEL) {
220 record.file = _location.file_name();
221 record.line = static_cast<int>(_location.line());
222 record.function = _location.function_name();
223 }
224 std::lock_guard<std::mutex> guard(_logger->_mutex);
225 try {
226 _logger->output(record);
227 } catch (...) {
228 // Logging must never become a terminate path.
229 }
230 }
231 };
232
233 public:
238 Logger(const std::string &name);
239
243 virtual ~Logger(void) = default;
244
255 static constexpr LogLevel defaultMinLevel(void) noexcept
256 {
257#if defined(NDEBUG)
258 return LogLevel::WARNING_LEVEL;
259#else
260 return LogLevel::DEBUG_LEVEL;
261#endif
262 }
263
269 void setMinLevel(LogLevel level) noexcept;
270
275 LogLevel getMinLevel(void) const noexcept;
276
281 void setFlushPolicy(FlushPolicy policy) noexcept;
282
287 FlushPolicy getFlushPolicy(void) const noexcept;
288
294 static std::string levelToString(LogLevel level);
295
300 static std::string getTimestamp(void);
301
312 static constexpr int levelValue(LogLevel level) noexcept
313 {
314 switch (level) {
315 case LogLevel::DEBUG_LEVEL:
316 return 0;
317 case LogLevel::INFO_LEVEL:
318 return 1;
319 case LogLevel::WARNING_LEVEL:
320 return 2;
321 case LogLevel::ERROR_LEVEL:
322 return 3;
323 default:
324 // Return a high sentinel so an unknown/future level is
325 // treated as most severe and never silently dropped.
326 return std::numeric_limits<int>::max();
327 }
328 }
329
335 LogMessage
336 debug(std::source_location loc = std::source_location::current())
337 {
338 return LogMessage(this, LogLevel::DEBUG_LEVEL, loc,
339 isEnabled(LogLevel::DEBUG_LEVEL));
340 }
341
347 LogMessage
348 info(std::source_location loc = std::source_location::current())
349 {
350 return LogMessage(this, LogLevel::INFO_LEVEL, loc,
351 isEnabled(LogLevel::INFO_LEVEL));
352 }
353
359 LogMessage
360 warning(std::source_location loc = std::source_location::current())
361 {
362 return LogMessage(this, LogLevel::WARNING_LEVEL, loc,
363 isEnabled(LogLevel::WARNING_LEVEL));
364 }
365
371 LogMessage
372 error(std::source_location loc = std::source_location::current())
373 {
374 return LogMessage(this, LogLevel::ERROR_LEVEL, loc,
375 isEnabled(LogLevel::ERROR_LEVEL));
376 }
377
384 LogMessage
386 std::source_location loc = std::source_location::current())
387 {
388 return LogMessage(this, level, loc, isEnabled(level));
389 }
390
395 virtual void output(const LogRecord &record) = 0;
396
401 const std::string &getName(void) const
402 {
403 return _name;
404 }
405 };
406
411 template<typename Type>
412 concept InheritFromLogger = std::is_base_of_v<Logger, Type>;
413
414} // namespace utility::logging
Proxy object returned by debug/info/warning/error.
Definition logger.hpp:132
~LogMessage()
Destructor emits the accumulated message via the parent logger.
Definition logger.hpp:205
LogMessage & operator<<(T &&value)
Stream any value into the log buffer.
Definition logger.hpp:176
LogMessage & operator<<(std::ostream &(*manip)(std::ostream &))
Stream an ostream manipulator (e.g. std::endl).
Definition logger.hpp:189
LogMessage(Logger *logger, LogLevel level, std::source_location loc, bool active)
Construct a LogMessage.
Definition logger.hpp:161
Abstract logger interface defining stream-style logging operations.
Definition logger.hpp:96
std::mutex _mutex
Serializes output access.
Definition logger.hpp:122
LogLevel getMinLevel(void) const noexcept
Get the current minimum log level.
Definition logger.cpp:134
static constexpr LogLevel defaultMinLevel(void) noexcept
Default minimum level for the current build type.
Definition logger.hpp:255
virtual void output(const LogRecord &record)=0
Output a fully-formed log record.
const std::string & getName(void) const
Get the logger name.
Definition logger.hpp:401
LogMessage info(std::source_location loc=std::source_location::current())
Begin an info-level log message.
Definition logger.hpp:348
FlushPolicy getFlushPolicy(void) const noexcept
Get the current output flush policy.
Definition logger.cpp:144
void setMinLevel(LogLevel level) noexcept
Set the minimum log level. Messages below this level are suppressed.
Definition logger.cpp:129
LogMessage debug(std::source_location loc=std::source_location::current())
Begin a debug-level log message.
Definition logger.hpp:336
LogMessage error(std::source_location loc=std::source_location::current())
Begin an error-level log message.
Definition logger.hpp:372
static std::string levelToString(LogLevel level)
Get string representation of log level.
Definition logger.cpp:71
void setFlushPolicy(FlushPolicy policy) noexcept
Set the output flush policy.
Definition logger.cpp:139
LogMessage warning(std::source_location loc=std::source_location::current())
Begin a warning-level log message.
Definition logger.hpp:360
static std::string getTimestamp(void)
Get current timestamp as formatted string.
Definition logger.cpp:87
static constexpr int levelValue(LogLevel level) noexcept
Get numeric value of a log level for comparison.
Definition logger.hpp:312
LogMessage log(LogLevel level, std::source_location loc=std::source_location::current())
Begin a log message with dynamic level.
Definition logger.hpp:385
virtual ~Logger(void)=default
Virtual destructor for proper cleanup.
Concept to ensure a type inherits from Logger.
Definition logger.hpp:412
FlushPolicy
Controls when a stream-backed logger flushes its output.
Definition logger.hpp:68
@ NEVER
Never flush except on destruction.
@ BUFFERED
Buffer debug/info; flush warnings/errors (default)
@ ALWAYS
Flush after every message (legacy behavior)
LogLevel
Log severity levels.
Definition logger.hpp:54
@ ERROR_LEVEL
Error messages for serious problems.
@ DEBUG_LEVEL
Detailed debugging information.
@ INFO_LEVEL
General informational messages.
Structured log record carrying metadata for a single log entry.
Definition logger.hpp:77
LogLevel level
Severity level.
Definition logger.hpp:78
int line
Source line number.
Definition logger.hpp:84
std::string file
Source file path.
Definition logger.hpp:83
std::string loggerName
Name of the logger that emitted the record.
Definition logger.hpp:82
std::string timestamp
Formatted timestamp string.
Definition logger.hpp:80
std::string function
Function name.
Definition logger.hpp:85
std::string message
Log message content.
Definition logger.hpp:79