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
|
#pragma once
#include <string>
#include <memory>
#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 std::shared_ptr<Logger> get_logger();
template<typename... Args>
static void trace(const std::string_view& format, const Args&... args)
{
auto logger = get_logger();
if (logger) logger->log_message(LogLevel::trace, format, args...);
}
template<typename... Args>
static void info(const std::string_view& format, const Args&... args)
{
auto logger = get_logger();
if (logger) logger->log_message(LogLevel::Info, format, args...);
}
template<typename... Args>
static void warn(const std::string_view& format, const Args&... args)
{
auto logger = get_logger();
if (logger) logger->log_message(LogLevel::Warn, format, args...);
}
template<typename... Args>
static void error(const std::string_view& format, const Args&... args)
{
auto logger = get_logger();
if (logger) logger->log_message(LogLevel::Err, format, args...);
}
template<typename... Args>
static void fatal(const std::string_view& format, const Args&... args)
{
auto logger = get_logger();
if (logger) logger->log_message(LogLevel::Fatal, format, args...);
}
template<typename... Args>
void log_message(LogLevel level, const std::string_view& format, const Args&... args)
{
if (level < m_log_level)
return;
std::lock_guard<std::mutex> lock(m_mutex);
std::string message = format_string(format, args...);
std::string full_message = get_time_stamp() + " [" + get_log_level_string(level) + "] " + message;
if (m_console_output)
{
set_console_color(level);
std::cout << full_message << std::endl;
reset_console_color();
}
if (m_file_output && m_log_file.is_open())
{
m_log_file << full_message << std::endl;
m_log_file.flush();
}
}
template<typename... Args>
std::string format_string(const std::string_view& format, const Args&... args)
{
std::string result = format.data();
std::vector<std::string> arg_strings = { to_string(args)... };
size_t arg_index = 0;
size_t pos = 0;
while ((pos = result.find("{}", pos)) != std::string::npos && arg_index < arg_strings.size())
{
result.replace(pos, 2, arg_strings[arg_index]);
pos += arg_strings[arg_index].length();
arg_index++;
}
return result;
}
template<typename T>
std::string to_string(const T& value)
{
std::stringstream ss;
ss << value;
return ss.str();
}
void set_log_level(LogLevel level) { m_log_level = level; }
void enable_console_output(bool enable) { m_console_output = enable; }
void enable_file_output(bool enable) { m_file_output = enable; }
void set_log_file(const std::string& filename);
public:
Logger();
~Logger();
std::string get_time_stamp();
std::string get_log_level_string(LogLevel level);
void set_console_color(LogLevel level);
void reset_console_color();
private:
LogLevel m_log_level;
bool m_console_output;
bool m_file_output;
std::ofstream m_log_file;
std::mutex m_mutex;
static std::shared_ptr<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
|