C++多线程异步日志实现

news2025/1/12 22:52:41

使用C++11标准,构建了一个方便使用的、轻量化的日志系统。封装线程安全的lockQueue,实现对每条日志添加信息、push到lockQueue中的LogTmp类,实现一个多线程异步的日志系统Logger。

lockqueue.h

#pragma once
#include <queue>
#include <string>
#include <thread>
#include <mutex> // pthread_mutex_t
#include <condition_variable> // pthread_condition_t

// 异步写日志的日志队列
class LockQueue
{
public:
    // 多个worker线程都会写日志queue 
    void Push(const std::string &data)
    {
        std::lock_guard<std::mutex> lock(m_mutex);
        m_queue.push(data);
        m_condvariable.notify_one();
    }

    // 一个线程读日志queue,写日志文件
    std::string Pop()
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        while (m_queue.empty())
        {
            // 日志队列为空,线程进入wait状态
            m_condvariable.wait(lock);
        }

        std::string data = m_queue.front();
        m_queue.pop();
        return data;
    }
private:
    std::queue<std::string> m_queue;
    std::mutex m_mutex;
    std::condition_variable m_condvariable;
};

 logger.h

#pragma once
#include "lockqueue.h"
#include "timestamp.h"
#include <sstream>
#include <string>
#include <mutex>
#include <fstream>
#include <atomic>

// 定义日志级别 INFO  ERROR  FATAL  DEBUG
enum LogLevel
{
    DEBUG = 0, // 调试信息
    INFO,  // 普通信息
    ERROR, // 错误信息
    FATAL, // core信息
};

class LogTmp;
class Logger;

// 直接使用左移运算法<<进行输出,末尾不需要加换行
#define LOG_INFO \
    if(Logger::getInstance()->isEnable() && LogLevel::INFO >= Logger::getInstance()->getLogLevel()) \
        LogTmp::ptr(new LogTmp(Logger::getInstance(), INFO, __FILE__, __LINE__, __FUNCTION__))->getStringStream()
// 直接使用左移运算法<<进行输出,末尾不需要加换行
#define LOG_ERROR \
    if(Logger::getInstance()->isEnable() && LogLevel::ERROR >= Logger::getInstance()->getLogLevel()) \
        LogTmp::ptr(new LogTmp(Logger::getInstance(), ERROR, __FILE__, __LINE__, __FUNCTION__))->getStringStream()
// 直接使用左移运算法<<进行输出,末尾不需要加换行
#define LOG_FATAL \
    if(Logger::getInstance()->isEnable() && LogLevel::FATAL >= Logger::getInstance()->getLogLevel()) \
        LogTmp::ptr(new LogTmp(Logger::getInstance(), FATAL, __FILE__, __LINE__, __FUNCTION__))->getStringStream()


#define LOG_DEBUG \
    if(Logger::getInstance()->isEnable() && LogLevel::DEBUG >= Logger::getInstance()->getLogLevel()) \
        LogTmp::ptr(new LogTmp(Logger::getInstance(), DEBUG, __FILE__, __LINE__, __FUNCTION__))->getStringStream()



class LogTmp{
public:
    using ptr = std::unique_ptr<LogTmp>; // 使用指针,防止对象提前被析构
    LogTmp(Logger* logger, const int logLevel, const char *file, const int line, const char* func);
    ~LogTmp();
    std::stringstream& getStringStream();
private:
    Logger* logger_;
    std::stringstream ss_; // 通过<<接收用户的多种数据的log信息
};


// 可以设置是否启用日志(默认为启用)和日志等级(默认为最低级)
class Logger
{
public:
    // static std::stringstream& getStringStream(const int logLevel,
    //             const char* file = __FILE__,   
    //             const int line = __LINE__,
    //             const char* func = __FUNCTION__);

    static Logger* getInstance();

    void setEnableLogger(bool flag);

    void setLogLevel(LogLevel level);
    int getLogLevel(){ return logLevel_; }

    bool isEnable(){ return enableLogger_; }

    void pushLog(const std::string& log);

private:
    Logger();
    ~Logger();
    std::atomic_bool enableLogger_; // 设置是否启用日志功能,默认为true
    
    std::atomic_int logLevel_;

    // 静态变量,全局只有一个,在多线程的多个实例化对象中也可保持线程安全
    static std::mutex fileMutex_;
    static std::mutex loggerMutex_;
    static LockQueue lockQue_;
    static std::unique_ptr<std::thread> writeLogThread_;

