CppDemo1/Logger.h

176 lines
4.6 KiB
C++

//
// Created by 张衡 on 25-7-8.
//
#ifndef LOGGER_H
#define LOGGER_H
#include <queue>
#include <mutex>
#include <string>
#include <condition_variable>
#include <thread>
#include <fstream>
#include <atomic>
#include <sstream>
#include <vector>
#include <stdexcept>
//辅助函数,多个参数转化字符串
template <typename T>
std::string to_string_helper(T&& arg) {
std::ostringstream oss;
oss << std::forward<T>(arg);
return oss.str();
}
class LogQueue {
public:
void push(const std::string& msg) {
std::lock_guard<std::mutex> lock(mutex_);
queue_.push(msg);
//构造好自动加锁,析构自动解锁
cond_var_.notify_all();
}; //字符串放队列中
bool pop(std::string& msg) {
std::unique_lock<std::mutex> lock(mutex_);
//条件变量唤醒之后直接加锁
cond_var_.wait(lock, [this]() {
return !queue_.empty() || is_shutdown_; //返回true就继续往下走
});
//消费逻辑
//队列不为空or已经关闭
if (is_shutdown_ && queue_.empty()) {
return false;
}
//保证队列的数据被清空掉
// while (is_shutdown_ && !queue_.empty()) {
// msg = queue_.front();
// queue_.pop();
// return false;
// }
msg = queue_.front(); //返回消息给msg指针
queue_.pop();
return true;
};
void shutdown() {
std::lock_guard<std::mutex> lock(mutex_);
is_shutdown_ = true;
cond_var_.notify_all();
};
private:
std::queue<std::string> queue_;
std::mutex mutex_;
std::condition_variable cond_var_;
bool is_shutdown_ = false;
};
enum class Loglevel {
INFO,
DEBUG,
WARNING,
ERROR
};
class Logger {
public:
Logger(const std::string& filename) : log_file_(filename, std::ios::out | std::ios::app)
, exit_flag_(false) {
if (!log_file_.is_open()) {
throw std::runtime_error("Failed to open log file");
}
worker_thread_ = std::thread([this]() {
std::string msg;
while (log_queue_.pop(msg)) {
log_file_ << msg << std::endl;
}
});
};
~Logger() {
exit_flag_ = true;
log_queue_.shutdown();
//如果日志结束了,说明主线程退出,这里是优雅退出,等待其他主线程
if (worker_thread_.joinable()) {
worker_thread_.join();
}
if (log_file_.is_open()) {
log_file_.close();
}
};
template<typename ... Args>
void log(Loglevel loglevel, const std::string& format, Args&& ...args) {
std::string level_str;
switch (loglevel) {
case Loglevel::INFO:
level_str = "[INFO] ";
break;
case Loglevel::DEBUG:
level_str = "[DEBUG] ";
break;
case Loglevel::WARNING:
level_str = "[WARNING] ";
break;
case Loglevel::ERROR:
level_str = "[ERROR] ";
break;
}
log_queue_.push(level_str + formatMessage(format, std::forward<Args>(args)...));
};
private:
std::string GetTimeStamp() {
std::time_t now = std::time(nullptr);
std::tm* local_time = std::localtime(&now);
char buffer[80];
std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", local_time);
return std::string(buffer);
}
template<typename ... Args>
std::string formatMessage(const std::string& format, Args&&...args) {
std::vector<std::string> arg_strings = {to_string_helper(std::forward<Args>(args))...};
std::ostringstream oss;
size_t arg_index = 0;
size_t pos = 0;
size_t placeholder = format.find("{}", pos);
oss << GetTimeStamp() << " ";
while (placeholder != std::string::npos) {
oss << format.substr(pos, placeholder - pos);
if (arg_index < arg_strings.size()) {
oss << arg_strings[arg_index];
} else {
oss << "{}";
//如果占位符没有对应参数,保留占位符
}
arg_strings[arg_index++];
pos = placeholder + 2;
placeholder = format.find("{}", pos);
}
oss << format.substr(pos);
while (arg_index < arg_strings.size()) {
oss << arg_strings[arg_index++];
}
return oss.str();
}
LogQueue log_queue_;
std::thread worker_thread_;
std::ofstream log_file_;
std::atomic<bool> exit_flag_;
};
#endif //LOGGER_H