libpromeki main
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
logger.h
Go to the documentation of this file.
1
8#pragma once
9
10#include <thread>
11#include <variant>
12#include <functional>
14#include <promeki/core/string.h>
16#include <promeki/core/queue.h>
17#include <promeki/core/map.h>
18#include <promeki/core/mutex.h>
19#include <promeki/core/atomic.h>
23
25
27static consteval const char *sourceFileName(const char *path) {
28 const char *ret = path;
29 for(const char *p = path; *p; ++p) {
30 if(*p == '/' || *p == '\\') ret = p + 1;
31 }
32 return ret;
33}
34
35#ifdef __FILE_NAME__
36#define PROMEKI_SOURCE_FILE __FILE_NAME__
37#else
38#define PROMEKI_SOURCE_FILE promeki::sourceFileName(__FILE__)
39#endif
40
41// Variadic macros for logging at various levels
42
43#define PROMEKI_DEBUG(name) \
44 namespace { \
45 [[maybe_unused]] static const char *_promeki_debug_name = PROMEKI_STRINGIFY(name); \
46 [[maybe_unused]] static bool _promeki_debug_enabled = \
47 promekiRegisterDebug(&_promeki_debug_enabled, PROMEKI_STRINGIFY(name), PROMEKI_SOURCE_FILE, __LINE__); \
48 }
49
50#define promekiLogImpl(_plevel, format, ...) \
51 do { if(!(_plevel) || (_plevel) >= Logger::defaultLogger().level()) \
52 Logger::defaultLogger().log(_plevel, PROMEKI_SOURCE_FILE, __LINE__, String::sprintf(format, ##__VA_ARGS__)); \
53 } while(0)
54#define promekiLog(level, format, ...) promekiLogImpl(level, format, ##__VA_ARGS__)
55#define promekiLogSync() Logger::defaultLogger().sync()
56#define promekiLogStackTrace(_plevel) \
57 do { if(!(_plevel) || (_plevel) >= Logger::defaultLogger().level()) \
58 Logger::defaultLogger().log(_plevel, PROMEKI_SOURCE_FILE, __LINE__, promekiStackTrace()); \
59 } while(0)
60
61#ifdef PROMEKI_DEBUG_ENABLE
62#define promekiDebug(format, ...) if(_promeki_debug_enabled) { promekiLog(Logger::LogLevel::Debug, format, ##__VA_ARGS__); }
63#else
64#define promekiDebug(format, ...)
65#endif
66
67#define promekiInfo(format, ...) promekiLog(Logger::LogLevel::Info, format, ##__VA_ARGS__)
68#define promekiWarn(format, ...) promekiLog(Logger::LogLevel::Warn, format, ##__VA_ARGS__)
69#define promekiErr(format, ...) promekiLog(Logger::LogLevel::Err, format, ##__VA_ARGS__)
70
71bool promekiRegisterDebug(bool *enabler, const char *name, const char *file, int line);
72
73#define PROMEKI_BENCHMARK_BEGIN(name) \
74 TimeStamp _promeki_debug_timestamp_##name; \
75 if(_promeki_debug_enabled) { _promeki_debug_timestamp_##name = TimeStamp::now(); }
76
77#define PROMEKI_BENCHMARK_END(name) \
78 if(_promeki_debug_enabled) { \
79 Logger::defaultLogger().log(Logger::LogLevel::Debug, __FILE__, __LINE__, String::sprintf("[%s] %s took %.9lf sec", \
80 _promeki_debug_name, PROMEKI_STRINGIFY(name), _promeki_debug_timestamp_##name.elapsedSeconds())); \
81 }
82
102class Logger {
103 public:
105 enum LogLevel {
106 Force = 0,
107 Debug = 1,
108 Info = 2,
109 Warn = 3,
110 Err = 4
111 };
112
114 struct LogEntry {
115 DateTime ts;
116 LogLevel level;
117 const char * file;
118 int line;
119 uint64_t threadId;
120 String msg;
121 };
122
124 struct LogFormat {
125 const LogEntry *entry;
126 const String *threadName;
127 };
128
135 using LogFormatter = std::function<String(const LogFormat &fmt)>;
136
142
149
160 static void setThreadName(const String &name);
161
169
176
179
182 _queue.emplace(CmdTerminate{});
183 _thread.join();
184 }
185
190 int level() const {
191 return _level.value();
192 }
193
205 void log(LogLevel loglevel, const char *file, int line, const String &msg);
206
218 void log(LogLevel loglevel, const char *file, int line, const StringList &lines);
219
224 void setLogFile(const String &filename) {
225 _queue.emplace(CmdSetFile{filename});
226 }
227
233 _level.setValue(level);
234 log(Force, "LOGGER", 0, String::sprintf("Logging Level Changed to %d", level));
235 return;
236 }
237
243 _consoleLogging.setValue(val);
244 return;
245 }
246
251 Mutex::Locker lock(_formatterMutex);
252 return _fileFormatter;
253 }
254
259 Mutex::Locker lock(_formatterMutex);
260 return _consoleFormatter;
261 }
262
269 {
270 Mutex::Locker lock(_formatterMutex);
271 _fileFormatter = formatter ? formatter : defaultFileFormatter();
272 }
273 _queue.emplace(CmdSetFormatter{std::move(formatter), false});
274 }
275
282 {
283 Mutex::Locker lock(_formatterMutex);
284 _consoleFormatter = formatter ? formatter : defaultConsoleFormatter();
285 }
286 _queue.emplace(CmdSetFormatter{std::move(formatter), true});
287 }
288
296 Error sync(unsigned int timeoutMs = 0) {
297 auto p = std::make_shared<Promise<void>>();
298 Future<void> f = p->future();
299 _queue.emplace(CmdSync{std::move(p)});
300 if(timeoutMs == 0) {
301 f.waitForFinished();
302 return Error::Ok;
303 }
304 return f.waitForFinished(timeoutMs);
305 }
306
307 private:
308
309 struct CmdSetThreadName {
310 uint64_t threadId;
311 String name;
312 };
313
314 struct CmdSetFile {
315 String filename;
316 };
317
318 struct CmdSetFormatter {
319 LogFormatter formatter;
320 bool console;
321 };
322
323 struct CmdSync {
324 std::shared_ptr<Promise<void>> promise;
325 };
326
327 struct CmdTerminate {};
328
329 using Command = std::variant<LogEntry, CmdSetThreadName, CmdSetFile, CmdSetFormatter, CmdSync, CmdTerminate>;
330
331 std::thread _thread;
332 Atomic<int> _level;
333 Atomic<bool> _consoleLogging;
334 Queue<Command> _queue;
335 mutable Mutex _formatterMutex;
336 LogFormatter _fileFormatter;
337 LogFormatter _consoleFormatter;
338 Map<uint64_t, String> _threadNames;
339
340 void worker();
341 void writeLog(const LogEntry &cmd, class FileIODevice *logFile);
342 class FileIODevice *openLogFile(const String &filename, class FileIODevice *existing);
343
344};
345
347
void setValue(T val)
Stores a new value with release semantics.
Definition atomic.h:54
T value() const
Loads the current value with acquire semantics.
Definition atomic.h:46
Wall-clock date and time based on std::chrono::system_clock.
Definition datetime.h:39
Lightweight error code wrapper for the promeki library.
Definition error.h:39
@ Ok
No error.
Definition error.h:51
IODevice wrapping a C stdio FILE pointer.
Definition fileiodevice.h:47
Dynamic array container wrapping std::vector.
Definition list.h:40
Asynchronous thread-safe logging facility.
Definition logger.h:102
void setLogLevel(LogLevel level)
Changes the minimum log level.
Definition logger.h:232
void setConsoleLoggingEnabled(bool val)
Enables or disables console (stderr) log output.
Definition logger.h:242
void log(LogLevel loglevel, const char *file, int line, const StringList &lines)
Enqueues multiple log messages with the same timestamp.
void setLogFile(const String &filename)
Sets the log output file.
Definition logger.h:224
static Logger & defaultLogger()
Returns the singleton default Logger instance.
int level() const
Returns the current minimum log level.
Definition logger.h:190
static LogFormatter defaultFileFormatter()
Returns the default file log formatter.
static char levelToChar(LogLevel level)
Converts a LogLevel to its single character representation.
static void setThreadName(const String &name)
Updates the cached thread name used in log output.
void setFileFormatter(LogFormatter formatter)
Sets a custom formatter for file log output.
Definition logger.h:268
Error sync(unsigned int timeoutMs=0)
Blocks until all queued log commands have been processed.
Definition logger.h:296
void setConsoleFormatter(LogFormatter formatter)
Sets a custom formatter for console log output.
Definition logger.h:281
Logger()
Constructs a Logger and starts the worker thread.
LogLevel
Severity levels for log messages.
Definition logger.h:105
@ Force
Forced messages are always logged.
Definition logger.h:106
@ Err
Error messages.
Definition logger.h:110
@ Debug
Debug-level messages.
Definition logger.h:107
@ Info
Informational messages.
Definition logger.h:108
@ Warn
Warning messages.
Definition logger.h:109
~Logger()
Destructor. Signals the worker thread to terminate and waits for it to finish.
Definition logger.h:181
static LogFormatter defaultConsoleFormatter()
Returns the default console log formatter.
LogFormatter fileFormatter() const
Returns the current file log formatter.
Definition logger.h:250
void log(LogLevel loglevel, const char *file, int line, const String &msg)
Enqueues a single log message.
std::function< String(const LogFormat &fmt)> LogFormatter
Function type for formatting log messages.
Definition logger.h:135
LogFormatter consoleFormatter() const
Returns the current console log formatter.
Definition logger.h:258
RAII scoped locker for Mutex.
Definition mutex.h:41
Mutual exclusion lock wrapping std::mutex.
Definition mutex.h:33
void emplace(Args &&... args)
Constructs an element in-place at the back of the queue.
Definition queue.h:78
Manages a list of strings.
Definition stringlist.h:21
Encoding-aware string class with copy-on-write semantics.
Definition string.h:35
static String sprintf(const char *fmt,...)
Creates a formatted string using printf-style syntax.
#define PROMEKI_NAMESPACE_BEGIN
Starts a promeki namespace block.
Definition namespace.h:14
#define PROMEKI_NAMESPACE_END
Ends a promeki namespace block.
Definition namespace.h:19
A single log entry.
Definition logger.h:114
Context passed to formatters, combining entry data with resolved thread name.
Definition logger.h:124