    static Logger *logger_;
};

 logger.cc

#include "logger.h"
#include <thread>
#include <iostream>
 
LockQueue Logger::lockQue_;
std::mutex Logger::fileMutex_;
std::mutex Logger::loggerMutex_;
std::unique_ptr<std::thread> Logger::writeLogThread_;
Logger* Logger::logger_ = nullptr;

LogTmp::LogTmp(Logger* logger, const int logLevel, const char *file, const int line, const char* func)
    : logger_(logger)
{
    std::string time = Timestamp::now().toString();
    ss_ << time;
    switch (logLevel)
    {
    case INFO:
        ss_ << "[INFO] ";
        break;
    case ERROR:
        ss_ << "[ERROR]";
        break;
    case FATAL:
        ss_ << "[FATAL]";
        break;
    case DEBUG:
        ss_ << "[DEBUG]";
        break;
    default:
        break;
    }
    ss_ << " FILE:" << file << " LINE:" << line << " FUNC:" << func << "::";
    // std::cout << ss_.str() << std::endl;
}

LogTmp::~LogTmp(){
    ss_ << std::endl;
    // std::cout << "LogTmp::~LogTmp()" << std::endl;
    logger_->pushLog(ss_.str());
}
std::stringstream& LogTmp::getStringStream(){
    // std::cout << "LogTmp::getStringStream()" << std::endl;
    return this->ss_;
}

Logger::Logger()
    : enableLogger_(true), logLevel_(LogLevel::DEBUG)
{
    // 启动写文件的新线程,专门从lockQue中取日志写文件
    if(writeLogThread_ == nullptr){
        writeLogThread_ = std::make_unique<std::thread>([&](){
            while(1){
                // 取出一条日志
                std::string loginfo = lockQue_.Pop();
                // std::cout << "aaa" << loginfo;
                // 获取当前日期
                std::string time = Timestamp::now().toString();
                std::string filePath = time.substr(0, 10) + "-log.txt";
                // std::cout << "aaa" << filePath << std::endl;
                std::unique_lock<std::mutex> ulock(fileMutex_);
                // C++ 文件流
                std::ofstream file(filePath, std::ios::app);
                if (file.is_open())
                {
                    file << loginfo;
                    file.close();
                }
                else
                {
                    std::cout << "error : log file not open!" << std::endl;
                    return;
                }
            }
        });
        // 设置分离线程,守护线程
        writeLogThread_->detach();
    }
}

// 在此处将ss_中所有内容写入LockQue
Logger::~Logger()
{}
void Logger::pushLog(const std::string& log){
    lockQue_.Push(log);
}

// std::stringstream& Logger::getStringStream(const int logLevel, const char *file, const int line, const char *func)
// {
//     {
//         std::unique_lock<std::mutex> ulock(loggerMutex_);
//         if(logger_ == nullptr){
//             logger_ = new Logger();
//         }
//     }

//     LogTmp logTmp(logger_, logLevel, file, line, func);
//     return logTmp.getStringStream();
// }

Logger *Logger::getInstance()
{
    std::unique_lock<std::mutex> ulock(loggerMutex_);
    if(logger_ == nullptr){
        logger_ = new Logger();
    }
    ulock.unlock();
    return logger_;
}

void Logger::setEnableLogger(bool flag)
{
    enableLogger_ = flag;
}

void Logger::setLogLevel(LogLevel level)
{
    logLevel_ = level;
}

testlog.cc

#include "logger.h"

int main(){
    Logger::getInstance()->setLogLevel(LogLevel::DEBUG);
    {
    LOG_DEBUG << "debug";
    LOG_INFO << "info";
    LOG_ERROR << "error";
    LOG_FATAL << "fatal";
    }

    Logger::getInstance()->setLogLevel(LogLevel::INFO);
    {
    LOG_DEBUG << "debug";
    LOG_INFO << "info";
    LOG_ERROR << "error";
    LOG_FATAL << "fatal";
    }

    Logger::getInstance()->setLogLevel(LogLevel::ERROR);
    {
    LOG_DEBUG << "debug";
    LOG_INFO << "info";
    LOG_ERROR << "error";
    LOG_FATAL << "fatal";
    }

    Logger::getInstance()->setLogLevel(LogLevel::FATAL);
    {
    LOG_DEBUG << "debug";
    LOG_INFO << "info";
    LOG_ERROR << "error";
    LOG_FATAL << "fatal";
    }
    
    getchar();
    return 0;
}

 日志写入测试结果

