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

使用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.mfbz.cn/a/740642.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

学期结束如何发布期末成绩?

当期末的试卷最后一张被收起&#xff0c;当教室里的喧嚣逐渐沉寂&#xff0c;学生们的心中充满了对成绩的期待与忐忑。期末成绩&#xff0c;关乎着学生的心情&#xff0c;更关系到他们的未来学习动力。那么&#xff0c;如何在保护学生隐私的同时&#xff0c;高效地公布成绩呢&a…

分享:Khoj:你的全能AI助手

在数字化时代&#xff0c;我们每天都会面对海量的信息&#xff0c;如何高效地管理和检索这些信息&#xff0c;同时提升工作效率&#xff0c;成为了许多人关注的焦点。为此&#xff0c;Khoj应运而生——一个功能强大、灵活多变的个人化AI助手&#xff0c;旨在助力用户轻松驾驭信…

双jdk切换

现在因为业务需求单一jdk8已经不满足日常需求了,以我为例之前用的jdk8,但是最新的一个项目用的是17版本的,没招了就下载配置的一套,需要手动切换用哪个版本的步骤如下 jdk8就自己安装配置吧,这只说在有8的版本上在配置17 1.下载一个17win的包(不下载exe) Java Downloads | O…

使用深度相机D435i+YOLOv8实现物体三维坐标实时显示

一、获取相机内参 下列指令为获取相机内参指令&#xff0c;输入此指令前需要获得相机的深度帧和彩色帧数据。 如何使用vsCode打开intel D435i深度相机 # 获取相机内参 depth_intrinsics depth_frame.profile.as_video_stream_profile().intrinsics color_intrinsics color…

Bootstrap和Bagging算法以及衍生算法

1. Bootstrap算法 实际上就是一种针对小样本的无放回式的抽样方法&#xff0c;通过方差的估计可以构造置信区间。 其核心思想和基本步骤如下&#xff1a;   &#xff08;1&#xff09; 采用重抽样技术从原始样本中抽取一定数量&#xff08;自己给定&#xff09;的样本&#…

Python高压电容导电体和水文椭圆微分

&#x1f3af;要点 &#x1f3af;二维热传导二阶偏微分方程 | &#x1f3af;调和函数和几何图曲率 | &#x1f3af;解潮汐波动方程 | &#x1f3af;解静止基态旋转球体流体运动函数 | &#x1f3af;水文空间插值 | &#x1f3af;流体流动模拟求解器 | &#x1f3af;随机算法解…

网络构建关键技术_1.网络高可用设计

1.网络高可用性概述 随着网络快速发展及应用日益深入&#xff0c;各种核心和增值业务在网络上广泛部署&#xff0c;网络的作用愈来愈凸显出来。即使网络出现短时间中断&#xff0c;都可能对业务带来比较大的影响&#xff0c;甚至给企业造成一定程度的经济损失。因此&#xff0c…

【CT】LeetCode手撕—23. 合并 K 个升序链表

目录 题目1- 思路2- 实现⭐23. 合并 K 个升序链表——题解思路 3- ACM 实现 题目 原题连接&#xff1a;23. 合并 K 个升序链表 1- 思路 模式识别&#xff1a;合并 K 个链表 ——> 优先队列 思路 借助优先队列&#xff0c;每次从 k 个链表中&#xff0c;各取一个元素&…

全球开店7万家,711如何策划极致产品力?

引言&#xff1a;越来越多的中国企业家认识到“极致产品力正成为最重要、最核心的品牌战略&#xff0c;是 21 世纪全球商业的主战场”。 《极致产品力》日本策划方案落地班是一个产品策划实操课程&#xff0c;可以帮助企业找到增长路径、拿到产品方案。 现今&#xff0c;许多中…

分布式数据库垂直分片如何计算?

在复习分布式数据库这一部分时&#xff0c;发现垂直分片较难理解&#xff0c;上网上查找资料&#xff0c;也发现找不到&#xff0c;固在考试结束后将自己的理解记录下来。 垂直分片的计算总体上分为三个步骤;1、属性亲和度矩阵AA的计算&#xff1b;2、聚类亲和度CA矩阵的计算&a…

