46 std::lock_guard<std::mutex> lock(mutex_);
52 std::lock_guard<std::mutex> lock(mutex_);
53 colors_enabled_ = enabled;
58 std::lock_guard<std::mutex> lock(mutex_);
59 timestamps_enabled_ = enabled;
64 std::lock_guard<std::mutex> lock(mutex_);
65 console_logging_enabled_ = enabled;
69 std::lock_guard<std::mutex> lock(mutex_);
70 return console_logging_enabled_;
75 std::lock_guard<std::mutex> lock(mutex_);
76 file_logging_enabled_ = enabled;
77 if (enabled && !log_file_path_.empty()) {
79 }
else if (!enabled) {
85 std::lock_guard<std::mutex> lock(mutex_);
86 log_file_path_ = path;
87 if (file_logging_enabled_) {
94 std::lock_guard<std::mutex> lock(mutex_);
95 max_log_file_size_ = max_size_bytes;
99 std::lock_guard<std::mutex> lock(mutex_);
100 max_log_files_ = count;
105 std::lock_guard<std::mutex> lock(mutex_);
106 rotate_on_startup_ = enabled;
110 std::lock_guard<std::mutex> lock(mutex_);
111 return rotate_on_startup_;
116 std::lock_guard<std::mutex> lock(mutex_);
117 return file_logging_enabled_;
121 std::lock_guard<std::mutex> lock(mutex_);
122 return log_file_path_;
126 void log(
LogLevel level,
const std::string& module,
const std::string& message) {
127 std::lock_guard<std::mutex> lock(mutex_);
129 if (level < min_level_) {
134 std::ostringstream console_oss;
137 if (timestamps_enabled_) {
138 auto now = std::chrono::system_clock::now();
139 auto time_t = std::chrono::system_clock::to_time_t(now);
140 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
141 now.time_since_epoch()) % 1000;
145 localtime_s(&local_tm, &time_t);
147 local_tm = *std::localtime(&time_t);
149 console_oss <<
"[" << std::put_time(&local_tm,
"%H:%M:%S");
150 console_oss <<
"." << std::setfill(
'0') << std::setw(3) << ms.count() <<
"] ";
154 if (colors_enabled_ && is_terminal_) {
155 console_oss << get_color_code(level) <<
"[" << get_level_string(level) <<
"]" << get_reset_code();
157 console_oss <<
"[" << get_level_string(level) <<
"]";
161 if (!module.empty()) {
162 if (colors_enabled_ && is_terminal_) {
163 console_oss <<
" " << get_module_color(module) <<
"[" <<
module << "]" << get_reset_code();
165 console_oss <<
" [" <<
module << "]";
170 console_oss <<
" " << message << std::endl;
173 if (console_logging_enabled_) {
175 std::cerr << console_oss.str();
178 std::cout << console_oss.str();
184 if (file_logging_enabled_ && log_file_.is_open()) {
185 write_to_file(level, module, message);
190 Logger() : min_level_(
LogLevel::
INFO), colors_enabled_(true), timestamps_enabled_(true),
191 console_logging_enabled_(true), file_logging_enabled_(false),
192 max_log_file_size_(10 * 1024 * 1024), max_log_files_(5), current_file_size_(0),
193 rotate_on_startup_(false), startup_rotation_done_(false) {
195 is_terminal_ = isatty(fileno(stdout));
200 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
202 GetConsoleMode(hOut, &dwMode);
203 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
204 SetConsoleMode(hOut, dwMode);
213 std::string get_level_string(
LogLevel level) {
219 default:
return "UNKNOWN";
223 std::string get_color_code(
LogLevel level) {
224 if (!colors_enabled_ || !is_terminal_)
return "";
235 std::string get_module_color(
const std::string& module) {
236 if (!colors_enabled_ || !is_terminal_)
return "";
239 uint32_t hash = hash_string(module);
242 const char* colors[] = {
269 size_t color_count =
sizeof(colors) /
sizeof(colors[0]);
270 return colors[hash % color_count];
274 uint32_t hash_string(
const std::string& str) {
275 uint32_t hash = 5381;
277 hash = ((hash << 5) + hash) + c;
282 std::string get_reset_code() {
283 if (!colors_enabled_ || !is_terminal_)
return "";
288 void write_to_file(
LogLevel level,
const std::string& module,
const std::string& message) {
289 if (!log_file_.is_open())
return;
292 if (max_log_file_size_ > 0 && current_file_size_ >= max_log_file_size_) {
296 std::ostringstream file_oss;
299 auto now = std::chrono::system_clock::now();
300 auto time_t = std::chrono::system_clock::to_time_t(now);
301 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
302 now.time_since_epoch()) % 1000;
306 localtime_s(&local_tm, &time_t);
308 local_tm = *std::localtime(&time_t);
310 file_oss <<
"[" << std::put_time(&local_tm,
"%Y-%m-%d %H:%M:%S");
311 file_oss <<
"." << std::setfill(
'0') << std::setw(3) << ms.count() <<
"] ";
314 file_oss <<
"[" << get_level_string(level) <<
"]";
317 if (!module.empty()) {
318 file_oss <<
" [" <<
module << "]";
322 file_oss <<
" " << message << std::endl;
324 std::string log_line = file_oss.str();
325 log_file_ << log_line;
328 current_file_size_ += log_line.length();
331 void open_log_file() {
332 if (log_file_path_.empty())
return;
337 size_t last_slash = log_file_path_.find_last_of(
"/\\");
338 if (last_slash != std::string::npos) {
339 std::string dir_path = log_file_path_.substr(0, last_slash);
346 if (rotate_on_startup_ && !startup_rotation_done_) {
349 std::ifstream check_file(log_file_path_, std::ios::ate);
350 if (check_file.is_open() && check_file.tellg() > 0) {
352 rotate_log_files_impl();
355 startup_rotation_done_ =
true;
358 log_file_.open(log_file_path_, std::ios::app);
359 if (log_file_.is_open()) {
361 log_file_.seekp(0, std::ios::end);
362 current_file_size_ =
static_cast<size_t>(log_file_.tellp());
363 log_file_.seekp(0, std::ios::end);
367 void close_log_file() {
368 if (log_file_.is_open()) {
371 current_file_size_ = 0;
374 void rotate_log_files() {
375 if (log_file_path_.empty() || max_log_files_ <= 0)
return;
378 rotate_log_files_impl();
386 void rotate_log_files_impl() {
387 if (log_file_path_.empty() || max_log_files_ <= 0)
return;
390 for (
int i = max_log_files_ - 1; i >= 1; i--) {
391 std::string old_name = log_file_path_ +
"." + std::to_string(i);
392 std::string new_name = log_file_path_ +
"." + std::to_string(i + 1);
395 if (i == max_log_files_ - 1) {
396 std::remove(new_name.c_str());
400 std::rename(old_name.c_str(), new_name.c_str());
404 std::string backup_name = log_file_path_ +
".1";
405 std::rename(log_file_path_.c_str(), backup_name.c_str());
408 mutable std::mutex mutex_;
410 bool colors_enabled_;
411 bool timestamps_enabled_;
415 bool console_logging_enabled_;
418 bool file_logging_enabled_;
419 std::string log_file_path_;
420 std::ofstream log_file_;
421 size_t max_log_file_size_;
423 size_t current_file_size_;
426 bool rotate_on_startup_;
427 bool startup_rotation_done_;