上图中空行是测试完成后手打进去的,便于查看日志分级结果。 (第一行的hello是之前的日志)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1858490.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pdf转图片转换器,pdf转图片的工具

在日常的工作和学习中&#xff0c;我们经常会遇到需要将PDF文件转换为图片格式的情况。那么&#xff0c;如何才能将PDF格式转换为图片格式呢&#xff1f;今天&#xff0c;我将为大家介绍几种简单易用的方法&#xff0c;帮助大家轻松实现PDF转图片。 打开“轻云pdf处理官网网站”…

Linux 动态监控系统

top与ps命令很相似。它们都用来显示正在执行的进程。Top与ps最大的不同之处&#xff0c;在于top在执行一段时间可以更新正在运行的的进程。 一、基本指令 top top -d&#xff1a; 秒数 :每隔设定值秒数更新&#xff0c;未设置下默认为3秒 top -i:使top不显示任何闲置或者僵死进…

行人重识别Reid(可实现人员换装情况下的人员检索)

本项目的行人重识别是出自论文"Beyond Scalar Neuron: Adopting Vector-Neuron Capsules for Long-Term Person Re-Identification",该文章所涉及到的相关理论在我另外一篇文章中有讲解&#xff1a;Reid系列论文学习——换装Reid&#xff0c;不过经过本人测试&#x…

高清录屏软件哪个好用?分享这3款录屏软件

在我们的日常生活和工作中&#xff0c;视频录制是我们经常需要使用到的功能。不仅可以帮助我们录制网课内容方便后期复习&#xff0c;还可以录制视频会议&#xff0c;记录上级指示&#xff0c;甚至可以通过录制一些视频教程或游戏视频上传网络进行分享。 对电脑屏幕进行录制&a…

postman教程-22-Newman结合Jenkins执行自动化测试

上一小节我们学习了Postman Newman运行集合生成测试报告的方法&#xff0c;本小节我们讲解一下Postman Newman结合Jenkins执行自动化测试的方法。 在软件开发过程中&#xff0c;持续集成&#xff08;CI&#xff09;是一种实践&#xff0c;旨在通过自动化的测试和构建过程来频繁…

QThread 与QObject::moveToThread利用Qt事件循环在子线程执行多个函数

1. QThread的两种用法 第一种用法就是继承QThread&#xff0c;然后覆写 virtual void run()&#xff0c; 这种用法的缺点是不能利用信号槽机制。 第二种用法就是创建一个线程&#xff0c;创建一个对象&#xff0c;再将对象moveToThread, 这种可以充分利用信号槽机制&#xff…

使用插件和微调优化 GPT 模型

文章目录 LLM 用例和示例产品警惕 AI 幻觉&#xff1a;限制与考虑使⽤插件和微调优化 GPT 模型 OpenAI 在其网站上展示了许多激励人心的客户故事&#xff0c;我们需要了解这些模型如何改变我们的社会并为商业和创造力开辟新机遇。正如你将看到的&#xff0c;许多企业已经开始使…

【kaggle数据集无法下载解决办法】

kaggle数据集无法下载的解决办法 当我们在做机器学习相关问题的时候&#xff0c;需要到kaggle网站上下载数据集&#xff0c;但是很多时候速度很慢或者连接超时等问题&#xff0c;此时解决办法如下&#xff1a; 在本地安装Kaggle API包 打开终端输入如下指令&#xff1a; pip i…

ONLYOFFICE8.1-------宝藏级别桌面编辑器测评

简介 ONLYOFFICE 8.1 是一个功能强大的办公套件&#xff0c;提供了一系列广泛的功能&#xff0c;用于文档管理、协作和沟通。它包括用于创建和编辑文本文档、电子表格、演示文稿等的工具。ONLYOFFICE 8.1 的一些关键特性包括&#xff1a; 1. 协作&#xff1a;ONLYOFFICE 8.1 …

Ubuntu 使用Vscode的一些技巧 ROS

Ubuntu VSCode的一些设置&#xff08;ROS&#xff09; 导入工作空间 推荐只导入工作空间下的src目录 如果将整个工作空间导入VSCode&#xff0c;那么这个src就变成了次级目录&#xff0c;容易在写程序的时候把本应该添加到具体工程src目录里的代码文件给误添加到这个catkin_w…

邂逅Three.js探秘图形世界之美

可能了解过three.js等大型的3D 图形库同学都知道啊&#xff0c;学习3D技术都需要有图形学、线性代数、webgl等基础知识&#xff0c;以前读书学的线性代数足够扎实的话听这节课也会更容易理解&#xff0c;这是shader课程&#xff0c;希望能帮助你理解着色器&#xff0c;也面向第…

20240624在飞凌OK3588-C的Buildroot下查证GPIO64和gpiochip64的差异

20240624在飞凌OK3588-C的Buildroot下查证GPIO64和gpiochip64的差异 2024/6/24 20:19 GPIOchip代表GPIO控制器的编号&#xff0c;gpio代表特定GPIO的引脚号 本文以linux R4/Buildroot位例子&#xff0c;同样适用于Android12和其他【使用linux内核的】操作系统。 https://www.ji…

Java包介绍

今天看jdk文档&#xff0c;顺便写一下java几个包的作用。 java.applet 主要用于创建java applet小应用程序&#xff0c;可以嵌入到网页中能够呈现出特殊的效果&#xff0c;现在基本已经被废弃&#xff0c;很少使用。 java.awt AWT 是Abstract Window ToolKit (抽象窗口工具包…

分享9款AI抠图神器:不会PS?AI一键批量抠图,3秒轻松搞定!(建议收藏)

文章首发于公众号&#xff1a;X小鹿AI副业 大家好&#xff0c;我是程序员X小鹿&#xff0c;前互联网大厂程序员&#xff0c;自由职业2年&#xff0c;也一名 AIGC 爱好者&#xff0c;持续分享更多前沿的「AI 工具」和「AI副业玩法」&#xff0c;欢迎一起交流~ 今天被一位在用 AI…

猫头虎 AI 前沿科技探索之路(持续更新):ChatGPT/GPT-4 科研应用、论文写作、数据分析与 AI 绘图及文生视频实战全攻略

猫头虎 AI 前沿科技探索之路(持续更新)&#xff1a;ChatGPT/GPT-4 科研应用、论文写作、数据分析与 AI 绘图及文生视频实战全攻略 背景介绍 随着人工智能技术的飞速发展&#xff0c;AI 的应用已经渗透到各个领域&#xff0c;从商业决策到医疗健康&#xff0c;再到日常生活中的…

【扩散模型(一)】Stable Diffusion中的重建分支(reconstruction branch)和条件分支(condition branch)

Stable Diffusion 是一种基于扩散模型的生成模型&#xff0c;用于生成图像等数据。在解释 Stable Diffusion 的过程中&#xff0c;经常会提到两个主要的分支&#xff1a;重建分支&#xff08;reconstruction branch&#xff09;和条件分支&#xff08;condition branch&#xf…

猫头虎 分享已解决Error || **Data Leakage**: `Unexpectedly high validation performance`

猫头虎 分享已解决Error || Data Leakage: Unexpectedly high validation performance &#x1f42f; 摘要 &#x1f4c4; 大家好&#xff0c;我是猫头虎&#xff0c;一名专注于人工智能领域的博主。在AI开发中&#xff0c;我们经常会遇到各种各样的错误&#xff0c;其中Data…

通过ETLCloud实现SQL Server数据同步至Oracle

SQL Server与Oracle作为全球两大主流的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;在企业级应用中扮演着至关重要的角色。它们各自凭借独特的技术优势、强大的数据处理能力以及高度的可扩展性&#xff0c;支撑着从中小型企业到大型跨国公司的各类复杂业务需…

【面试题】前端 移动端自适应?_前端移动端适配面试题

设备像素比 设备像素比 (DevicePixelRatio) 指的是设备物理像素和逻辑像素的比例 。比如 iPhone6 的 DPR 是2。 设备像素比 物理像素 / 逻辑像素。可通过 window.devicePixelRatio 获取&#xff0c;CSS 媒体查询代码如下 media (-webkit-min-device-pixel-ratio: 3), (min-…

Springboot 前端传参后台接收当不存在参数bean对象时报错解决

后端接收代码 PostMapping(value "/updateUser") public String updateUser(RequestBody SysUser sysUser) {} 当前端传送多于的参数时报错如下&#xff1a; Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: U…