Logger 日志类
线程安全的日志组件
默认保存到文件,并支持回调函数,比如显示到界面
cpp
#ifndef LOGGER_H
#define LOGGER_H
#include <iostream>
#include <sstream>
#include <mutex>
#include <thread>
#include <iomanip>
#include <fstream>
#include <string>
#include <condition_variable>
#include <thread>
#include <queue>
#include <chrono>
#include <ctime>
#include <functional>
// 线程安全的日志组件
class Logger
{
public:
enum class LogLevel
{
Unknown=0, //未知信息
Debug, //调试信息
Info, //普通信息
Trace, //详细信息
Warning, //警告信息
Error, //错误信息,但程序可以继续运行。
Fatal, //出现无法恢复的错误,可能导致程序崩溃。
Panic //出现严重错误,必须立即停止程序运行。
};
Logger();
Logger(const std::string& path);
~Logger();
void set_file_name(const std::string& path);
//添加回调函数
void set_callback(const std::function<void(std::pair<LogLevel, std::string>)>& func);
//输出日志
void add_log(LogLevel level, const std::string& message);
//转字符串
static std::string level2string(LogLevel level);
private:
//工作线程
void worker();
//获取当前时间
std::string get_current_timestamp();
//结束线程
void stop();
private:
std::mutex _mutex;
std::condition_variable _condition;
std::queue<std::pair<LogLevel, std::string>> _queue;
std::thread _worker;
std::string _path;
std::ofstream _file;
bool _stop;
std::function<void(std::pair<LogLevel, std::string>)> _unction;
};
#endif // LOGGER_H
cpp
#include "logger.h"
Logger::Logger(){}
Logger::Logger(const std::string& path) : _path(path),_stop(false), _worker(&Logger::worker, this)
{
_file.open(_path, std::ios_base::app);
}
void Logger::set_file_name(const std::string& path)
{
_path = path;
_file.open(_path, std::ios_base::app);
}
Logger::~Logger()
{
stop();
_worker.join();
//关闭文件
if (_file.is_open())
{
_file.close();
}
}
//添加回调函数 输出
void Logger::set_callback(const std::function<void(std::pair<LogLevel, std::string>)>& func)
{
_unction = func;
}
std::string Logger::level2string(LogLevel level)
{
switch (level)
{
case LogLevel::Debug: return "Debug";
case LogLevel::Info: return "Info";
case LogLevel::Trace: return "Trace";
case LogLevel::Warning: return "Warning";
case LogLevel::Error: return "Error";
case LogLevel::Fatal: return "Fatal";
case LogLevel::Panic: return "Panic";
default: return "Unknown";
}
}
//输出日志
void Logger::add_log(LogLevel level, const std::string& message)
{
std::lock_guard<std::mutex> lock(_mutex);
_queue.emplace(level, message);
_condition.notify_one();
}
//工作线程
void Logger::worker()
{
for(;;)
{
std::pair<LogLevel, std::string> pair;
{
std::unique_lock<std::mutex> lock(_mutex);
_condition.wait(lock, [this] { return _stop || !_queue.empty(); });
if (_stop && _queue.empty())
{
break;
}
pair = _queue.front();
_queue.pop();
}
// //输出到文件
// std::ofstream out(_path, std::ios_base::app);
// if (!out.good())
// {
// continue;
// }
// out << get_current_timestamp() <<" "<<level2string(pair.first) <<":"<< pair.second << std::endl;
// out.close();
// 如果文件流不可用,则重新尝试打开
if (!_file.is_open())
{
_file.open(_path, std::ios_base::app);
if (!_file.is_open())
{
// 打开文件失败,继续下一个循环
continue;
}
}
// 输出日志消息到文件流
_file << get_current_timestamp() << " " << level2string(pair.first) << ":" << pair.second << std::endl;
_file.flush(); // 刷新缓冲区
if(_unction)
{
_unction(pair);
}
}
}
//获取当前时间
std::string Logger::get_current_timestamp()
{
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
std::stringstream ss;
ss << std::put_time(std::localtime(&time), "%Y-%m-%d %H:%M:%S") << '.' << std::setfill('0') << std::setw(3) << ms.count();
return ss.str();
}
//结束线程
void Logger::stop()
{
std::lock_guard<std::mutex> lock(_mutex);
_stop = true;
_condition.notify_one();
}
比如:
使用qt界面,接收日志,,通过接收 logReceived 信号,获取日志信息显示。这里QtConcurrent::run 可以方便接收信息,触发异步信号,不阻塞界面。不然界面会卡死,更新UI只能在一个线程中
cpp
// 增加日志回调
void StreamManagerWidget::log_function(std::pair<Logger::LogLevel, std::string> pair)
{
// 在后台线程执行日志记录任务
QtConcurrent::run(&_threadPool,[this, pair](){
// 将日志信息格式化为 QString
QString logMessage = QString::fromStdString(Logger::level2string(pair.first) +": "+ pair.second);
// 发送信号,在主线程中更新 UI
emit logReceived(logMessage);
});
}