-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlogger.h
More file actions
136 lines (116 loc) · 4.5 KB
/
logger.h
File metadata and controls
136 lines (116 loc) · 4.5 KB
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
#pragma once
#include <chrono>
#include <cstdint>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <mutex>
#include <sstream>
#include <type_traits>
namespace cpplib {
namespace detail {
template <typename Enum>
constexpr auto to_underlying(Enum value) noexcept -> std::underlying_type_t<Enum> {
return static_cast<std::underlying_type_t<Enum>>(value);
}
}
enum class LogLevel : std::uint8_t {
TRACE = 0,
DEBUG,
INFO,
WARN,
ERROR,
CRITICAL
};
constexpr bool operator<(LogLevel lhs, LogLevel rhs) noexcept {
return detail::to_underlying(lhs) < detail::to_underlying(rhs);
}
enum class OutputTarget : std::uint8_t {
NONE = 0,
TERMINAL = 1 << 0,
FILE = 1 << 1,
GUI = 1 << 2
};
inline OutputTarget operator|(OutputTarget lhs, OutputTarget rhs) noexcept {
return static_cast<OutputTarget>(detail::to_underlying(lhs) | detail::to_underlying(rhs));
}
inline OutputTarget operator&(OutputTarget lhs, OutputTarget rhs) noexcept {
return static_cast<OutputTarget>(detail::to_underlying(lhs) & detail::to_underlying(rhs));
}
class Logger {
public:
Logger(LogLevel level = LogLevel::INFO, OutputTarget targets = OutputTarget::TERMINAL,
const std::string& file = "")
: log_level(level), log_targets(targets) {
if (!file.empty()) {
log_file = std::make_unique<std::ofstream>(file, std::ios::app);
}
}
~Logger() {
if (log_file) {
log_file->close();
}
}
void log(LogLevel level, const std::string& message) {
if (level < log_level) {
return;
}
std::string log_message = "[" + getTimestamp() + "] [" + levelToString(level) + "] " + message;
std::lock_guard<std::mutex> lock(log_mutex);
if (hasTarget(log_targets, OutputTarget::TERMINAL)) {
std::cout << log_message << std::endl;
}
if (log_file && hasTarget(log_targets, OutputTarget::FILE)) {
*log_file << log_message << std::endl;
}
if (hasTarget(log_targets, OutputTarget::GUI)) {
// Implement GUI logging later
}
}
void trace(const std::string& message) { log(LogLevel::TRACE, message); }
void debug(const std::string& message) { log(LogLevel::DEBUG, message); }
void info(const std::string& message) { log(LogLevel::INFO, message); }
void warn(const std::string& message) { log(LogLevel::WARN, message); }
void error(const std::string& message) { log(LogLevel::ERROR, message); }
void critical(const std::string& message) { log(LogLevel::CRITICAL, message); }
void setLogLevel(LogLevel level) { log_level = level; }
private:
LogLevel log_level;
OutputTarget log_targets;
std::unique_ptr<std::ofstream> log_file;
std::mutex log_mutex;
std::string getTimestamp(bool includeSubsecond = false) {
auto now = std::chrono::system_clock::now();
auto time_t_now = std::chrono::system_clock::to_time_t(now);
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
std::tm tm_snapshot{};
#if defined(_WIN32)
localtime_s(&tm_snapshot, &time_t_now);
#else
localtime_r(&time_t_now, &tm_snapshot);
#endif
std::ostringstream oss;
oss << std::put_time(&tm_snapshot, "%Y-%m-%d %H:%M:%S");
if (includeSubsecond) {
oss << '.' << std::setfill('0') << std::setw(3) << milliseconds.count();
}
return oss.str();
}
std::string levelToString(LogLevel level) {
switch (level) {
case LogLevel::TRACE: return "TRACE";
case LogLevel::DEBUG: return "DEBUG";
case LogLevel::INFO: return "INFO";
case LogLevel::WARN: return "WARN";
case LogLevel::ERROR: return "ERROR";
case LogLevel::CRITICAL: return "CRITICAL";
default: return "UNKNOWN";
}
}
inline bool hasTarget(OutputTarget targets, OutputTarget target) {
return detail::to_underlying(targets & target) != 0;
}
};
}