栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

spdlog库学习(三):运行

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

spdlog库学习(三):运行

系列文章目录

提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录
  • 系列文章目录
  • example运行:
  • bench_mark运行
  • 简单代码分析
      • 普通用法
      • 编译时日志等级


example运行:

示例中的程序,可以通过教程简单的编译出来

$ git clone https://github.com/gabime/spdlog.git
$ cd spdlog && mkdir build && cd build
$ cmake .. && make -j

执行结果如下:
输出好像并没有自带代码行号,不过应该可以自行配置吧

bench_mark运行

不知为何,bench目录中的 CMakelist.txt不能直接使用。通过先安装这个库,再使用g++编译运行

$ sudo make install 
$ g++ latency.cpp -lspdlog -lpthread -o latency

以下四个文件,有两个文件因为缺少头文件编译出错

  • async_bench.cpp
  • bench.cpp
  • formatter-bench.cpp
  • latency.cpp
?─? g++ formatter-bench.cpp -lspdlog -lpthread -o formatter-bench
formatter-bench.cpp:6:10: fatal error: benchmark/benchmark.h: No such file or directory
    6 | #include "benchmark/benchmark.h"
      |          ^~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
?─ ~/learn/spdlog/bench v1.x ?3                                                                                                                                                                                                                            ?  gyl@mi 22:47:01
?─? g++ latency.cpp -lspdlog -lpthread -o latency 
latency.cpp:10:10: fatal error: benchmark/benchmark.h: No such file or directory
   10 | #include "benchmark/benchmark.h"
      |          ^~~~~~~~~~~~~~~~~~~~~~~
      
compilation terminated.

另外两个程序运行如下

  • bench

  • async_bench
    该方法用于估计自己程序中的效率。以如下参数运行该程序,结果如下

简单代码分析
#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");// 排版格式
    
    //运行时log等级
    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");
    
    // 编译时设置日志级别
    // define SPDLOG_ACTIVE_LEVEL to desired level
    SPDLOG_TRACE("Some trace message with param {}", 42);
    SPDLOG_DEBUG("Some debug message");
}
普通用法

不考虑日志等级,代码中展示了两种输出日志的方式:仅字符串输出和格式化参数输出,对应代码如下:

知识点:

  • 除了普通的格式化字符串,还提供了宽字符的格式化字符串wformat_string。
  • 使用可变参数:fmt是一个用于格式化的对象。该类还是一个模板,高级语法,需要仔细研究下实现
  • 使用完美转发:将变参传递给logger
    default_logger_raw()是实现在spdlog-inl.h。所以spdlog库的组织是 xxx.h是头文件,xxx.-inl.h是实现文件。

    default_logger_raw()返回了一个裸指针。没有使用智能指针,恐怕是为了效率,而且仅供内部使用。

registry是实现在details命名空间的类,而且是一个单例,使用时直接调用静态函数instance()。
定义在spdlog/details/registry.h
实现在spdlog/details/registry-inl.h
插一句题外话,c++11 要求类内静态函数要线程安全,所以c++11以后实现单例模式,这就是标准做法。C++函数内的静态变量初始化以及线程安全问题

get_default_raw()返回一个registry对象内部保存的logger对象。

而且上面有描述:返回默认logger的裸指针,给默认的api使用,效率更高,但是不能和set_default_logger()一起使用。也就是说,不要在不同的线程分别调用set_defautl_logger()和std::info()等默认的日志输出api

初探几个类:
registry
logger是一个绝对的核心类,但是其非常大,随后再研究。从上面看registry应该是用于管理logger的一个类了。

class SPDLOG_API registry
{
public:
    using log_levels = std::unordered_map;
    registry(const registry &) = delete;
    registry &operator=(const registry &) = delete;  // 不可拷贝与赋值

    void register_logger(std::shared_ptr new_logger);  // 注册logger
    void initialize_logger(std::shared_ptr new_logger); // 初始化logger
    std::shared_ptr get(const std::string &logger_name);  //根据名字获取一个logger
    std::shared_ptr default_logger();   // 获取默认logger

