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_; // 设置是否启用日志功能,默认为truestd::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.mzph.cn/pingmian/34496.shtml

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

相关文章

Go微服务: redis分布式锁保证数据原子操作的一致性

概述 随着云计算和大数据技术的飞速发展&#xff0c;分布式系统已经成为现代IT架构的重要组成部分在分布式系统中&#xff0c;数据的一致性是一个至关重要的挑战&#xff0c;特别是在并发访问和修改共享资源的场景下分布式锁是一种跨进程、跨机器节点的互斥锁&#xff0c;用于…

如何模拟一个具有网络管理功能的被测件的一些思路

不知道大家有没有遇到过这个问题&#xff1f; 当我们在学习如何测试网络管理时&#xff0c;难题不在于如何编写测试脚本&#xff0c;而是编写完测试脚本后&#xff0c;没有真实被测件来让我们执行测试脚本&#xff0c;进而调试脚本。这也是我在给大家讲CANoe工具和CAPL编程语言…

08.QT控件:QWidget

一、Widget 简介 Widget 是 Qt 中的核⼼概念.。英⽂原意是 "小部件"&#xff0c;我们此处也把它翻译为 "控件"。控件是构成⼀个图形化界⾯的基本要素。 Qt 作为⼀个成熟的 GUI 开发框架, 内置了⼤量的常⽤控件。并且 Qt 也提供了 "⾃定义控件" 的…

《第一行代码 第3版》学习笔记——第十一章 网络技术

1 webview用法 class MainActivity : ComponentActivity() {SuppressLint("SetJavaScriptEnabled")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {NetWorkDemoTheme {// A surface container using the bac…

主流MQ对比和选型

在以下几个我们比较关心的维度进行对比 ActiveMQ RabbitMQ RocketMQkafka官网https://activemq.apache.org/https://www.rabbitmq.com/https://rocketmq.apache.org/https://kafka.apache.org/githubhttps://github.com/apache/activemqhttps://github.com/rabbitmqhttps://g…

AI如何让办公更智能?WPS AI海外版给出答案

导读&#xff1a;从语义检查到一键生成PPT&#xff0c;WPS Office海外版如何面向2亿月活用户快速推出AI功能&#xff1f; 近日&#xff0c;WPS Office海外版应用亚马逊云科技Amazon Bedrock等生成式AI技术与服务&#xff0c;在海外正式推出人工智能应用WPS AI海外版&#xff0c…

Postman测试,如何保持用户登录状态?

为了在Postman中保持用户登录状态&#xff0c;我们可以使用以下步骤&#xff1a; 1. 下载和安装Postman 首先&#xff0c;我们需要下载和安装Postman。Postman是一个流行的API开发和测试工具&#xff0c;可以帮助我们发送HTTP请求并测试API的功能。 2. 创建一个新的Postman …

【Vue】vue-router路由使用

前言 Vue Router是Vue框架中非常重要的一个功能。 目标 1 单页面应用与多页面应用的区别; 2 vue-router的具体实现方法; 3 路由模式有哪几种,有什么区别; 4 如何进行路由守卫与路由缓存; 一 路由的概念 概念 Vue Router是Vue提供的路由管理器。将组件与路由一一对应起来,…

6-2 归并排序

6-2 归并排序 分数 10 全屏浏览 切换布局 作者 软件工程DS&A课程组 单位 燕山大学 以下代码采用分而治之算法实现归并排序。请补充函数mergesort&#xff08;&#xff09;的代码。提示&#xff1a;mergesort&#xff08;&#xff09;函数可用递归实现&#xff0c;其中参…

Conda创建与激活虚拟环境(指定虚拟环境创建位置)

1.Conda优势 Conda是一个开源的软件包管理系统和环境管理系统&#xff0c;主要用于在不同的计算环境中安装和管理软件包和其依赖项。它最初是为Python而设计的&#xff0c;但现在也可以用于管理其他语言的软件包。 Conda提供了对虚拟环境的支持&#xff0c;这使得用户可以在同…

如何在Java中处理UnsupportedOperationException异常?

如何在Java中处理UnsupportedOperationException异常&#xff1f; 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在Java编程中&#xff0c;我们经常会遇到各…

swiper实例

大家好&#xff0c;我是燐子&#xff0c;今天给大家带来swiper实例 微信小程序中的 swiper 组件是一种用于创建滑动视图的容器组件&#xff0c;常用于实现图片轮播、广告展示等效果。它通过一系列的子组件 swiper-item 来定义滑动视图的每一个页面。 基本用法 以下是一个简单的…

ESAPI.setAttribute设置值前端取不到

我在后端使用java设置email request.setAttribute("email",ESAPI.encoder().encodeForHTML("123456qq.com"))前端jsp页面获取不到&#xff0c; var email"<%ESAPI.encoder().encodeForHTML(request.getParameter("email"))%>"…

web前端——HTML

目录 一、HTML概述 1.HTML是什么&#xff1f; 2.HTML具体化解释 二、HTML基本语法 1.声明 2. Head头标签 3.body身体标签 4.一个html的基本结构 5.标签 6.标签属性 ①属性的格式 ②属性的位置 ③添加多个属性 三、基本常用标签 1.超链接 2.图像标签 ①图像标…

springboot集成JPA并配置hikariCP连接池问题解决

一、引入需要的依赖 springboot版本 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-parent</artifactId><version>2.3.2.RELEASE</version><relativePath/></parent> jpa依赖 <!--…

从零开始做题:会打篮球的鸡

会打篮球的鸡 1 题目 给你password你帮鸡肋找找会打篮球的鸡在哪儿行吗&#xff1f; password:iVBORw0KGgoAAAANSUhEUgAAAgAAAPoCAIAAADCwUOzAAAACXBIWXMAAAsTAAALEwEAmpwYAAB2KElEQVR4nO3dd3xb1f3/8WvLe8QjdpbtxJm2Eyd29t6LJBBWgEICFAqUUmaBlrZ8Ke23fLFUvYsYRQKZScECGQHkpC9…

OpenGL进阶系列1 - OpenGL1.x和2.x功能演进(上古历史)

时间版本功能详细描述1992v1.0 NewList/EndList/CallListglspec10.pdfBegin/Endglspec10.pdfVertex/TexCoord/Color/Normal/Index/Rectglspec10.pdfMatrixMode/LoadMatrix/Multmatrixglspec10.pdfRoate/Translate/Scaleglspec10.pdf

1964springboot VUE小程序在线学习管理系统开发mysql数据库uniapp开发java编程计算机网页源码maven项目

一、源码特点 springboot VUE uniapp 小程序 在线学习管理系统是一套完善的完整信息管理类型系统&#xff0c;结合springboot框架uniapp和VUE完成本系统&#xff0c;对理解vue java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09;&#xff0c;…

DLS平台:GPT-5预计于2025年底至2026年初发布,将实现“博士水平”智能

摘要 OpenAI首席技术官Mira Murati近日透露&#xff0c;GPT-5可能推迟到2025年底或2026年初发布。这一消息打破了市场对GPT-5在2023年底或2024年夏季发布的预期。尽管推迟&#xff0c;但GPT-5将实现显著的性能飞跃&#xff0c;在特定任务中达到“博士水平”的智能。这标志着人…

Java 8 Date and Time API

Java 8引入了新的日期和时间API&#xff0c;位于java.time包下&#xff0c;旨在替代旧的java.util.Date和java.util.Calendar类。新API更为简洁&#xff0c;易于使用&#xff0c;并且与Joda-Time库的一些理念相吻合。以下是Java 8 Date and Time API中几个核心类的简要概述&…