CppDemo1/Logger.h

208 lines
5.4 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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