    // Return raw ptr to the default logger.
    // To be used directly by the spdlog default api (e.g. spdlog::info)
    // This make the default API faster, but cannot be used concurrently with set_default_logger().
    // e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
    logger *get_default_raw();  //获取默认logger的裸指针

    // set default logger.
    // default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
    void set_default_logger(std::shared_ptr new_default_logger);  // 设置默认logger

    void set_tp(std::shared_ptr tp);  // 设置 线程池

    std::shared_ptr get_tp(); //获取线程池

    // Set global formatter. Each sink in each logger will get a clone of this object
    void set_formatter(std::unique_ptr formatter); //设置格式化输出

    void enable_backtrace(size_t n_messages);  // 开启trace记录

    void disable_backtrace();  // 关闭trace记录

    void set_level(level::level_enum log_level);  // 设置日志等级

    void flush_on(level::level_enum log_level); // 设置刷新日志等级

    void flush_every(std::chrono::seconds interval); // 设置自动刷新周期

    void set_error_handler(err_handler handler);  // 设置错误处理函数

    void apply_all(const std::function)> &fun); // ??

    void flush_all(); //立即刷新所有日志

    void drop(const std::string &logger_name);  //删除某个日志
 
    void drop_all(); // 删除所有日志

    // clean all resources and threads started by the registry
    void shutdown();  // 删除所有这个类的资源和线程

    std::recursive_mutex &tp_mutex(); // 获取递归锁

    void set_automatic_registration(bool automatic_registration); //??

    // set levels for all existing/future loggers. global_level can be null if should not set.
    void set_levels(log_levels levels, level::level_enum *global_level); // 为所有的 日志输出日志等级

    static registry &instance();  // 单例

private:
    registry();
    ~registry();

    void throw_if_exists_(const std::string &logger_name);
    void register_logger_(std::shared_ptr new_logger);
    bool set_level_from_cfg_(logger *logger); 
    std::mutex logger_map_mutex_, flusher_mutex_;  # 两把锁
    std::recursive_mutex tp_mutex_;  # 递归锁
    std::unordered_map> loggers_;  # 存储所有logger
    log_levels log_levels_;                         // 日志等级
    std::unique_ptr formatter_;  // 格式化?
    spdlog::level::level_enum global_log_level_ = level::info;  // 默认全局日志等级info
    level::level_enum flush_level_ = level::off; // 刷新等级 off
    err_handler err_handler_;  // 错误处理?
    std::shared_ptr tp_;  // 专门用来输出日志的线程池?
    std::unique_ptr periodic_flusher_; // 定时刷新
    std::shared_ptr default_logger_;  // 默认的logger
    bool automatic_registration_ = true; // 自动注册?
    size_t backtrace_n_messages_ = 0;  // backtrace的循环缓冲存储消息数量
};
编译时日志等级

由宏实现,从这里大致也可以猜到是用宏的大小比较,决定在编译时将宏映射为输出还是不输出。从这里也可以看到日志库的日志等级安排。
SPDLOG_ACTIVE_LEVEL
设置日志等级:#define define SPDLOG_ACTIVE_LEVEL
其内部实现,还是用了 default_logger_raw()
根据实现,所有>=该设置的日志等级,都会被输出。

日志等级由低到高分别是:

// SPDLOG_LEVEL_TRACE,
// SPDLOG_LEVEL_DEBUG,
// SPDLOG_LEVEL_INFO,
// SPDLOG_LEVEL_WARN,
// SPDLOG_LEVEL_ERROR,
// SPDLOG_LEVEL_CRITICAL,
上面6个应该是大多数日志库用的日志等级排序,程序员最好记住。起码要知道,当发现写的日志打印不出来时,怎么调高一个等级输出。
CRITICAL是最高的日志等级,INFO比DEBUG的日志等级要高。
// SPDLOG_LEVEL_OFF 是为了关闭所有日志而存在的,

以trace来看宏的嵌套调用

  • 两种宏,一种可以自定义logger,一种使用默认logger。
  • SPDLOG_LOGGER_CALL调用了logger的日志输出。
  • source_loc用于输出代码的文件名、行号、函数名等信息。
  • SPDLOG_FUNCTION 将__FUNCTION__强制转换为 const char* ,不知道为什么
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/950058.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号