LkEngine 0.1.2
 
Loading...
Searching...
No Matches
Log.h
1/******************************************************************
2 * LLog
3 *
4 * Logger class and logging implementation for the entire engine.
5 ******************************************************************/
6#pragma once
7
16#include "LogMacros.h"
17#include "LogFormatters.h"
18
19#include <stdint.h>
20#include <cstring>
21#include <codecvt>
22#include <filesystem>
23#if defined(LK_PLATFORM_WINDOWS)
24# include <format>
25#elif defined(LK_PLATFORM_LINUX)
26# include <spdlog/fmt/fmt.h>
27#endif
28#include <locale>
29#include <map>
30
31#if LK_LOG_USE_IOSTREAM
32# include <iostream>
33# include <iterator>
34#endif
35
36#if defined(LK_PLATFORM_WINDOWS)
37# define LK_ASSERT_MESSAGE_BOX 1
38# define LK_ENGINE_CONSOLE_ENABLED 1
39#else
40# define LK_ASSERT_MESSAGE_BOX 0
41# define LK_ENGINE_CONSOLE_ENABLED 1
42#endif
43
48#define SPDLOG_LEVEL_NAMES \
49 { "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "OFF" }
50
51#include <spdlog/spdlog.h>
52#include <spdlog/fmt/ostr.h>
53#include <spdlog/fmt/fmt.h>
54
55#include <glm/glm.hpp>
56#include <glm/gtx/string_cast.hpp>
57
58#include "LkEngine/Core/Assert.h"
60
61#include "LkEngine/Renderer/Color.h"
62
63namespace LkEngine {
64
74 enum class ELogLevel
75 {
76 Trace,
77 Debug,
78 Info,
79 Warning,
80 Error,
81 Fatal
82 };
83
88 enum class ELoggerType
89 {
90 Core = 0,
91 Client,
92 EditorConsole,
93 TestRunner,
94 };
95
99 using LogLevelColorConfig = std::pair<ELogLevel, uint16_t>;
100
106 class LLog
107 {
108 public:
110 {
111 bool Enabled = true;
112 ELogLevel Filter = ELogLevel::Debug;
113
114 FTagDetails() = default;
115 FTagDetails(const ELogLevel InFilter) : Filter(InFilter) {}
116 };
117
118 LLog();
119 ~LLog();
120
121 static LLog& Get();
122
123 static void Initialize(std::string_view LogfileName = "");
124
125 static void RegisterLogger(const ELoggerType Type,
126 const std::string& Name,
127 const ELogLevel LogLevel = ELogLevel::Info,
128 const std::vector<LogLevelColorConfig>& LevelConfigs = {},
129 const Color::EColorCode MainColor = Color::EColorCode::Reset);
130
134 FORCEINLINE static std::shared_ptr<spdlog::logger>& GetLogger(const ELoggerType LoggerType)
135 {
136 switch (LoggerType)
137 {
138 case ELoggerType::Core: return Logger_Core;
139 case ELoggerType::Client: return Logger_Client;
140 case ELoggerType::EditorConsole: return Logger_EditorConsole;
141 case ELoggerType::TestRunner: return Logger_TestRunner;
142 }
143
144 return Logger_Core;
145 }
146
151 template<typename... TArgs>
152 #if defined(LK_ENGINE_MSVC)
153 static void PrintMessage(const ELoggerType LoggerType, const ELogLevel Level,
154 std::format_string<TArgs...> Format, TArgs&&... Args);
155 #elif defined(LK_ENGINE_GCC) || defined(LK_ENGINE_CLANG)
156 static void PrintMessage(const ELoggerType LoggerType, const ELogLevel Level,
157 fmt::format_string<TArgs...> Format, TArgs&&... Args);
158 #endif
159
164 template<typename... TArgs>
165 #if defined(LK_ENGINE_MSVC)
166 static void PrintMessageWithTag(const ELoggerType LoggerType, const ELogLevel Level, std::string_view Tag,
167 std::format_string<TArgs...> Format, TArgs&&... Args);
168 #elif defined(LK_ENGINE_GCC) || defined(LK_ENGINE_CLANG)
169 static void PrintMessageWithTag(const ELoggerType LoggerType, const ELogLevel Level, std::string_view Tag,
170 fmt::format_string<TArgs...> Format, TArgs&&... Args);
171 #endif
172
173 static void PrintMessageWithTag(const ELoggerType LoggerType, const ELogLevel Level,
174 std::string_view Tag, std::string_view Message);
175
176 template<typename... TArgs>
177 #if defined(LK_ENGINE_MSVC)
178 static void PrintAssertMessage(const ELoggerType LoggerType, std::string_view Prefix,
179 std::format_string<TArgs...> Message, TArgs&&... Args);
180 #elif defined(LK_ENGINE_GCC) || defined(LK_ENGINE_CLANG)
181 static void PrintAssertMessage(const ELoggerType LoggerType, std::string_view Prefix,
182 fmt::format_string<TArgs...> Message, TArgs&&... Args);
183 #endif
184 static void PrintAssertMessage(const ELoggerType LoggerType, std::string_view Prefix);
185
186 template<typename... TArgs>
187 #if defined(LK_ENGINE_MSVC)
188 static void Print(std::format_string<TArgs...> Format, TArgs&&... Args)
189 #elif defined(LK_ENGINE_GCC) || defined(LK_ENGINE_CLANG)
190 static void Print(fmt::format_string<TArgs...> Format, TArgs&&... Args)
191 #endif
192 {
193 #if LK_LOG_USE_IOSTREAM
194 std::ostream_iterator<char> Out(std::cout);
195 std::format_to(Out, Format, std::forward<TArgs>(Args)...);
196 #else
197 #if defined(LK_ENGINE_MSVC)
198 const std::string FormattedString = std::format(Format, std::forward<TArgs>(Args)...);
199 #elif defined(LK_ENGINE_GCC) || defined(LK_ENGINE_CLANG)
200 const std::string FormattedString = fmt::format(Format, std::forward<TArgs>(Args)...);
201 #endif
202
203 printf("%s", FormattedString.c_str());
204 fflush(stdout);
205 #endif
206 }
207
208 template<typename... TArgs>
209 #if defined(LK_ENGINE_MSVC)
210 static void PrintLn(std::format_string<TArgs...> Format, TArgs&&... Args)
211 #elif defined(LK_ENGINE_GCC) || defined(LK_ENGINE_CLANG)
212 static void PrintLn(fmt::format_string<TArgs...> Format, TArgs&&... Args)
213 #endif
214 {
215 #if LK_LOG_USE_IOSTREAM
216 std::ostream_iterator<char> Out(std::cout);
217 std::format_to(Out, Format, std::forward<TArgs>(Args)...);
218 *Out = '\n';
219 #else
220 #if defined(LK_ENGINE_MSVC)
221 const std::string FormattedString = std::format(Format, std::forward<TArgs>(Args)...);
222 #elif defined(LK_ENGINE_GCC) || defined(LK_ENGINE_CLANG)
223 const std::string FormattedString = fmt::format(Format, std::forward<TArgs>(Args)...);
224 #endif
225 printf("%s\n", FormattedString.c_str());
226 fflush(stdout);
227 #endif
228 }
229
230 FORCEINLINE static const char* LevelToString(const ELogLevel Level)
231 {
232 switch (Level)
233 {
234 case ELogLevel::Trace: return "Trace";
235 case ELogLevel::Debug: return "Debug";
236 case ELogLevel::Info: return "Info";
237 case ELogLevel::Warning: return "Warning";
238 case ELogLevel::Error: return "Error";
239 case ELogLevel::Fatal: return "Fatal";
240 }
241
242 LK_CORE_ASSERT(false, "Unknown log level: {}", static_cast<int>(Level));
243 return "";
244 }
245
246 FORCEINLINE static ELogLevel LevelFromString(std::string_view InString)
247 {
248 const std::string StrLower = StringUtils::ToLower(InString);
249 if (StrLower == "trace") return ELogLevel::Trace;
250 else if (StrLower == "debug") return ELogLevel::Debug;
251 else if (StrLower == "info") return ELogLevel::Info;
252 else if (StrLower == "warning") return ELogLevel::Warning;
253 else if (StrLower == "error") return ELogLevel::Error;
254 else if (StrLower == "fatal") return ELogLevel::Fatal;
255
256 LK_CORE_ASSERT(false, "LevelFromString failed for '{}' (lower: '{}')", InString, StrLower);
257 return ELogLevel::Info;
258 }
259
263 FORCEINLINE static spdlog::level::level_enum ToSpdlogLevel(const ELogLevel Level)
264 {
265 switch (Level)
266 {
267 case ELogLevel::Trace: return spdlog::level::trace;
268 case ELogLevel::Debug: return spdlog::level::debug;
269 case ELogLevel::Info: return spdlog::level::info;
270 case ELogLevel::Warning: return spdlog::level::warn;
271 case ELogLevel::Error: return spdlog::level::err;
272 case ELogLevel::Fatal: return spdlog::level::critical;
273 }
274
275 LK_CORE_ASSERT(false, "ToSpdlogLevel error");
276 return spdlog::level::info;
277 }
278
279 template<bool Safe = false>
280 FORCEINLINE static std::string_view GetLoggerName(const ELoggerType LoggerType)
281 {
282 if constexpr (Safe)
283 {
284 switch (LoggerType)
285 {
286 case ELoggerType::Core: return (Logger_Core ? Logger_Core->name() : "");
287 case ELoggerType::Client: return (Logger_Client ? Logger_Client->name() : "");
288 case ELoggerType::EditorConsole: return (Logger_EditorConsole ? Logger_EditorConsole->name() : "");
289 case ELoggerType::TestRunner: return (Logger_TestRunner ? Logger_TestRunner->name() : "");
290 }
291 }
292 else
293 {
294 switch (LoggerType)
295 {
296 case ELoggerType::Core: return Logger_Core->name();
297 case ELoggerType::Client: return Logger_Client->name();
298 case ELoggerType::EditorConsole: return Logger_EditorConsole->name();
299 case ELoggerType::TestRunner: return Logger_TestRunner->name();
300 }
301 LK_CORE_ASSERT(false, "Unknown logger type: {}", static_cast<int>(LoggerType));
302 }
303
304 return "";
305 }
306
307
308 private:
309 inline static std::shared_ptr<spdlog::logger> Logger_Core = nullptr;
310 inline static std::shared_ptr<spdlog::logger> Logger_Client = nullptr;
311 inline static std::shared_ptr<spdlog::logger> Logger_EditorConsole = nullptr;
312 inline static std::shared_ptr<spdlog::logger> Logger_TestRunner = nullptr;
313
315 inline static std::map<std::string, FTagDetails> EnabledTags;
316 };
317}
318
319
320namespace LkEngine {
321
322 template<typename... TArgs>
323 #if defined(LK_ENGINE_MSVC)
324 FORCEINLINE void LLog::PrintMessage(const ELoggerType LoggerType, const ELogLevel Level,
325 std::format_string<TArgs...> Format, TArgs&&... Args)
326 #elif defined(LK_ENGINE_GCC) || defined(LK_ENGINE_CLANG)
327 FORCEINLINE void LLog::PrintMessage(const ELoggerType LoggerType, const ELogLevel Level,
328 fmt::format_string<TArgs...> Format, TArgs&&... Args)
329 #endif
330 {
331 FTagDetails& TagDetails = EnabledTags[GetLoggerName(LoggerType).data()];
332 if (TagDetails.Enabled && TagDetails.Filter <= Level)
333 {
334 auto& Logger = LLog::GetLogger(LoggerType);
335 switch (Level)
336 {
337 case ELogLevel::Trace:
338 Logger->trace(Format, std::forward<TArgs>(Args)...);
339 break;
340 case ELogLevel::Debug:
341 Logger->debug(Format, std::forward<TArgs>(Args)...);
342 break;
343 case ELogLevel::Info:
344 Logger->info(Format, std::forward<TArgs>(Args)...);
345 break;
346 case ELogLevel::Warning:
347 Logger->warn(Format, std::forward<TArgs>(Args)...);
348 break;
349 case ELogLevel::Error:
350 Logger->error(Format, std::forward<TArgs>(Args)...);
351 break;
352 case ELogLevel::Fatal:
353 Logger->critical(Format, std::forward<TArgs>(Args)...);
354 break;
355 }
356 }
357 }
358
359 template<typename... TArgs>
360 #if defined(LK_ENGINE_MSVC)
361 FORCEINLINE void LLog::PrintMessageWithTag(const ELoggerType LoggerType, const ELogLevel Level, std::string_view Tag,
362 std::format_string<TArgs...> Format, TArgs&&... Args)
363 #elif defined(LK_ENGINE_GCC) || defined(LK_ENGINE_CLANG)
364 FORCEINLINE void LLog::PrintMessageWithTag(const ELoggerType LoggerType, const ELogLevel Level, std::string_view Tag,
365 fmt::format_string<TArgs...> Format, TArgs&&... Args)
366 #endif
367 {
368 const FTagDetails& TagDetails = EnabledTags[GetLoggerName(LoggerType).data()];
369 if (TagDetails.Enabled && (TagDetails.Filter <= Level))
370 {
371 #if defined(LK_PLATFORM_WINDOWS)
372 const std::string FormattedString = std::format(Format, std::forward<TArgs>(Args)...);
373 #elif defined(LK_ENGINE_GCC) || defined(LK_ENGINE_CLANG)
374 const std::string FormattedString = fmt::format(Format, std::forward<TArgs>(Args)...);
375 #endif
376 auto& Logger = LLog::GetLogger(LoggerType);
377 switch (Level)
378 {
379 case ELogLevel::Trace:
380 Logger->trace("[{0}] {1}", Tag, FormattedString);
381 break;
382 case ELogLevel::Debug:
383 Logger->debug("[{0}] {1}", Tag, FormattedString);
384 break;
385 case ELogLevel::Info:
386 Logger->info("[{0}] {1}", Tag, FormattedString);
387 break;
388 case ELogLevel::Warning:
389 Logger->warn("[{0}] {1}", Tag, FormattedString);
390 break;
391 case ELogLevel::Error:
392 Logger->error("[{0}] {1}", Tag, FormattedString);
393 break;
394 case ELogLevel::Fatal:
395 Logger->critical("[{0}] {1}", Tag, FormattedString);
396 break;
397 }
398 }
399 }
400
401 FORCEINLINE void LLog::PrintMessageWithTag(const ELoggerType LoggerType, const ELogLevel Level,
402 std::string_view Tag, std::string_view Message)
403 {
404 FTagDetails& TagDetails = EnabledTags[GetLoggerName(LoggerType).data()];
405 if (TagDetails.Enabled && TagDetails.Filter <= Level)
406 {
407 auto& Logger = LLog::GetLogger(LoggerType);
408 switch (Level)
409 {
410 case ELogLevel::Trace:
411 Logger->trace("[{0}] {1}", Tag, Message);
412 break;
413 case ELogLevel::Debug:
414 Logger->debug("[{0}] {1}", Tag, Message);
415 break;
416 case ELogLevel::Info:
417 Logger->info("[{0}] {1}", Tag, Message);
418 break;
419 case ELogLevel::Warning:
420 Logger->warn("[{0}] {1}", Tag, Message);
421 break;
422 case ELogLevel::Error:
423 Logger->error("[{0}] {1}", Tag, Message);
424 break;
425 case ELogLevel::Fatal:
426 Logger->critical("[{0}] {1}", Tag, Message);
427 break;
428 }
429 }
430 }
431
432
433 template<typename... TArgs>
434 #if defined(LK_ENGINE_MSVC)
435 FORCEINLINE void LLog::PrintAssertMessage(const ELoggerType LoggerType, std::string_view Prefix,
436 std::format_string<TArgs...> Format, TArgs&&... Args)
437 #elif defined(LK_ENGINE_GCC) || defined(LK_ENGINE_CLANG)
438 FORCEINLINE void LLog::PrintAssertMessage(const ELoggerType LoggerType, std::string_view Prefix,
439 fmt::format_string<TArgs...> Format, TArgs&&... Args)
440 #endif
441 {
442 #if defined(LK_PLATFORM_WINDOWS)
443 const std::string FormattedString = std::format(Format, std::forward<TArgs>(Args)...);
444 #elif defined(LK_ENGINE_GCC) || defined(LK_ENGINE_CLANG)
445 const std::string FormattedString = fmt::format(Format, std::forward<TArgs>(Args)...);
446 #endif
447 if (auto Logger = GetLogger(LoggerType); Logger != nullptr)
448 {
449 Logger->error("{0}: {1}", Prefix, FormattedString);
450 }
451 else
452 {
453 PrintLn("{2}{0}: {1}{3}", Prefix, FormattedString, LK_ANSI_COLOR_BG_BRIGHT_RED, LK_ANSI_COLOR_RESET);
454 }
455
456 #if LK_ENGINE_CONSOLE_ENABLED
457 LK_CONSOLE_FATAL("{}", FormattedString);
458 #endif
459 #if LK_ASSERT_MESSAGE_BOX
460 MessageBoxA(nullptr, FormattedString.c_str(), "LkEngine Error", (MB_OK | MB_ICONERROR));
461 #endif
462 }
463
464 FORCEINLINE void LLog::PrintAssertMessage(const ELoggerType LoggerType, std::string_view Message)
465 {
466 if (auto Logger = GetLogger(LoggerType); Logger != nullptr)
467 {
468 LLog::GetLogger(LoggerType)->error("{0}", Message);
469 }
470 else
471 {
472 PrintLn("{1}{0}{2}", Message, LK_ANSI_COLOR_BG_BRIGHT_RED, LK_ANSI_COLOR_RESET);
473 }
474
475 #if LK_ENGINE_CONSOLE_ENABLED
476 LK_CONSOLE_FATAL("{}", Message);
477 #endif
478 #if LK_ASSERT_MESSAGE_BOX
479 MessageBoxA(nullptr, Message.data(), "LkEngine Error", (MB_OK | MB_ICONERROR));
480 #endif
481 }
483}
484
487/********************************************************
488 Log Utility Functions.
489********************************************************/
490namespace LkEngine {
491
492 namespace LogUtility
493 {
495 static bool CompareLogFiles(const std::filesystem::directory_entry& A, const std::filesystem::directory_entry& B)
496 {
497 return (A.path().filename().string() < B.path().filename().string());
498 }
499
501 static int CountLogFilesInDir(const std::filesystem::path& InDirectory)
502 {
503 namespace fs = std::filesystem;
504 int Files = 0;
505 if (fs::exists(InDirectory) && fs::is_directory(InDirectory))
506 {
507 for (const fs::directory_entry& Entry : fs::directory_iterator(InDirectory))
508 {
509 if (Entry.is_regular_file() && Entry.path().extension() == ".log")
510 {
511 Files++;
512 }
513 }
514 }
515
516 return Files;
517 }
518
519 static void CleanLogDirectory(const std::filesystem::path& InDirectory, const int MaxLogFiles)
520 {
521 namespace fs = std::filesystem;
522 std::vector<fs::directory_entry> LogFiles;
523 LogFiles.reserve(MaxLogFiles);
524
525 /* Check if the directory exists. */
526 if (fs::exists(InDirectory) && fs::is_directory(InDirectory))
527 {
528 for (const auto& Entry : fs::directory_iterator(InDirectory))
529 {
530 if (Entry.is_regular_file() && Entry.path().extension() == ".log")
531 {
532 LogFiles.push_back(Entry);
533 }
534 }
535 }
536
537 /* If we have more than MaxLogFiles, sort and remove the oldest. */
538 if (LogFiles.size() > MaxLogFiles)
539 {
540 /* Sort log files based on their names (timestamps in filenames). */
541 std::sort(LogFiles.begin(), LogFiles.end(), CompareLogFiles);
542
543 /* Remove the oldest files, keeping only the most recent ones. */
544 for (std::size_t Index = 0; Index < LogFiles.size() - MaxLogFiles; Index++)
545 {
546 const fs::directory_entry& LogFile = LogFiles[Index];
547 if (LogFile.path().extension() == ".log")
548 {
549 fs::remove(LogFile.path());
550 }
551 }
552 }
553 }
554 }
555}
556
557
Core macros used by the entire engine.
Log formatters.
String utilities.
Definition Log.h:107
static void Initialize(std::string_view LogfileName="")
Definition Log.cpp:60
static void PrintMessageWithTag(const ELoggerType LoggerType, const ELogLevel Level, std::string_view Tag, std::string_view Message)
Print a formatted message.
Definition Log.h:401
static FORCEINLINE std::shared_ptr< spdlog::logger > & GetLogger(const ELoggerType LoggerType)
Get a logger instance with the help of a logger type.
Definition Log.h:134
static FORCEINLINE spdlog::level::level_enum ToSpdlogLevel(const ELogLevel Level)
Definition Log.h:263
ELoggerType
Definition Log.h:89
ELogLevel
Definition Log.h:75
Definition Asset.h:11
Definition Log.h:110