当前位置: 首页 > 工具软件 > spdlog > 使用案例 >

spdlog入门概要

毋修为
2023-12-01

为什么选择spdlog?

  • 轻量(header only)
  • 支持异步\backtrace\rotating files\daily file …
  • 文档健全github\ wiki
  • 线程安全
  • 高可拓展

spdlog组成部分

主要由logger(记录器)和sink(接收器)两部分组成。

spdlog的高可拓展体现在logger和sink的可由用户自定义。

logger

主要关注几个要点:

  • logger类型

    • 异步
    • rotating-logger(日志切割)
  • 输出格式(fmt)

  • 日志队列的容量及线程数(并行下的日志顺序问题)

  • 日志级别(level)

  • flush时机

sink

对于sink主要关注输出位置

  • stdout / stderr
  • 文件
  • 数据库 或 其他外部实体

快速上手

spdlog内部为每个进程都维护一张全局Logger记录表,记载了用户每个进程中通过factory method创建的Logger实例(某些情况需要用户手动注册)。因而,在使用Logger时极其方便,只需要知道创建时指定的名称即可。

因此,我们可以通过工厂方法创建一个名为async_file_logger异步Logger,输出到/logs/async_log.txt

auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");

在需要用的get

auto logger = spdlog::get("async_file_logger");
logger->info("some things want to say.");

在默认情况下,spdlog为我们设置了写到标准输出的logger,
我们可以通过set_default_logger进行调整

spdlog::set_default_logger(some_other_logger);

一般情况下,我们需要Logger同时具备日志切割及异步输出的功能,在spdlog中我们可以使用factory methd —— rotating_logger_mt


更多具体的例子请参考spadlog wiki, 里面的具有丰富的sample、tutorial、api描述以及注意事项,比log4cpp要健全得多。

拥有完善的文档是对所有开发者最大的敬意。


封装Demo级别的Log

主要实现以下功能

  1. 支持异步输出日志
  2. 支持日志切割
  3. 单个线程负责写文件
  4. warn级别日志输出到stdout, 全部信息都需要写入文件
  5. 只有定义DEBUG宏,才开启写入DEBUG信息

用法

#define DEBUG

#include "log.h"

int main(void) {
    init_logger();
    // ... ...
    spdlog::info("Log Demo");
    
    return 0;
}

实现

#ifdef DEBUG
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
// #define SPDLOG_DEBUG_ON
#endif

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

/* basic */
constexpr const char* filename = "logs/log.txt";
/* rotating file config */
constexpr int file_size  = 10*1024*1024; // 10M
constexpr int back_count = 5;

/* async sink config */
constexpr int task_count = 1024 * 8;
constexpr int tp_size    = 1;


static inline void init_logger(){
    // create console_sink
    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    console_sink->set_level(spdlog::level::warn);
    
    // create rotating file sink
    auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(filename, file_size, back_count, false);
#ifdef DEBUG
    file_sink->set_level(spdlog::level::debug);
#else
    file_sink->set_level(spdlog::level::info);
#endif
    // sink's bucket
    spdlog::sinks_init_list sinks{console_sink, file_sink};

    // create async logger, and use global threadpool
    spdlog::init_thread_pool(task_count, tp_size);
    auto logger = std::make_shared<spdlog::async_logger>("aslogger", sinks, spdlog::thread_pool());

    // ajust level.
#ifdef DEBUG
    logger->set_level(spdlog::level::debug);
#else
    logger->set_level(spdlog::level::info);
#endif

    spdlog::register_logger(logger);
    spdlog::set_default_logger(logger);
}

一个坑点

若你希望看到DEBUG或是TRACE级别的信息,你需要使用以下东西:

// 或 SPDLOG_LEVEL_TRACE
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG

// 或 spdlog::set_level(spdlog::level::trace);
spdlog::set_level(spdlog::level::debug); 

经过笔者测试(TRACE也是同理,不多赘述)

  • 当仅使用spdlog::set_level(spdlog::level::debug)而不设置宏SPDLOG_ACTIVE_LEVEL时,
    只有以下方法会生效
    spdlog::debug()
    
  • 同时使用spdlog::set_level(spdlog::level::debug)并设置SPDLOG_ACTIVE_LEVEL
    时,所有相关的操作都会生效
    spdlog::debug()
    SPDLOG_DEBUG()
    SPDLOG_LOGGER_DEBUG()
    
  • 当仅设置SPDLOG_ACTIVE_LEVEL为DEBUG级别时,所有DEBUG及以下级别的输出都将被忽略

注意: 如果你使用spdlog::set_level,而且是手动构造的logger,那么最好将logger先通过spdlog::register_logger将相应的logger注册到全局表中。因为,在通常的情况下,形如spdlog::*的相关设置只会影响已经注册到全局表中的logger。相似的,spdlog::initialzie_logger被用于设置log level和 formatter(输出格式)的同时将logger注册到全局表中。

参考: spdlog::set_default_logger(logger) will cause spdlog::set_level(spdlog::level::debug) to fail

 类似资料: