使用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是之前的日志)