208 lines
5.4 KiB
C++
208 lines
5.4 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>
|
||
#include <memory>
|
||
#include <iostream>
|
||
|
||
|
||
//辅助函数,多个参数转化字符串
|
||
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 LogOutput {
|
||
public:
|
||
virtual ~LogOutput() = default;
|
||
virtual void write(const std::string& msg) = 0;
|
||
};
|
||
|
||
// 文件输出
|
||
class FileOutput : public LogOutput {
|
||
public:
|
||
FileOutput(const std::string& filename) : file_(filename, std::ios::out | std::ios::app) {
|
||
if (!file_.is_open()) {
|
||
throw std::runtime_error("Failed to open log file");
|
||
}
|
||
}
|
||
void write(const std::string& msg) override {
|
||
file_ << msg << std::endl;
|
||
}
|
||
~FileOutput() {
|
||
if (file_.is_open()) file_.close();
|
||
}
|
||
private:
|
||
std::ofstream file_;
|
||
};
|
||
|
||
// 控制台输出
|
||
class ConsoleOutput : public LogOutput {
|
||
public:
|
||
void write(const std::string& msg) override {
|
||
std::cout << msg << std::endl;
|
||
}
|
||
};
|
||
|
||
// 网络输出(示例,实际实现需补充)
|
||
class NetworkOutput : public LogOutput {
|
||
public:
|
||
void write(const std::string& msg) override {
|
||
// 这里可以实现网络发送逻辑
|
||
// 示例:std::cout << "[Network] " << msg << std::endl;
|
||
}
|
||
};
|
||
|
||
class Logger {
|
||
public:
|
||
// 通过unique_ptr传入多态输出对象
|
||
Logger(std::unique_ptr<LogOutput> output_obj)
|
||
: output_obj_(std::move(output_obj)), exit_flag_(false) {
|
||
worker_thread_ = std::thread([this]() {
|
||
std::string msg;
|
||
while (log_queue_.pop(msg)) {
|
||
output_obj_->write(msg);
|
||
}
|
||
});
|
||
}
|
||
~Logger() {
|
||
exit_flag_ = true;
|
||
log_queue_.shutdown();
|
||
if (worker_thread_.joinable()) {
|
||
worker_thread_.join();
|
||
}
|
||
}
|
||
|
||
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:
|
||
static 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::unique_ptr<LogOutput> output_obj_; // 多态输出对象
|
||
std::atomic<bool> exit_flag_;
|
||
};
|
||
|
||
|
||
|
||
|
||
#endif //LOGGER_H
|