c++ 自定义Logger 日志类

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);
    });
}
相关推荐
长弓聊编程几秒前
Linux系统使用valgrind分析C++程序内存资源使用情况
linux·c++
陌小呆^O^4 分钟前
Cmakelist.txt之win-c-udp-client
c语言·开发语言·udp
cherub.7 分钟前
深入解析信号量:定义与环形队列生产消费模型剖析
linux·c++
I_Am_Me_20 分钟前
【JavaEE进阶】 JavaScript
开发语言·javascript·ecmascript
暮色_年华22 分钟前
Modern Effective C++item 9:优先考虑别名声明而非typedef
c++
重生之我是数学王子30 分钟前
QT基础 编码问题 定时器 事件 绘图事件 keyPressEvent QT5.12.3环境 C++实现
开发语言·c++·qt
Ai 编码助手32 分钟前
使用php和Xunsearch提升音乐网站的歌曲搜索效果
开发语言·php
学习前端的小z36 分钟前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
神仙别闹43 分钟前
基于C#和Sql Server 2008实现的(WinForm)订单生成系统
开发语言·c#
XINGTECODE44 分钟前
海盗王集成网关和商城服务端功能golang版
开发语言·后端·golang