c++ 线程安全queue队列Logger日志

单例模式的日志记录器设计
本文介绍了一个基于单例模式实现的日志记录器类,详细解释了其工作流程,包括线程安全的初始化、日志消息的格式化与队列化、以及异步输出至文件或标准输出。此外,提供了完整的源代码实现。

1、简单逻辑

进程中单例模式Logger类,第一次getInstance()进行初始化后,并启动log输出线程。
若不使用setLogFile()设置log输出文件,默认是标准输出std::cout。

用户格式化的log信息,加锁入队,使用条件变量通知唤醒等待的log输出线程。

log输出线程中,当队列不为空,就进行输出到制定文件或者std::cout,否者wait(),解锁,直到有入队唤醒,再次加锁。

2、Logger.h 源码

#pragma once

#include <queue>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <fstream>
#include <cstring>
#include <iostream>
#include <sstream>

enum Priority {
    LOG_DEBUG, LOG_STATE, LOG_INFO, LOG_WARNING, LOG_ERROR,
};	
	
class Logger
{
public:
    Logger &operator=(const Logger &) = delete;
    Logger(const Logger &) = delete;	
    static Logger& instance();
    ~Logger();

    void setLogFile(char *pathname);
    void log(Priority priority, const char* __file, const char* __func, int __line, const char *fmt, ...);
	void log2(Priority priority, const char *fmt, ...);

private:
    Logger();
    void run();

    std::atomic<bool> _shutdown;
    std::thread _thread;
    std::mutex _mutex;
    std::condition_variable _cond;
    std::queue<std::string> _queue;
    std::ofstream _ofs;
};

}
#ifdef _DEBUG
#define LOG_DEBUG(fmt, ...) Logger::instance().log(LOG_DEBUG, __FILE__, __FUNCTION__,__LINE__, fmt, ##__VA_ARGS__)
#else
#define LOG_DEBUG(fmt, ...)
#endif
#define LOG_INFO(fmt, ...) Logger::instance().log2(LOG_INFO, fmt, ##__VA_ARGS__)
#define LOG_ERROR(fmt, ...) Logger::instance().log(LOG_ERROR, __FILE__, __FUNCTION__,__LINE__, fmt, ##__VA_ARGS__)


class Timestamp
{
public:
    Timestamp()
        : _beginTimePoint(std::chrono::high_resolution_clock::now())
    { }

    void reset()
    {
        _beginTimePoint = std::chrono::high_resolution_clock::now();
    }

    int64_t elapsed()
    {
        return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - _beginTimePoint).count();
    }

    static std::string localtime();

private:
    std::chrono::time_point<std::chrono::high_resolution_clock> _beginTimePoint;
};

3、Logger.cpp 源码

#if defined(WIN32) || defined(_WIN32) 
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#endif

#include "Logger.h"
#include <stdarg.h>
#include <iostream>
#include "Timestamp.h"

const char* Priority_To_String[] =
{
    "DEBUG",
    "CONFIG",
    "INFO",
    "WARNING",
    "ERROR"
};

Logger::Logger() 
    : _shutdown(false)
{
    _thread = std::thread(&Logger::run, this);
}

Logger& Logger::instance()
{
    static Logger s_logger;
    return s_logger;
}

Logger::~Logger()
{
    _shutdown = true;
    _cond.notify_all();

    _thread.join();
}

void Logger::setLogFile(char *pathname)
{
    _ofs.open(pathname);
    if (_ofs.fail()) 
    {
        std::cerr << "Failed to open logfile." << std::endl;
    }
}

void Logger::log(Priority priority, const char* __file, const char* __func, int __line, const char *fmt, ...)
{	
    char buf[2048] = {0};

    sprintf(buf, "[%s][%s:%s:%d] ", Priority_To_String[priority],  __file, __func, __line);
    va_list args;
    va_start(args, fmt);
    vsprintf(buf + strlen(buf), fmt, args);
    va_end(args);

    std::string entry(buf);
    std::unique_lock<std::mutex> lock(_mutex);	
    _queue.push(std::move(entry));
    lock.unlock();
    
    _cond.notify_all(); 
}

void Logger::log2(Priority priority, const char *fmt, ...)
{
	char buf[4096] = { 0 };

	sprintf(buf, "[%s] ", Priority_To_String[priority]);  
	va_list args;
	va_start(args, fmt);
	vsprintf(buf + strlen(buf), fmt, args);
	va_end(args);

	std::string entry(buf);
	std::unique_lock<std::mutex> lock(_mutex); 
	_queue.push(std::move(entry));
    lock.unlock(); 
    
	_cond.notify_all();
}

void Logger::run()
{
    std::unique_lock<std::mutex> lock(_mutex);

    while(!_shutdown) 
    {
        if(!_queue.empty())
        {
            if(_ofs.is_open() && (!_shutdown))
                _ofs << "[" << Timestamp::localtime() << "]" 					 
                     << _queue.front() << std::endl;		
            else
                std::cout << "[" << Timestamp::localtime() << "]" 
                      << _queue.front() << std::endl;		
            _queue.pop();
        }
        else
        {
            _cond.wait(lock);
        }
    }
}



std::string Timestamp::localtime()
{
    std::ostringstream stream;
    auto now = system_clock::now();
    time_t tt = system_clock::to_time_t(now);
	
#if defined(WIN32) || defined(_WIN32)
    struct tm tm;
    localtime_s(&tm, &tt);
    stream << std::put_time(&tm, "%F %T");
#elif  defined(__linux) || defined(__linux__) 
    char buffer[200] = {0};
    std::string timeString;
    std::strftime(buffer, 200, "%F %T", std::localtime(&tt));
    stream << buffer;
#endif	
    return stream.str();
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aworkholic

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值