[C++] 第三方日志库spdlog介绍和使用

spdlog介绍

spdlog是一个C++的日志库,它的名字来源于Speed + Log的组合,主打高性能与易用性。

spdlog的特点如下:

异步记录

使用异步日志记录,可以在保证性能的同时,避免了IO阻塞,提高系统资源利用率。

循环缓冲区

使用固定大小的循环缓冲区,可避免内存泄漏和重复分配内存以及减少磁盘I/O散乱度,进一步提高速度和效率。

多线程支持

spdlog支持多线程日志记录,线程安全且无需加锁,可有效减小潜在的性能损失。

支持多种日志输出方式

包括控制台、文件(支持自动轮转)、流式操作符等,方便实现不同场景下的日志处理。

简单易用

接口简单、文档齐全,只要几行代码就可以很容易地集成到你的项目中。

总之,spdlog是一个非常优秀的C++日志库,具有高性能、多功能、易用等优点,非常适合用于各类C++应用程序中的日志处理。

GitHub - gabime/spdlog: Fast C++ logging library.

如何在CMake项目中使用?

下载发布版本的源代码包

gabime/spdlog at v1.12.0 (github.com)

https://codeload.github.com/gabime/spdlog/zip/refs/tags/v1.12.0

解压并放到你的CMake项目下3rdparty子目录中

在CMake项目中包含spdlog的头文件目录(这里通过common项目)

创建demo的cpp文件(spdlog_demo_01.cpp),并建立到common的依赖

如何使用spdlog来写日志

基本使用

#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");
}

带颜色的 stdout/stderr日志 

#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
void main()
{
    // create a color multi-threaded logger
    auto console = spdlog::stdout_color_mt("console");    
    auto err_logger = spdlog::stderr_color_mt("stderr");    
    spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
    spdlog::get("stderr")->warn("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
}

 

基本文件日志

# include <iostream>
#include "spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"
void main()
{
    try 
    {
        auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
        logger->warn("This is a warn message");
        logger->info("This is a info message");
    }
    catch (const spdlog::spdlog_ex &ex)
    {
        std::cout << "Log init failed: " << ex.what() << std::endl;
    }
}

滚动日志文件

# include <iostream>
#include "spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"
void main()
{
    try 
    {
         // Create a file rotating logger with 5 MB size max and 3 rotated files
        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);
         
        logger->warn("This is a warn message in rotating file");
        logger->info("This is a info message in rotating file");
    }
    catch (const spdlog::spdlog_ex &ex)
    {
        std::cout << "Log init failed: " << ex.what() << std::endl;
    }
}

按天生成日志文件

# include <iostream>
#include "spdlog.h"
#include "spdlog/sinks/daily_file_sink.h"
void main()
{
    try 
    {
         // Create a daily logger - a new file is created every day at 2:30 am
        auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
         
        logger->warn("This is a warn message in daily file");
        logger->info("This is a info message in daily file");
    }
    catch (const spdlog::spdlog_ex &ex)
    {
        std::cout << "Log init failed: " << ex.what() << std::endl;
    }
}

Backtrace日志支持

# include <iostream>
#include "spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
void main()
{
    try 
    {
        auto my_logger = spdlog::stdout_color_mt("console");
        // Debug messages can be stored in a ring buffer instead of being logged immediately.
        // This is useful to display debug logs only when needed (e.g. when an error happens).
        // When needed, call dump_backtrace() to dump them to your log.

        spdlog::enable_backtrace(32); // Store the latest 32 messages in a buffer. 
        // or my_logger->enable_backtrace(32)..
        for(int i = 0; i < 100; i++)
        {
            spdlog::debug("Backtrace message {}", i); // not logged yet..
        }
        // e.g. if some error happened:
        spdlog::dump_backtrace(); // log them now! show the last 32 messages
// or my_logger->dump_backtrace(32)..
    }
    catch (const spdlog::spdlog_ex &ex)
    {
        std::cout << "Log init failed: " << ex.what() << std::endl;
    }
}

 这里只显示最后32条日志:

按周期写入(Periodic flush)

