标签 spdlog 下的文章

“”

spdlog 下载编译

wget https://github.com/gabime/spdlog/archive/refs/tags/v1.9.2.tar.gz
tar xvf v1.9.2.tar.gz
cd spdlog-1.9.2/
cmake -DCMAKE_INSTALL_PREFIX=$(pwd)/../../install .

spdlog 简单使用

#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.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");
    
    // Set the default logger to file logger
    auto file_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic.txt");
    spdlog::set_default_logger(file_logger);            
}

spdlog 常见问题

//打印行号
// 先设置日志输出格式
// %s:文件名,my_file.cpp
// %#:行号,123
// %!:函数名,my_func
spdlog::set_pattern("%Y-%m-%d %H:%M:%S [%l] [%t] - <%s>|<%#>|<%!>,%v");

// 使用宏才会有行号
SPDLOG_DEBUG("Some debug message");
spdlog::info("Welcome to spdlog!");

志输出格式具体见:https://github.com/gabime/spdlog/wiki/3.-Custom-formatting

spdlog 推荐用法

#define DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__)
#define LOG(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__)
#define WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__)
#define ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__)

DEBUG("debug");
LOG("info");
WARN("warn");
ERROR("error");

spdlog 控制台输出

// 设置默认logger为控制台即可
// 设置默认logger,这里是控制台,所以spdlog::info的内容会输出到控制台
auto console = spdlog::stdout_color_mt("console");
spdlog::set_default_logger(console);

spdlog 控制台输出官方代码