基于VTK9.3.0+Visual Studio2017 c++实现DICOM影像MPR多平面重建

开源库&#xff1a;VTK9.3.0 开发工具&#xff1a;Visual Studio2017 开发语言&#xff1a;C 实现过程&#xff1a; void initImageActor(double* Matrix, double* center, vtkSmartPointer<vtkImageCast> pImageCast,vtkSmartPointer<vtkImageReslice> imageRe…

6.折半查找

折半查找 基本思路在有序表中,取中间元素作为比较对象,若给定值与中间元素的要查找的数相等,则查找成功;若给定值小于中间元素的要查找的数,则在中间元素的左半区继续查找;若给定值大于中间元素的要查找的数,则在中间元素的右半区继续查找。不断重复上述查找过 程,直到查找成功…

数据结构——二分算法

二分查找 1. 在排序数组中查找元素的第一个和最后一个位置 代码实现&#xff1a; /*** Note: The returned array must be malloced, assume caller calls free().*/int binarySearch(int *nums, int numsSize, int target) {int l 0, r numsSize - 1; while (l <…

Kubernetes 中 ElasticSearch 中的 MinIO 审核日志

无论您是在本地还是在云中&#xff0c;您都希望确保以同构的方式设置工具和流程。无论在何处访问基础结构&#xff0c;您都希望确保用于与各种基础结构进行交互的工具与其他区域相似。 考虑到这一点&#xff0c;在部署您自己的 MinIO 对象存储基础架构时&#xff0c;深入了解您…

缓存雪崩、穿透、击穿的概念和如何避免

雪崩 概念 高并发请求多个key&#xff0c;此时多个key同时失效、不在缓存中&#xff0c;请求全部打到数据库&#xff0c;使数据库无法处理这么多的连接&#xff0c;导致数据库死机 如何避免 防止多个key同时过期&#xff0c;设置随机的过期时间不设置过期时间&#xff0c;有…

汽车抬头显示器HUD阳光倒灌实验太阳光模拟器

简述 HUD阳光倒灌实验是评估汽车抬头显示器&#xff08;HUD&#xff09;在强烈日照条件下的性能表现的一种测试方法。该实验通过模拟太阳光照射&#xff0c;检测HUD在阳光直射下的显示效果&#xff0c;以确保驾驶者在强烈日照下仍能清晰地看到HUD显示的信息&#xff0c;从而提…

【计算机视觉】人脸算法之图像处理基础知识(六)

图像直方图 图像直方图是描述图像中像素强度分布的一种统计图表&#xff0c;它是图像处理和计算机视觉领域中一个非常基础且重要的概念。图像直方图通常用于分析图像的亮度、对比度特性&#xff0c;以及在图像增强、阈值分割、特征提取等多种图像处理任务。 import cv2 impor…

ogg格式怎么转换为mp3?这四种转换方法非常好用!

ogg格式怎么转换为mp3&#xff1f;在当今数字化时代&#xff0c;音频文件格式的转换已经成为确保音频内容在各种设备和平台上无缝播放的至关重要的一环&#xff0c;尽管OGG格式拥有支持多声道的优势&#xff0c;但由于其使用率相对较低&#xff0c;这可能会在某些情况下导致兼容…

基于单片机的智能台灯控制系统

摘要&#xff1a; 文章设计一款单片机智能台灯控制系统&#xff0c;实现对台灯的手动和自动控制功能&#xff0c;以 STC89C52 单片机作为多功能智能台灯的主控制器&#xff0c;光电检测模块检测坐姿&#xff0c;红外传感器检测人体&#xff0c;光敏电阻检测光强&#xff0c;同…

数字人视频剪辑解决方案,个性化定制服务

如何高效、专业地剪辑出高质量的数字人视频&#xff0c;却成为众多企业面临的难题。针对这一挑战&#xff0c;美摄科技凭借其在人工智能、计算机视觉等领域的深厚积累&#xff0c;推出了面向企业的数字人视频剪辑解决方案&#xff0c;为企业带来前所未有的视频制作新体验。 一…