轻量级C++11日志库spdlog

spdlog是一个只有头文件的轻量级C++11日志库,速度非常快,扩展性很强,更重要的是社区活跃,文档齐全。原来项目中使用的是log4cplus,感觉稍微有点笨重。
github地址:https://github.com/gabime/spdlog
它支持的平台包括Windows、Linux、Mac、Android。特点如下:
1.一个字,快;三个字,非常快
2.仅包括头文件
3.日志格式丰富,格式化处理使用开源的fmt库(https://github.com/fmtlib/fmt )
4.可选的异步模式
这里的同步/异步指日志信息是否直接输出/写入文件,直接写就是同步,稍后写就是异步。spdlog默认的状态就是同步了,同步也没什么好说的。这里介绍下异步的逻辑实现:
异步状态下,日志会先存入队列,然后由线程从队列中取数据,当队列满的时候会有淘汰策略。如果工作线程中抛出了异常,向队列写入下一条日志时异常会再次抛出,可以在写入队列时捕捉工作者线程的异常,淘汰策略一般两种:
①阻塞新来的的日志,直到队列有剩余空间(默认处理方式)
②把新的日志丢掉(需要设定:spdlog::set_async_mode(队列大小,spdlog::async_overflow_policy::discard_log_msg))
5.自定义格式
一般情况下,希望输出的日志信息中带有各类基本信息。但具体情况要具体分析,spdlog中提供了个性化的输出方式,可以自己指定模式进行输出。基本上就是各类“%参数”的组合。具体内容可以参考:https://github.com/gabime/spdlog/wiki/3.-Custom-formatting
6.单线程/多线程日志
spdlog中提供了单线程和多线程模式,由使用者在对象创建中自己指定。
st:单线程版本,不用加锁,效率高,但不保证线程安全
mt:多线程版本,保证多线程并发情况线程安全,但效率稍低
7.多种日志输出方式:控制台输出(stdout)——默认输出方式,日志文件,数据库或其他外部实体
①当天日志(spdlog::daliy_logger):记录当天的所有日志,但在指定时间点会把日志清空
②循环日志(spdlog::rotating_logger):日志创建成功后,如果写入的日志大小超过限制就会写入到新日志文件中去。不过同时存在的日志总数是有上限的,达到上限后按指定策略淘汰。需要特别注意的是日志更迭的规则:当日志A存满时,将日志A名称更改为B,再新建一个日志命名为A,依次类推,直到达到上限数字
③单个日志(spdlog::basic_logger):只有一个日志文件,所有日志都会在该文件中累加
④输出终端(spdlog::stdout_color):日志打印至终端,不同等级日志颜色不同
8.日志过滤——日志等级能在编译时修改也能在运行时修改
9.支持从argv参数和环境变量加载日志等级
10.回溯支持——将调试消息存储在环形缓冲区中,稍后根据需要显示。

一.spdlog的结构

spdlog可以分成三级结构,从上而下是logger registry、logger、sink,其各自功能如下:
logger registry(日志管理器):负责管理所有的logger,用户建立的所有logger都会在registry处进行登记然后统一管理。使用 <name, logger> 将日志对象和其名称对应起来,后面使用的时候可以直接通过名称获取对应的日志对象。
logger(日志记录器):是用户直接操作的对象,通过操作logger进行日志逻辑的生成
sink(日志记录槽):受logger控制,执行具体的动作(动作包括写入日志文件/输出到控制台)
简单来说,就是一个logger registry管多个logger,一个logger管多个sink。
说到底,日志库的目的就是把日志信息写到指定地方。从上面对于结构的功能描述,sink才是真正操作日志进行写操作的结构,那sink可以把日志信息写到哪里呢?主要有三个去向:
1.控制台输出(stdout)——默认输出方式
2.日志文件
3.数据库或其他外部实体
通过刷新方式可以控制日志何时写入文件中,spdlog提供了两种刷新方式:
1.程序正常退出时写入(默认)
2.程序运行中,在指定位置进行写入(实时刷新日志,便于锁定错误所在位置)
要想使用实时刷新日志,spdlog提供了两种方法:
1.logger对象->flush_on(设定等级),flush_on是一次性刷新,执行到此时按照设定等级进行日志刷新。
2.logger对象->flush_every(周期时间),flush_every是设置刷新周期,定时进行刷新。刷新的级别采取默认了。

二.一个例子

#include <iostream>
#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h" // support for basic file logging
#include "spdlog/sinks/rotating_file_sink.h" // support for rotating file logging

int main(int, char* [])
{
    try 
    {
        // Create basic file logger (not rotated)
        auto my_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic.txt");
        
        // create a file rotating logger with 5mb size max and 3 rotated files
        auto file_logger = spdlog::rotating_logger_mt("file_logger", "myfilename", 1024 * 1024 * 5, 3);
    }
    catch (const spdlog::spdlog_ex& ex)
    {
        std::cout << "Log initialization failed: " << ex.what() << std::endl;
    }
}

这个例子是最基础的用法
更多例子参考官方example
如果想循序渐进学习spdlog,从QuickStart开始

三.压测

下面是同步和异步模式下的压测数据,环境是Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
Synchronous mode

[info] **************************************************************
[info] Single thread, 1,000,000 iterations
[info] **************************************************************
[info] basic_st         Elapsed: 0.17 secs        5,777,626/sec
[info] rotating_st      Elapsed: 0.18 secs        5,475,894/sec
[info] daily_st         Elapsed: 0.20 secs        5,062,659/sec
[info] empty_logger     Elapsed: 0.07 secs       14,127,300/sec
[info] **************************************************************
[info] C-string (400 bytes). Single thread, 1,000,000 iterations
[info] **************************************************************
[info] basic_st         Elapsed: 0.41 secs        2,412,483/sec
[info] rotating_st      Elapsed: 0.72 secs        1,389,196/sec
[info] daily_st         Elapsed: 0.42 secs        2,393,298/sec
[info] null_st          Elapsed: 0.04 secs       27,446,957/sec
[info] **************************************************************
[info] 10 threads, competing over the same logger object, 1,000,000 iterations
[info] **************************************************************
[info] basic_mt         Elapsed: 0.60 secs        1,659,613/sec
[info] rotating_mt      Elapsed: 0.62 secs        1,612,493/sec
[info] daily_mt         Elapsed: 0.61 secs        1,638,305/sec
[info] null_mt          Elapsed: 0.16 secs        6,272,758/sec

Asynchronous mode

[info] -------------------------------------------------
[info] Messages     : 1,000,000
[info] Threads      : 10
[info] Queue        : 8,192 slots
[info] Queue memory : 8,192 x 272 = 2,176 KB 
[info] -------------------------------------------------
[info] 
[info] *********************************
[info] Queue Overflow Policy: block
[info] *********************************
[info] Elapsed: 1.70784 secs     585,535/sec
[info] Elapsed: 1.69805 secs     588,910/sec
[info] Elapsed: 1.7026 secs      587,337/sec
[info] 
[info] *********************************
[info] Queue Overflow Policy: overrun
[info] *********************************
[info] Elapsed: 0.372816 secs    2,682,285/sec
[info] Elapsed: 0.379758 secs    2,633,255/sec
[info] Elapsed: 0.373532 secs    2,677,147/sec

参考链接:http://t.zoukankan.com/shuqin-p-12214439.html

原文链接:https://blog.csdn.net/caoshangpa/article/details/79476025

  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
好的,下面是一个使用spdlog封装的日志系统的示例代码: ```cpp #include <spdlog/spdlog.h> #include <spdlog/sinks/basic_file_sink.h> class Logger { public: enum class Level { Trace, Debug, Info, Warn, Error, Critical, Off }; static void init(const std::string& filename, Level level = Level::Debug) { auto logger = spdlog::basic_logger_mt("logger", filename); spdlog::set_default_logger(logger); spdlog::set_level(static_cast<spdlog::level::level_enum>(level)); } static std::shared_ptr<spdlog::logger> get() { return spdlog::default_logger(); } }; #define LOG_TRACE(...) Logger::get()->trace(__VA_ARGS__) #define LOG_DEBUG(...) Logger::get()->debug(__VA_ARGS__) #define LOG_INFO(...) Logger::get()->info(__VA_ARGS__) #define LOG_WARN(...) Logger::get()->warn(__VA_ARGS__) #define LOG_ERROR(...) Logger::get()->error(__VA_ARGS__) #define LOG_CRITICAL(...) Logger::get()->critical(__VA_ARGS__) ``` 首先,我们包含了必要的头文件,包括spdlog和basic_file_sink头文件,用于向文件中记录日志。 接下来,我们定义了一个Logger类,它有一个公共的init函数,用于初始化日志系统。该函数接受一个文件名和一个日志级别参数。在该函数中,我们创建了一个基本的文件日志器,并将其设置为默认日志器。我们还设置了日志级别,该级别将转换为spdlog的level_enum类型。 接下来,我们定义了一组宏,用于记录不同级别的日志。这些宏调用spdlog默认日志器的相应函数,如trace、debug、info、warn、error和critical。这些宏可以在我们的应用程序中的任何地方使用,以记录相应级别的日志。 最后,我们使用这些宏来记录日志。例如: ```cpp int main() { Logger::init("logs/log.txt", Logger::Level::Debug); LOG_INFO("Starting application..."); LOG_DEBUG("Some debug information..."); LOG_WARN("A warning message..."); LOG_ERROR("An error occurred!"); LOG_CRITICAL("A critical error occurred!"); return 0; } ``` 这将初始化我们的日志系统,并使用LOG_INFO、LOG_DEBUG、LOG_WARN、LOG_ERROR和LOG_CRITICAL宏记录不同级别的日志。这些日志将被记录在“logs/log.txt”文件中。 希望这个封装的示例代码可以帮助您设计一个高级的spdlog日志系统。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

草上爬

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

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

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

打赏作者

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

抵扣说明:

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

余额充值