#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
void stdout_example()
{
    // create 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 同时输出控制台和写日志文件

// spdlog 同时输出console和文件
#define DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_DEBUG(spdlog::get("daily_logger"), __VA_ARGS__)
#define LOG(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_INFO(spdlog::get("daily_logger"), __VA_ARGS__)
#define WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_WARN(spdlog::get("daily_logger"), __VA_ARGS__)
#define ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_ERROR(spdlog::get("daily_logger"), __VA_ARGS__)

auto console = spdlog::stdout_color_mt("console");
spdlog::set_default_logger(console);
// 每天2:30 am 新建一个日志文件
auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
// 遇到warn flush日志,防止丢失
logger->flush_on(spdlog::level::warn);

spdlog 文件按天分割

#include "spdlog/sinks/daily_file_sink.h"
void daily_example()
{
    // Create a daily logger - a new file is created every day on 2:30am
    auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
}

spdlog 定时flush到文件
spdlog为了提高性能,降低对磁盘的写操作,通过flush机制来一次性把日志写入到文件里面持久化。所以如果没有恰当的配置,停止调试或者进程崩溃的时候会有日志丢失的问题。

//每三秒刷新一次
spdlog::flush_every(std::chrono::seconds(3));

遇到error级别,立即flush到文件:

enum level_enum
{
    trace = SPDLOG_LEVEL_TRACE, // 最低
    debug = SPDLOG_LEVEL_DEBUG,
    info = SPDLOG_LEVEL_INFO,
    warn = SPDLOG_LEVEL_WARN,
    err = SPDLOG_LEVEL_ERROR,
    critical = SPDLOG_LEVEL_CRITICAL, // 最高
    off = SPDLOG_LEVEL_OFF,
    n_levels
};

auto logger = spdlog::daily_logger_mt("daily_logger", "log/daily.txt", 2, 30);
// 遇到warn或者更高级别,比如err,critical 立即flush日志,防止丢失
logger->flush_on(spdlog::level::warn);

spdlog 使用完整代码

#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include <iostream>
#include <memory>

// spd 带行号的打印,同时输出console和文件
#define DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_DEBUG(spdlog::get("daily_logger"), __VA_ARGS__)
#define LOG(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_INFO(spdlog::get("daily_logger"), __VA_ARGS__)
#define WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_WARN(spdlog::get("daily_logger"), __VA_ARGS__)
#define ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_ERROR(spdlog::get("daily_logger"), __VA_ARGS__)

int main(int argc, char *argv[]) {
    // 按文件大小
    //auto file_logger = spdlog::rotating_logger_mt("file_log", "log/log.log", 1024 * 1024 * 100, 3);
    // 每天2:30 am 新建一个日志文件
    auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
    // 遇到warn flush日志,防止丢失
    logger->flush_on(spdlog::level::warn);
    //每三秒刷新一次
    spdlog::flush_every(std::chrono::seconds(3));
    
    // Set the default logger to file logger
    auto console = spdlog::stdout_color_mt("console");
    spdlog::set_default_logger(console);
    spdlog::set_level(spdlog::level::debug); // Set global log level to debug

    // change log pattern
    // %s:文件名
    // %#:行号
    // %!:函数名
    spdlog::set_pattern("%Y-%m-%d %H:%M:%S [%l] [%t] - <%s>|<%#>|<%!>,%v");

    LOG("test info");
    ERROR("test error");
    
    // Release and close all loggers
    spdlog::drop_all();
}

spdlog 线程安全
对于sinks,以 _mt 后缀结尾的是线程安全的,比如:daily_file_sink_mt
以_st 后缀结尾的是非线程安全的,比如:daily_file_sink_st

spdlog 接口
spdlog 总体而言提供了日志接口

spdlog::debug(), 默认的日志对象,使用默认的日志信息格式,输出至 stdout。
logger->debug(), 指定日志对象进行日志记录,输出至该日志对象对应的文件中。
SPDLOG_LOGGER_DEBUG(logger), SPDLOG_DEBUG(), 使用宏对以上两种接口进行包装,产生的日志格式包含 文件、函数、行。

spdlog set_level 设置日志级别

低于设置级别的日志将不会被输出。各level排序,数值越大级别越高:

// Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message should not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message should be displayed..");

第一行日志debug级别低于设定的级别info,在level为info时不会被输出。
第二行日志debug级别与设定的级别相同,所以可以显示出来。

typedef enum
{
    trace = 0,
    debug = 1,
    info = 2,
    warn = 3,
    err = 4,
    critical = 5,
    off = 6
} level_enum;

spdlog 同步和异步设置
Asynchronous logging

官方说明:https://github.com/gabime/spdlog/wiki/6.-Asynchronous-logging

默认情况下是不开启异步模式的,开启异步模式方式如下:

size_t q_size = 4096; //queue size must be power of 2
spdlog::set_async_mode(q_size);

队列大小:

队列占用的内存 = 设置的队列大小 * slot的大小, 64位系统下slot大小为104字节。由此可根据系统的log输出总量来确定队列大小。

spdlog drop -- 释放logger

在程序结束时,应该调用drop_all() 释放所有logger。

There is a bug in VS runtime that cause the application dead lock when it exits. If you use async logging, please make sure to call spdlog::drop_all() before main() exit. If some loggers are not in the registry, those should be released manually as well. stackoverflow: std::thread join hangs if called after main exits when using vs2012 rc

// Release and close all loggers
spdlog::drop_all();

或者单独drop某个logger

spd::drop("console");
spd::drop("basic_logger");

spdlog dump_backtrace

// 日志的所有信息能够被储存在一个环形缓冲里(包括 调试/追踪信息),之后可以根据需要将之打印出来。
// 需要时,调用dump_backtrace()

spdlog::enable_backtrace(32); // 保存最后的32条信息,早些时候的信息会被丢掉。
spdlog::dump_backtrace(); // 这时会打印出来,最后的32条信息。

spdlog::logger::dump_backtrace() will not work inside signal handlers.

spdlog_level environment variable

./example SPDLOG_LEVEL=info,mylogger=trace 

也可以使用

export SPDLOG_LEVEL=info
./example

#include "spdlog/cfg/env.h"
void load_levels_example()
{
    // Set the log level to "info" and mylogger to to "trace":
    // SPDLOG_LEVEL=info,mylogger=trace && ./example
    spdlog::cfg::load_env_levels();
    // or from command line:
    // ./example SPDLOG_LEVEL=info,mylogger=trace
    // #include "spdlog/cfg/argv.h" // for loading levels from argv
    // spdlog::cfg::load_argv_levels(args, argv);
}