[C++]-日志记录库SPDLog简介[通俗易懂]

[C++]-日志记录库SPDLog简介[通俗易懂]文章目录spdlog库日志记录槽sink日志记录器logger输出格式pattern对齐方式截断字符串格式化fmtFormatSpecificationspdlog使用异常处理logger基础用法stdout日志文件日志基本文件循环文件每日文件示例spdlog是一款开源的、快速的日志库。spdlog库spdlog是基于C++11实现的一款纯头文件的日志管理库(git地址:https://github.com/gabime/spdlog,API说明:https://spdlog.docsforge.c

大家好,又见面了,我是你们的朋友全栈君。

spdlog是一款开源的、快速的日志库。

spdlog库

spdlog是基于C++11实现的一款纯头文件的日志管理库(git地址:https://github.com/gabime/spdlog,API说明:https://spdlog.docsforge.com/v1.x/1.quickstart/):

  • 配置特别简单,仅包含头文件即可;
  • 写日志方式简单明了;
  • 可实现自动按日期创建日志文件/定时创建日志文件;
  • 可自定义日志格式;
  • 可以输出当前输出日志所在的文件及函数;
  • 可自定义文档大小;
  • 可将不同级别的信息输出到不同日志文件;
  • 多平台等。

spdlog中各对象都分为多线程与单线程版本:

  • *_st:单线程版本,不用加锁,效率更高。
  • *_mt:多线程版本,用于多线程程序是线程安全的。

日志记录槽sink

spdlog定义了几种sinks用于不同场景(也可自定义)下的日志输出,sink中主要包含:

  • set_pattern(const std::string&):设置日志输出的内容格式。
  • set_level(level_enum): 设置日志输出的最低等级。
  • log(log_msg):由logger自动调用,外部不会主动调用。

日志记录器logger

一个logger对象中存储有多个sink,当调用logger的日志输出函数时,logger会调用自身存储的所有sink对象的log(log_msg) 函数进行输出。logger中主要包括:

  • set_pattern(const std::string&):设置logger包含的所有sink的日志输出内容格式。
  • set_level(level_enum):设置logger日志输出最低等级,如果logger包含的sink没有设置日志等级的话,则会为其设置日志等级。
  • log(level_enum level,log_msg content):按照level等级进行输出content,logger其中日志输出最低等级小于或等于level的sink会进行执行输出操作。
  • trace(content,arg1,arg2…):按照trace等级进行输出,输出内容由content与后面的参数格式化而成。同类的函数还包括:debug/info/warn…。

输出格式pattern

通过set_pattern可设定日志格式,如set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l](%@): %v");

输出标记flag:

flag meaning example
%v 日志内容 “my log test content”
%t 线程ID “123”
%P 进程ID “234”
%n 记录器Logger名 “basicLogger”
%l 日志级别 “debug”, “info”, etc
%L 日志级别简称 “D”, “I”, etc
%a 星期几(简称) “Thu”
%A 星期几 “Thursday”
%b 月份简称 “Aug”
%B 月份 “August”
%c 日期时间 “Thu Aug 23 15:35:46 2014”
%C 年(两位) “14”
%Y “2014”
%D %x 日期简写 “08/23/14”
%m 月份(数字) “11”
%d 日(数组) “29”
%H 小时(24制) “23”
%I 小时(12制) “11”
%M 分钟 “59”
%S “58”
%e 毫秒 “678”
%f 微秒 “056789”
%F 纳秒 “256789123”
%p AM/PM “AM”
%r 时间(12制) “02:55:02 pm”
%R 时分(24制) “23:55”
%T %X 时间(24制) “23:55:59”
%z 时区(偏移) “+02:00”
%E epoch(秒) “1528834770”
%% 百分号 “%”
%+ 默认格式 “[2014-10-31 23:46:59.678] [mylogger] [info] Some message”
%^ start color range (can be used only once) “[mylogger] [info(green)] Some message”
%$ end color range (for example %^[+++]%$ %v) (can be used only once) [+++] Some message
%@ 文件名与行数 my_file.cpp:123
%s 文件名 my_file.cpp
%g 文件名(含路径) /some/dir/my_file.cpp
%# 行数 123
%! 函数名 my_func
%o 相对上一条记录的时间间隔(毫秒) 456
%i 相对上一条记录的时间间隔(微秒) 456
%u 相对上一条记录的时间间隔(纳秒) 11456
%O 相对上一条记录的时间间隔(秒) 4

日志输出中要携带文件名、行数或函数名时,必须使用SPDLOG_LOGGER_*宏,且要激活对应的级别(哪些级别以上的日志会被记录):

// 记录INFO及以上级别日志
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
#include "spdlog/spdlog.h"

SPDLOG_LOGGER_INFO(myLogger, "Support for floats {:03.2f}", 1.23456);
SPDLOG_LOGGER_WARN(myLogger, "Easy padding in numbers like {:08d}", 12);

对齐方式

每个flag都可携带对齐方式(最多支持64字符),

align meaning example result
%<width><flag> 右对齐 %8l ” info”
%-<width><flag> 左对齐 %-8l “info “
%=<width><flag> 居中 %=8l ” info “

截断

通过!可设定对应输出的最大长度:

align meaning example result
%<width>!<flag> 右对齐且截断 %3!l “inf”
%-<width>!<flag> 左对齐且截断 %-2!l “in”
%=<width>!<flag> 居中且截断 %=1!l “i”

字符串格式化fmt

spdlog中字符串格式化使用fmt(https://github.com/fmtlib/fmt)库。

格式化方式:{ [arg_id] [: (format_spec | chrono_format_spec)] }

  • arg_id:参数标识;

    • 忽略(为空时),依次对应每一个参数;
    • 索引(数字,从0开始),引用第几个索引;
    • 名称,命名参数;
  • format_spec:参数格式化方式(类型、对齐、填充等);

Format Specification

格式化符说明:

format_spec ::=  [[fill]align][sign]["#"]["0"][width]["." precision]["L"][type]
fill        ::=  <a character other than '{' or '}'>
align       ::=  "<" | ">" | "^" // 左、右、居中对齐
sign        ::=  "+" | "-" | " "
width       ::=  integer | {[arg_id]} // 宽度:数字或指定的参数
precision   ::=  integer | {[arg_id]} // 精度:数字或指定的参数
type        ::=  "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" 
                 | "g" | "G" | "o" | "p" | "s" | "x" | "X"

#不同的转换下有不同的意义:

  • 整数时,表示前面添加进制前缀,如0x, 0b等;
  • 浮点数时:总是有小数点(即使没有小数部分);

L只对数字有效,根据本地设置来输出:如,

auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890);
// s == "1,234,567,890"

格式化类型:

type meaning
s 字符串
c 字符
b/B 二进制
d 数字(十进制)
o 八进制
x/X 十六进制
a/A 十六进制浮点数(p表示指数)
e/E 科学计数
f/F 浮点数(包括NAN,INF),固定小数位数输出
g/G 浮点数输出
p 指针

示例:

fmt::format("{:*^30}", "centered");  // use '*' as a fill char
// Result: "***********centered***********"

fmt::format("{:#04x}", 0);
// Result: "0x00"

fmt::print(
  "┌{0:─^{2}}┐\n"
  "│{1: ^{2}}│\n"
  "└{0:─^{2}}┘\n", "", "Hello, world!", 20);
┌────────────────────┐
│   Hello, world!    │
└────────────────────┘

spdlog使用

spdlog默认日志输出级别是INFO。

默认情况下,日志是同步模式的,可通过以下方法开启异步模式:

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

在异步模式下,日志先存入队列(队列占用的内存 = 设置的队列大小 * slot的大小, 64位系统下slot大小为104字节。),再由工作者线程从队列中取出并输出。当队列满时,会根据设定策略处理:

  • 阻塞新来的日志,直到队列中有剩余空间(默认处理方式);

  • 丢弃新来的日志,需要如下设定策略:

    spdlog::set_async_mode(q_size, spdlog::async_overflow_policy::discard_log_msg);
    

异常处理

当输出日志时发生异常时,spdlog会向std::err 打印一条语句,为了避免输出的异常语句刷屏,打印频率被限制在每分钟一条。可通过set_error_handler来设定异常处理函数:

    //can be set globaly or per logger(logger->set_error_handler(..))
    spdlog::set_error_handler([](const std::string& msg)
    {
        std::cerr << "my err handler: " << msg << std::endl;
    });

logger

默认情况下,spdlog的默认logger为输出到stdout:

#    ifdef _WIN32
    auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
#    else
    auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
#    endif

在使用完logger后,要关闭掉以释放(否则无再建立同名logger)

spdlog::drop_all();	// 关闭所有logger
spd::drop("basic_logger"); // 关闭指定logger

基础用法

spdlog中使用{}(里面可指定格式)作为格式化符

以下方式把日志输出到默认logger上:

//#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
#include "spdlog/spdlog.h"

int main() 
{
    spdlog::info("{:<30}", "left aligned");    
    spdlog::warn("Easy padding in numbers like {:08d}", 12);
    spdlog::error("Some error message with arg: {}", 1);    
    spdlog::critical("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42);
    
    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日志

以彩色方式输出到标准输出设备上:

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

文件日志

基本文件

最简单的日志文件:

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

循环文件

日志文件超过指定大小后,自动生成一个新的;并且只保留最多指定数量的日志文件:

#include "spdlog/sinks/rotating_file_sink.h"
void rotating_example()
{
    // Create a file rotating logger with 5mb size max and 3 rotated files
    auto max_size = 1024*1024 * 5;
    auto max_files = 3;
    auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files);
}

每日文件

每天指定时间生成一个新的日志文件:

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

示例

设定默认日志记录文件并在不同地方获取使用:

#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO

#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"


void writeLog(int n) {
    for (int i = 0; i < n; ++i) {
        // 获取logger后输出日志
        auto myLogger = spdlog::get("baseLogger");
        myLogger->info("{}: Hello, {}!", i + 1, "World");
        myLogger->info("Welcome to spdlog!");
        myLogger->error("Some error message with arg: {}", 1);

        // 带文件名与行号的日志输出
        SPDLOG_LOGGER_INFO(myLogger, "Support for floats {:03.2f}", 1.23456);
        SPDLOG_LOGGER_WARN(myLogger, "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::error("Some error message with arg: {}", 1);
        spdlog::warn("Easy padding in numbers like {:08d}", 12);
        spdlog::info("Support for floats {:03.2f}", 1.23456);
    }
}

void testSPDLog() {
    // 设定日志最大100k,且最多保留10个
    auto myLogger = spdlog::rotating_logger_mt("baseLogger", "logs/basic.log", 1024 * 100, 10);
    spdlog::set_default_logger(myLogger);
    myLogger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l](%@): %v"); // 非通过宏输出的日志%@输出为空
    myLogger->set_level(spdlog::level::info);

    myLogger->info("Hello, {}!", "World");

    writeLog(10);
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/152339.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 一行代码教你伪装黑客

    一行代码教你伪装黑客在电脑上单击右键新建一个记事本右键笔记本打开编辑输入如下代码并保存starthttps://pranx.com/hacker/笔记本后缀改成.bat完成后我们双击点击,就会出现如下图的一个流动界面了,是不是很酷炫,其实也就是一个普通网页,用来给外行人装装逼,哈哈哈哈…

  • bytebuffer.putint_get的用法和例句

    bytebuffer.putint_get的用法和例句最近再看java的NIO,里面提到了几个基本的类,其中ByteBuffer是最基础的,用于Channel的读写传输数据使用。下面总结一下我理解的ByteBuffer。先从代码开始分析staticpublicvoidasIntBuffer(){ByteBufferbBuf=ByteBuffer.allocate(512);bBuf.putI

  • VIM技巧

    VIM技巧

  • 什么是MVC软件架构模式_mvc架构的设计思路

    什么是MVC软件架构模式_mvc架构的设计思路缘起:作为程序员,很容易天天被业务追逐着,抽不开时间修炼。有一天突然停了一下,忽地就会有一种怅然的感觉,过去的那些日子我学到了什么?有人很认真地说自己有10年经验,有人笑说你不过是一年经验用了10年而已。

    2022年10月10日
  • java scanner怎么用_Java中Scanner类的用法及使用步骤分享!「建议收藏」

    java scanner怎么用_Java中Scanner类的用法及使用步骤分享!「建议收藏」Scanner是Java中的一个新特征,Java程序员可以通过Scanner类来获取用户的输入,帮助程序员接收从键盘输入的数据,对于程序员而言非常有帮助。当程序需要从键盘获取用户输入的命令或数据时,例如:获取用户输入的计算表达式。可以通过Scanner类方便地获取用户输入。通过Scanner类获取用户输入时,控制台会一直等待用户的输入,直到用户敲回车键结束,把所输入的内容传给Scanner,程序要…

  • maven mvn 命令行 编译打包

    maven mvn 命令行 编译打包*一行命令#切换到项目根目录mvnclean&&mvncompile&&mvnpackage&&starttarget*配置好jdkmaven教程*下载安装mavenhttp://maven.apache.org/download.cgiapache-maven-3.3.3-bin….

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号