LogIt++
Loading...
Searching...
No Matches
ConsoleLogger.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef _LOGIT_CONSOLE_LOGGER_HPP_INCLUDED
3#define _LOGIT_CONSOLE_LOGGER_HPP_INCLUDED
4
7
8#include "ILogger.hpp"
9#include <iostream>
10#if defined(_WIN32)
11#include <windows.h>
12#endif
13#ifdef __EMSCRIPTEN__
14#include <emscripten/emscripten.h>
15#endif
16#include <mutex>
17#include <atomic>
18
19namespace logit {
20
32 class ConsoleLogger : public ILogger {
33 public:
34
37 struct Config {
39#ifdef __EMSCRIPTEN__
40 bool async = false;
41#else
42 bool async = true;
43#endif
44 };
45
49 }
50
53 ConsoleLogger(const Config& config) : m_config(config) {
55 }
56
59 ConsoleLogger(const bool async) {
60 m_config.async = async;
62 }
63
64 virtual ~ConsoleLogger() = default;
65
69 void set_config(const Config& config) {
70 std::lock_guard<std::mutex> lock(m_mutex);
71 m_config = config;
72 }
73
78 std::lock_guard<std::mutex> lock(m_mutex);
79 return m_config;
80 }
81
89 void log(const LogRecord& record, const std::string& message) override {
91#ifdef __EMSCRIPTEN__
92 std::lock_guard<std::mutex> lock(m_mutex);
93 handle_ansi_colors_emscripten(message);
94 return;
95#else
96 std::unique_lock<std::mutex> lock(m_mutex);
97 if (!m_config.async) {
98# if defined(_WIN32)
99 // For Windows, parse the message for ANSI color codes and apply them
100 handle_ansi_colors_windows(message);
101# else
102 // For other systems, output the message as is
103 std::cout << message << std::endl;
104# endif
105 return;
106 }
107 lock.unlock();
109 std::lock_guard<std::mutex> lock(m_mutex);
110# if defined(_WIN32)
111 // For Windows, parse the message for ANSI color codes and apply them
112 handle_ansi_colors_windows(message);
113# else
114 // For other systems, output the message as is
115 std::cout << message << std::endl;
116# endif
117 });
118#endif
119 }
120
128 std::string get_string_param(const LoggerParam& param) const override {
129 switch (param) {
130 case LoggerParam::LastLogTimestamp: return std::to_string(get_last_log_ts());
131 case LoggerParam::TimeSinceLastLog: return std::to_string(get_time_since_last_log());
132 default:
133 break;
134 };
135 return std::string();
136 }
137
141 int64_t get_int_param(const LoggerParam& param) const override {
142 switch (param) {
145 default:
146 break;
147 };
148 return 0;
149 }
150
154 double get_float_param(const LoggerParam& param) const override {
155 switch (param) {
156 case LoggerParam::LastLogTimestamp: return (double)get_last_log_ts() / 1000.0;
157 case LoggerParam::TimeSinceLastLog: return (double)get_time_since_last_log() / 1000.0;
158 default:
159 break;
160 };
161 return 0.0;
162 }
163
165 void set_log_level(LogLevel level) override {
166 m_log_level = static_cast<int>(level);
167 }
168
170 LogLevel get_log_level() const override {
171 return static_cast<LogLevel>(m_log_level.load());
172 }
173
176 void wait() override {
177#ifdef __EMSCRIPTEN__
178 // Nothing to wait for in single-threaded mode
179 return;
180#else
181 std::unique_lock<std::mutex> lock(m_mutex);
182 if (!m_config.async) return;
183 lock.unlock();
185#endif
186 }
187
188 private:
189 mutable std::mutex m_mutex;
191 std::atomic<int64_t> m_last_log_ts = ATOMIC_VAR_INIT(0);
192 std::atomic<int> m_log_level = ATOMIC_VAR_INIT(static_cast<int>(LogLevel::LOG_LVL_TRACE));
193
194# if defined(_WIN32)
195
196 // Windows console colors
197 enum class WinColor {
198 Black = 0,
199 DarkBlue = 1,
200 DarkGreen = 2,
201 DarkCyan = 3,
202 DarkRed = 4,
203 DarkMagenta = 5,
204 DarkYellow = 6,
205 Gray = 7,
206 DarkGray = 8,
207 Blue = 9,
208 Green = 10,
209 Cyan = 11,
210 Red = 12,
211 Magenta = 13,
212 Yellow = 14,
213 White = 15,
214 };
215
218 void handle_ansi_colors_windows(const std::string& message) const {
219 std::string::size_type start = 0;
220 std::string::size_type pos = 0;
221
222 HANDLE handle_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
223
224 while ((pos = message.find("\033[", start)) != std::string::npos) {
225 // Output the part of the string before the ANSI code
226 if (pos > start) {
227 std::cout << message.substr(start, pos - start);
228 }
229
230 // Find the end of the ANSI code
231 std::string::size_type end_pos = message.find('m', pos);
232 if (end_pos != std::string::npos) {
233 // Extract the ANSI code
234 std::string ansi_code = message.substr(pos + 2, end_pos - pos - 2);
235 apply_color_from_ansi_code(ansi_code, handle_stdout);
236
237 // Update position
238 start = end_pos + 1;
239 } else {
240 break;
241 }
242 }
243
244 // Output any remaining part of the message
245 if (start < message.size()) {
246 std::cout << message.substr(start);
247 }
248 if (!message.empty()) std::cout << std::endl;
249
250 // Reset the console color to default
251 SetConsoleTextAttribute(handle_stdout, static_cast<WORD>(text_color_to_win_color(m_config.default_color)));
252 }
253
257 void apply_color_from_ansi_code(const std::string& ansi_code, HANDLE handle_stdout) const {
258 WORD color_value = static_cast<WORD>(text_color_to_win_color(m_config.default_color)); // Default color
259 const int code = std::stoi(ansi_code);
260 switch (code) {
261 case 30: color_value = static_cast<WORD>(WinColor::Black); break;
262 case 31: color_value = static_cast<WORD>(WinColor::DarkRed); break;
263 case 32: color_value = static_cast<WORD>(WinColor::DarkGreen); break;
264 case 33: color_value = static_cast<WORD>(WinColor::DarkYellow); break;
265 case 34: color_value = static_cast<WORD>(WinColor::DarkBlue); break;
266 case 35: color_value = static_cast<WORD>(WinColor::DarkMagenta); break;
267 case 36: color_value = static_cast<WORD>(WinColor::DarkCyan); break;
268 case 37: color_value = static_cast<WORD>(WinColor::Gray); break;
269 case 90: color_value = static_cast<WORD>(WinColor::DarkGray); break;
270 case 91: color_value = static_cast<WORD>(WinColor::Red); break;
271 case 92: color_value = static_cast<WORD>(WinColor::Green); break;
272 case 93: color_value = static_cast<WORD>(WinColor::Yellow); break;
273 case 94: color_value = static_cast<WORD>(WinColor::Blue); break;
274 case 95: color_value = static_cast<WORD>(WinColor::Magenta); break;
275 case 96: color_value = static_cast<WORD>(WinColor::Cyan); break;
276 case 97: color_value = static_cast<WORD>(WinColor::White); break;
277 default:
278 // Unknown code, use default color
279 break;
280 };
281 // Set the console text attribute to the desired color
282 SetConsoleTextAttribute(handle_stdout, color_value);
283 }
284
288 WinColor text_color_to_win_color(const TextColor& color) const {
289 switch (color) {
290 case TextColor::Black: return WinColor::Black;
291 case TextColor::DarkRed: return WinColor::DarkRed;
292 case TextColor::DarkGreen: return WinColor::DarkGreen;
293 case TextColor::DarkYellow: return WinColor::DarkYellow;
294 case TextColor::DarkBlue: return WinColor::DarkBlue;
295 case TextColor::DarkMagenta: return WinColor::DarkMagenta;
296 case TextColor::DarkCyan: return WinColor::DarkCyan;
297 case TextColor::LightGray: return WinColor::Gray;
298 case TextColor::DarkGray: return WinColor::DarkGray;
299 case TextColor::Red: return WinColor::Red;
300 case TextColor::Green: return WinColor::Green;
301 case TextColor::Yellow: return WinColor::Yellow;
302 case TextColor::Blue: return WinColor::Blue;
303 case TextColor::Magenta: return WinColor::Magenta;
304 case TextColor::Cyan: return WinColor::Cyan;
305 case TextColor::White: return WinColor::White;
306 default: return WinColor::White;
307 }
308 }
309# endif
310
311# ifdef __EMSCRIPTEN__
313 const char* text_color_to_css(TextColor color) const {
314 switch (color) {
315 case TextColor::Black: return "black";
316 case TextColor::DarkRed: return "darkred";
317 case TextColor::DarkGreen: return "darkgreen";
318 case TextColor::DarkYellow: return "olive";
319 case TextColor::DarkBlue: return "darkblue";
320 case TextColor::DarkMagenta: return "purple";
321 case TextColor::DarkCyan: return "teal";
322 case TextColor::LightGray: return "lightgray";
323 case TextColor::DarkGray: return "gray";
324 case TextColor::Red: return "red";
325 case TextColor::Green: return "green";
326 case TextColor::Yellow: return "yellow";
327 case TextColor::Blue: return "blue";
328 case TextColor::Magenta: return "magenta";
329 case TextColor::Cyan: return "cyan";
330 case TextColor::White: return "white";
331 default: return "inherit";
332 }
333 }
334
336 std::string css_color_from_ansi(const std::string& code) const {
337 int value = std::stoi(code);
338 switch (value) {
339 case 30: return "black";
340 case 31: return "darkred";
341 case 32: return "darkgreen";
342 case 33: return "olive";
343 case 34: return "darkblue";
344 case 35: return "purple";
345 case 36: return "teal";
346 case 37: return "lightgray";
347 case 90: return "gray";
348 case 91: return "red";
349 case 92: return "green";
350 case 93: return "yellow";
351 case 94: return "blue";
352 case 95: return "magenta";
353 case 96: return "cyan";
354 case 97: return "white";
355 default: return text_color_to_css(m_config.default_color);
356 }
357 }
358
360 void handle_ansi_colors_emscripten(const std::string& message) const {
361 std::string current_color = text_color_to_css(m_config.default_color);
362 std::string::size_type start = 0;
363 std::string::size_type pos = 0;
364
365 while ((pos = message.find("\033[", start)) != std::string::npos) {
366 if (pos > start) {
367 std::string part = message.substr(start, pos - start);
368 EM_ASM_({ console.log('%c' + UTF8ToString($0), 'color: ' + UTF8ToString($1)); }, part.c_str(), current_color.c_str());
369 }
370 std::string::size_type end_pos = message.find('m', pos);
371 if (end_pos != std::string::npos) {
372 std::string ansi_code = message.substr(pos + 2, end_pos - pos - 2);
373 current_color = css_color_from_ansi(ansi_code);
374 start = end_pos + 1;
375 } else {
376 break;
377 }
378 }
379
380 if (start < message.size()) {
381 std::string part = message.substr(start);
382 EM_ASM_({ console.log('%c' + UTF8ToString($0), 'color: ' + UTF8ToString($1)); }, part.c_str(), current_color.c_str());
383 }
384 }
385# endif // __EMSCRIPTEN__
386
388 void reset_color() {
389# ifdef __EMSCRIPTEN__
390 // No persistent console color in browsers
391 return;
392# elif defined(_WIN32)
393 handle_ansi_colors_windows(std::string());
394# else
395 std::cout << to_string(m_config.default_color);
396# endif
397 }
398
401 int64_t get_last_log_ts() const {
402 return m_last_log_ts;
403 }
404
407 int64_t get_time_since_last_log() const {
409 }
410 }; // ConsoleLogger
411
412}; // namespace logit
413
414#endif // _LOGIT_CONSOLE_LOGGER_HPP_INCLUDED
Defines the interface for loggers used in the logging system.
void log(const LogRecord &record, const std::string &message) override
Logs a message to the console with thread safety.
ConsoleLogger()
Default constructor that uses default configuration.
double get_float_param(const LoggerParam &param) const override
Retrieves a floating-point parameter from the logger.
LogLevel get_log_level() const override
Gets the minimal log level for this logger.
std::mutex m_mutex
Mutex to protect console output.
int64_t get_last_log_ts() const
Retrieves the timestamp of the last log.
virtual ~ConsoleLogger()=default
int64_t get_time_since_last_log() const
Retrieves the time since the last log.
std::string get_string_param(const LoggerParam &param) const override
Retrieves a string parameter from the logger.
void set_config(const Config &config)
Sets the logger configuration.
int64_t get_int_param(const LoggerParam &param) const override
Retrieves an integer parameter from the logger.
ConsoleLogger(const bool async)
Constructor with asynchronous flag.
std::atomic< int > m_log_level
void set_log_level(LogLevel level) override
Sets the minimal log level for this logger.
Config get_config()
Gets the current logger configuration.
std::atomic< int64_t > m_last_log_ts
ConsoleLogger(const Config &config)
Constructor with custom configuration.
Config m_config
Configuration for the console logger.
void wait() override
Waits for all asynchronous tasks to complete.
void reset_color()
Resets the console text color to the default.
Interface for loggers that handle log message output.
Definition ILogger.hpp:15
void add_task(std::function< void()> task)
Adds a task to the queue in a thread-safe manner.
void wait()
Waits for all tasks in the queue to be processed.
static TaskExecutor & get_instance()
Get the singleton instance of the TaskExecutor.
#define LOGIT_CURRENT_TIMESTAMP_MS()
Macro to get the current timestamp in milliseconds.
Definition config.hpp:63
#define LOGIT_DEFAULT_COLOR
Defines the default color for console output.
Definition config.hpp:22
The primary namespace for the LogIt++ library.
std::string to_string(LogLevel level, int mode=0)
Convert LogLevel to a std::string representation.
Definition enums.hpp:90
LogLevel
Logging levels.
Definition enums.hpp:15
@ LOG_LVL_TRACE
Trace level logging.
Definition enums.hpp:16
LoggerParam
Enumeration for different logger parameters that can be retrieved.
Definition enums.hpp:47
@ TimeSinceLastLog
The time elapsed since the last log in seconds.
Definition enums.hpp:51
@ LastLogTimestamp
The timestamp of the last log.
Definition enums.hpp:50
TextColor
Text colors for console output.
Definition enums.hpp:26
Configuration for the console logger.
bool async
Flag indicating whether logging should be asynchronous.
TextColor default_color
Default text color for console output.
Stores log metadata and content.
Definition LogRecord.hpp:17
const int64_t timestamp_ms
Timestamp in milliseconds.
Definition LogRecord.hpp:19