# include <iostream>
#include <chrono>
#include <thread>
#include "spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
void main()
{
    try 
    {
        auto my_logger = spdlog::stdout_color_mt("console");
        // periodically flush all *registered* loggers every 30 seconds:
        // warning: only use if all your loggers are thread-safe ("_mt" loggers)
        spdlog::flush_every(std::chrono::seconds(30));
        for(int i = 0; i < 10; i++)
        {
            my_logger->info("Periodic flush message 1: {}", i); 
        }
        std::this_thread::sleep_for(std::chrono::seconds(10));
        for(int i = 0; i < 10; i++)
        {
            my_logger->info("Periodic flush message 2: {}", i); // not logged yet..
        }
        std::this_thread::sleep_for(std::chrono::seconds(100));
    }
    catch (const spdlog::spdlog_ex &ex)
    {
        std::cout << "Log init failed: " << ex.what() << std::endl;
    }
}

从日志写的时间来看,2次写日志并没有间隔30秒,感觉按周期写入日志配置没有生效。

Stopwatch使用

#include <iostream>
#include "spdlog.h"
#include "spdlog/stopwatch.h"
void main()
{
    spdlog::stopwatch sw;    
    spdlog::info("Elapsed {}", sw);
    spdlog::info("Elapsed {:.3}", sw);
}

同时写多个格式不一样的日志

#include <iostream>
#include "spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
// create a logger with 2 targets, with different log levels and formats.
// The console will show only warnings or errors, while the file will log all.
void multi_sink_example()
{
    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    console_sink->set_level(spdlog::level::warn);
    console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");

    auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multisink.txt", true);
    file_sink->set_level(spdlog::level::trace);

    spdlog::logger logger("multi_sink", {console_sink, file_sink});
    logger.set_level(spdlog::level::debug);
    logger.warn("this should appear in both console and file");
    logger.info("this message should not appear in the console, only in the file");
}

void main()
{
    multi_sink_example();
}

关于pattern格式,可以参考 3. Custom formatting · gabime/spdlog Wiki · GitHub 

spdlog综合案例

同时把日志写入到控制台和回滚日志文件,日志保持相同的格式:

#pragma once

#include <string>
#include <vector>
#include <spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>

namespace flaw_detect{
    namespace common{
        
        /*
        日志类
        */
        class Logger{
            public:
                /*
                初始化日志,在每个可执行程序的入口类中进行初始化,然后通过spdlog::info(*)等方法来进行写日志.
                */
                static void initLogger(const std::string& appName, const spdlog::filename_t& logFilePath, size_t maxSize = 1024*1024*10, size_t maxFiles=10, std::string logPattern = "%Y-%m-%dT%H:%M:%S.%e - %n - %l - %P - %t - %s - %# - %v", spdlog::level::level_enum level = spdlog::level::info);
        };
    } 
}
#include <filesystem>
#include <sstream>
#include "common.hpp"

namespace fs = std::filesystem;

namespace flaw_detect{
    namespace common{
        void Logger::initLogger(const std::string& appName, const spdlog::filename_t& logFilePath, size_t maxSize, size_t maxFiles, std::string logPattern, spdlog::level::level_enum level){
            auto consoleSink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
            consoleSink->set_level(level);
            consoleSink->set_pattern(logPattern);

            auto fileSink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(logFilePath, maxSize, maxFiles);
            fileSink->set_level(level);
            fileSink->set_pattern(logPattern);
            
            // spdlog::sinks_init_list sinkLst = { fileSink, consoleSink };
            // spdlog::logger logger(appName, sinkLst.begin(), sinkLst.end());
            // logger.set_level(level);
            // logger.set_pattern(logPattern);
            spdlog::set_default_logger(std::make_shared<spdlog::logger>(appName, spdlog::sinks_init_list({consoleSink, fileSink})));
        };
    }
}
#include <iostream>
#include "spdlog.h"
#include "common.hpp"

void write_log_test(){
    for (int i = 0; i < 5; i++) {
        spdlog::info("spdlog_demo_01 test {}", i);
    }
}
void main()
{
    flaw_detect::common::Logger::initLogger("spdlog_demo", "logs/spdlog_demo.txt");
    write_log_test();
    
    // Under VisualStudio, this must be called before main finishes to workaround a known VS issue
    spdlog::drop_all(); 
}

遇到一个小问题:无法打印写日志的cpp文件名以及行号, 解决方法如下:

在“#include "spdlog.h"”前面添加“#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO”, 然后通过宏定义(比如“SPDLOG_INFO”)来写日志即可。

  • 10
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

老狼IT工作室

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

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

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

打赏作者

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

抵扣说明:

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

余额充值