在项目中使用spdlog输出日志并且二次封装成单例异步模式

spdlog是一个轻量级的C++日志库,支持跨平台和C++11。它可以用于控制台和文件输出,支持多线程和异步日志记录,还能进行日志文件的大小限制和分割。此外,文章展示了如何创建单例模式封装spdlog并实现异步日志记录。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是spdlog

spdlog是一个高性能的C++日志库,支持跨平台,兼容C++11,是一款轻量级日志文件, header-only。可以支持多线程、异步、可以将日志输出到控制台或者文件里面

编译

$ git clone https://github.com/gabime/spdlog.git
$ cd spdlog && mkdir build && cd build
$ cmake .. && make -j
$ make install

可能出现的问题

在这里插入图片描述
有两个可能的原因:

  1. 构建spdlog源码包之后没有make install
  2. 安装一下fmt

使用

控制台输出

#include <spdlog/spdlog.h>

int main() 
{
    spdlog::info("Welcome to spdlog!");
    spdlog::error("Some error message with arg: {}", 1);
    
    spdlog::warn("Easy padding in numbers like {:08d}", 12);
    spdlog::critical("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42);
    spdlog::info("Support for floats {:03.2f}", 1.23456);
    spdlog::info("Positional args are {1} {0}..", "too", "supported");
    spdlog::info("{:<30}", "left aligned");
    
    spdlog::set_level(spdlog::level::debug); // Set global log level to debug
    spdlog::debug("This message should be displayed..");    
    
    // change log pattern
    spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
    
    // Compile time log levels
    // define SPDLOG_ACTIVE_LEVEL to desired level
    SPDLOG_TRACE("Some trace message with param {}", 42);
    SPDLOG_DEBUG("Some debug message");
}

在这里插入图片描述

文件输出

#include <iostream>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>

int main()
{
    try 
    {
        auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
        logger->critical("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42);
    }
    catch (const spdlog::spdlog_ex& ex)
    {
        std::cout << "Log initialization failed: " << ex.what() << std::endl;
    }
}

在这里插入图片描述

循环日志

日志达到一定长度之后会分割,而且不会无限增长

#include <iostream>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include<spdlog/sinks/rotating_file_sink.h>
int main()
{
    try 
    {
         auto max_size = 1048576 * 5;
         auto max_files = 3;
         auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files);
        for(int i=0;i<1000000;i++){
            logger->info("Support for floats {:03.2f}", 1.23456);
        }
    }
    catch (const spdlog::spdlog_ex& ex)
    {
        std::cout << "Log initialization failed: " << ex.what() << std::endl;
    }
}


在这里插入图片描述

异步日志

#include <iostream>
#include <spdlog/spdlog.h>
#include <spdlog/async.h>
#include <spdlog/sinks/rotating_file_sink.h>

int main(){
    spdlog::init_thread_pool(8192, 1);
    auto logger = spdlog::rotating_logger_mt<spdlog::async_factory>("file_logger", "mylogs", 1024 * 1024 * 5, 100);
    int i = 0;
    
    while (i < 1000000)
    {
        logger->info("Async message #{}", i);
        i++;
    }
    spdlog::drop_all();
    return 0;
}

在这里插入图片描述
在这里插入图片描述

封装成单例异步模式

参考spdlog简单封装 单例模式

log.h

// logger.h
#ifndef LOGGER_H
#define LOGGER_H

#include <spdlog/spdlog.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include<spdlog/async.h>

// 日志的配置项
struct LogConfig {
    std::string level;
    std::string path;
    int64_t     size;
    int         count;
};

// 日志的单例模式
class Logger {
public:
    static Logger* getInstance() {
        static Logger instance;
        return &instance;
    }

	//c++14返回值可设置为auto
    std::shared_ptr<spdlog::logger> getLogger() {
        return loggerPtr;
    }

    void Init(const LogConfig& conf);

    std::string GetLogLevel();

    void SetLogLevel(const std::string& level);


private:
    Logger() = default;
    std::shared_ptr<spdlog::logger> loggerPtr;
};

// 日志相关操作的宏封装
#define INITLOG(conf)      Logger::getInstance()->Init(conf)
#define GETLOGLEVEL()      Logger::getInstance()->GetLogLevel()
#define SETLOGLEVEL(level) Logger::getInstance()->SetLogLevel(level)
#define BASELOG(logger, level, ...) (logger)->log(spdlog::source_loc{__FILE__, __LINE__, __func__}, level, __VA_ARGS__)
#define LOG_TRACE(...)     BASELOG(Logger::getInstance()->getLogger(), spdlog::level::trace, __VA_ARGS__)
#define LOG_DEBUG(...)     BASELOG(Logger::getInstance()->getLogger(), spdlog::level::debug, __VA_ARGS__)
#define LOG_INFO(...)      BASELOG(Logger::getInstance()->getLogger(), spdlog::level::info, __VA_ARGS__)
#define LOG_WARN(...)      BASELOG(Logger::getInstance()->getLogger(), spdlog::level::warn, __VA_ARGS__)
#define LOG_ERROR(...)     BASELOG(Logger::getInstance()->getLogger(), spdlog::level::err, __VA_ARGS__)
#define LOG_CRITICAL(...)  BASELOG(Logger::getInstance()->getLogger(), spdlog::level::critical, __VA_ARGS__)

#endif // LOGGER_H



log.cpp

// logger.cpp
#include "log.h"

void Logger::Init(const LogConfig& conf) {
    //自定义的sink,日志的名字是file_logger
    loggerPtr = spdlog::rotating_logger_mt<spdlog::async_factory>("file_logger", conf.path.c_str(), conf.size, conf.count);
  
    //设置格式
    //参见文档 https://github.com/gabime/spdlog/wiki/3.-Custom-formatting
    //[%Y-%m-%d %H:%M:%S.%e] 时间
    //[%l] 日志级别
    //[%t] 线程
    //[%s] 文件
    //[%#] 行号
    //[%!] 函数
    //[%v] 实际文本
    loggerPtr->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [thread %t] [%s %!:%#] %v");

    // 设置日志级别
    loggerPtr->set_level(spdlog::level::from_str(conf.level));
    // 设置刷新日志的日志级别,当出现level或更高级别日志时,立刻刷新日志到  disk
    loggerPtr->flush_on(spdlog::level::from_str(conf.level));
}

/*
 * trace 0
 * debug 1
 * info 2
 * warn 3
 * error 4
 * critical 5
 * off 6 (not use)
 */
std::string Logger::GetLogLevel() {
    auto level = loggerPtr->level();
    return spdlog::level::to_string_view(level).data();
}

void Logger::SetLogLevel(const std::string& log_level) {
    auto level = spdlog::level::from_str(log_level);
    if (level == spdlog::level::off) {
        LOG_WARN("Given invalid log level {}", log_level);
    } else {
        loggerPtr->set_level(level);
        loggerPtr->flush_on(level);
    }
}

threadpool.h

参考markparticle/WebServer


#include <functional>
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include<assert.h>
#include <condition_variable>

class ThreadPool
{

private:
    struct Pool {
        //互斥锁
        std::mutex mtx;
        //条件变量
        std::condition_variable cond;
        //是否关闭
        bool isClosed;
        //任务队列
        std::queue<std::function<void()>> tasks;
    };
    //指向线程池的指针
    std::shared_ptr<Pool> pool_; 
public:
    explicit ThreadPool(int threadCount = std::thread::hardware_concurrency()): pool_(std::make_shared<Pool>()) {
            assert(threadCount > 0);
            for(int i = 0; i < threadCount; i++) {
                //lambada函数
                std::thread([pool = pool_] {
                    std::unique_lock<std::mutex> locker(pool->mtx);
                    while(true) {
                        if(!pool->tasks.empty()) {
                            auto task = std::move(pool->tasks.front());
                            pool->tasks.pop();
                            locker.unlock();
                            task();
                            locker.lock();
                        } 
                        else if(pool->isClosed) break;
                        else pool->cond.wait(locker);
                    }
                }).detach();
            }
    }
        ThreadPool() = default;

    ThreadPool(ThreadPool&&) = default;
    
    ~ThreadPool() {
        if(static_cast<bool>(pool_)) {
            {
                std::lock_guard<std::mutex> locker(pool_->mtx);
                pool_->isClosed = true;
            }
            pool_->cond.notify_all();
        }
    }

    template<class F>
    void AddTask(F&& task) {
        {
            std::lock_guard<std::mutex> locker(pool_->mtx);
            pool_->tasks.emplace(std::forward<F>(task));
        }
        pool_->cond.notify_one();
    }

};

test.cpp

#include "log.h"
#include "threadpool.h"
#include <features.h>

#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 30
#include <sys/syscall.h>
#define gettid() syscall(SYS_gettid)
#endif

void ThreadLogTask(int i, int cnt) {
    for(int j = 0; j < 10000000; j++ ){ 
        LOG_INFO("PID:[{:08d}]======= {:08d} ========= ", gettid(), cnt++);
    }
}

void TestThreadPool() {
    LogConfig conf2 = {
        .level = "trace",
        .path  = "../log/thread_test.log",
        .size  = 5 * 1024 * 1024,
        .count = 10,
    };
    INITLOG(conf2);
    ThreadPool threadpool(6);
    for(int i = 0; i < 18; i++) {
        threadpool.AddTask(std::bind(ThreadLogTask, i % 4, i * 10000));
    }
    getchar();
    
}

int main() {
    TestThreadPool();
}

运行结果

在这里插入图片描述
在这里插入图片描述

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值