1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
#pragma once
#include "Core/Memory.h"
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <fstream>
#include <chrono>
#include <iomanip>
#include <mutex>
#include <string_view>
namespace Donut
{
enum class LogLevel
{
TRACE = 0,
INFO = 2,
WARN = 3,
ERR = 4,
FATAL = 5
};
class Logger
{
public:
static void Init();
static void Shutdown();
static Ref<Logger> GetLogger();
template<typename... Args>
static void Trace(const std::string_view& format, const Args&... args)
{
auto logger = GetLogger();
if (logger) logger->LogMessage(LogLevel::TRACE, format, args...);
}
template<typename... Args>
static void Info(const std::string_view& format, const Args&... args)
{
auto logger = GetLogger();
if (logger) logger->LogMessage(LogLevel::INFO, format, args...);
}
template<typename... Args>
static void Warn(const std::string_view& format, const Args&... args)
{
auto logger = GetLogger();
if (logger) logger->LogMessage(LogLevel::WARN, format, args...);
}
template<typename... Args>
static void Error(const std::string_view& format, const Args&... args)
{
auto logger = GetLogger();
if (logger) logger->LogMessage(LogLevel::ERR, format, args...);
}
template<typename... Args>
static void Fatal(const std::string_view& format, const Args&... args)
{
auto logger = GetLogger();
if (logger) logger->LogMessage(LogLevel::FATAL, format, args...);
}
template<typename... Args>
void LogMessage(LogLevel level, const std::string_view& format, const Args&... args)
{
if (level < m_LogLevel)
return;
std::lock_guard<std::mutex> lock(m_Mutex);
std::string message = FormatString(format, args...);
std::string fullMessage = GetTimeStamp() + " [" + GetLogLevelString(level) + "] " + message;
if (m_ConsoleOutput)
{
SetConsoleColor(level);
std::cout << fullMessage << std::endl;
ResetConsoleColor();
}
if (m_FileOutput && m_LogFile.is_open())
{
m_LogFile << fullMessage << std::endl;
m_LogFile.flush();
}
}
template<typename... Args>
std::string FormatString(const std::string_view& format, const Args&... args)
{
std::string result = format.data();
std::vector<std::string> argStrings = { ToString(args)... };
size_t argIndex = 0;
size_t pos = 0;
while ((pos = result.find("{}", pos)) != std::string::npos && argIndex < argStrings.size())
{
result.replace(pos, 2, argStrings[argIndex]);
pos += argStrings[argIndex].length();
argIndex++;
}
return result;
}
template<typename T>
std::string ToString(const T& value)
{
std::stringstream ss;
ss << value;
return ss.str();
}
void SetLogLevel(LogLevel level) { m_LogLevel = level; }
void EnableConsoleOutput(bool enable) { m_ConsoleOutput = enable; }
void EnableFileOutput(bool enable) { m_FileOutput = enable; }
void SetLogFile(const std::string& filename);
public:
Logger();
~Logger();
std::string GetTimeStamp();
std::string GetLogLevelString(LogLevel level);
void SetConsoleColor(LogLevel level);
void ResetConsoleColor();
private:
LogLevel m_LogLevel;
bool m_ConsoleOutput;
bool m_FileOutput;
std::ofstream m_LogFile;
std::mutex m_Mutex;
static Ref<Logger> s_Logger;
};
}
#if defined(DONUT_DEBUG)
#define DONUT_TRACE(format, ...) ::Donut::Logger::Trace(format, ##__VA_ARGS__)
#define DONUT_INFO(format, ...) ::Donut::Logger::Info(format, ##__VA_ARGS__)
#define DONUT_WARN(format, ...) ::Donut::Logger::Warn(format, ##__VA_ARGS__)
#define DONUT_ERROR(format, ...) ::Donut::Logger::Error(format, ##__VA_ARGS__)
#define DONUT_FATAL(format, ...) ::Donut::Logger::Fatal(format, ##__VA_ARGS__)
#else
#define DONUT_TRACE(format, ...) {}
#define DONUT_INFO(format, ...) {}
#define DONUT_WARN(format, ...) {}
#define DONUT_ERROR(format, ...) {}
#define DONUT_FATAL(format, ...) {}
#endif
|