第四部分:Spdlog日志库的核心组件分析-logger

打印 上一主题 下一主题

主题 1676|帖子 1676|积分 5028

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
Spdlog是一个快速且可扩展的C++日志库,它支持多线程和异步日志记录。在本文中,我们将分析Spdlog日志库的核心代码,探究其实现原理和代码结构。
Spdlog的基本架构

上一篇文章介绍了spdlog的五个主要组件,其中最重要是Logger、Sink和Formatter其中,Logger负责日志的记录和管理,Sink负责将日志输出到不同的目标(比如控制台、文件、网络等),Formatter负责将日志格式化为字符串。我们会在下面详细的介绍下它们。
Logger

Logger是Spdlog日志库的核心组件,它负责记录和管理日志。Logger的定义如下:
  1. class logger {
  2. public:
  3.     explicit logger(std::string logger_name, sinks_init_list sinks);
  4.     template<typename T>
  5.     void log(level::level_enum lvl, const T &msg);
  6.    
  7.                 template<typename... Args>
  8.     void log(source_loc loc, level::level_enum lvl, format_string_t<Args...> fmt, Args &&... args);
  9.    
  10.     template<typename... Args>
  11.     void log(level::level_enum lvl, format_string_t<Args...> fmt, Args &&... args);
  12.    
  13.     // ...
  14.     template<typename... Args>
  15.     void trace(format_string_t<Args...> fmt, Args &&... args);
  16.    
  17.     template<typename... Args>
  18.     void debug(format_string_t<Args...> fmt, Args &&... args);
  19.     template<typename... Args>
  20.     void info(format_string_t<Args...> fmt, Args &&... args);
  21.     // ....
  22. private:
  23.     std::string name_;
  24.     std::vector<sink_ptr> sinks_;
  25.     // ...
  26. };
复制代码
Logger主要包含一个名称和一组Sink,它还提供了log()方法用于记录日志。当调用log()方法时,Logger会将日志消息传递给每个Sink,并由Sink将日志输出到目标。Logger还提供了其他一些方法,比如设置日志级别、添加和删除Sink等。
Logger 是个日志包装器,包含了日志名称和一组Sink,它提供了输出不同级别日志的方法,通过不同Sink的组合可以输出到一个或多个不同输出路径(文件,控制台,网络等)。
日志名全局唯一

每个 logger 都有一个名称,并且是全局唯一的,通过上一篇提到的 register 组件注册到全局的 map里,代码如下, registry 的 loggers_ 字段通过名字记录了所有的 logger 实例。
  1. class registry
  2. {
  3. public:
  4.     // .....
  5.     // 注册日志
  6.           void register_logger(std::shared_ptr<logger> new_logger);
  7. private:
  8.   // ....
  9.         std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
  10. }
复制代码
registry 提供 register_logger 接口注册日志。这里值得注意点是:注册时候如果发现已经存在则会抛异常, throw_if_exists_ 会检查是否已经存在同名日志实例,存在则通过 throw_spdlog_ex 抛出异常。
  1. SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name)
  2. {
  3.     if (loggers_.find(logger_name) != loggers_.end())
  4.     {
  5.         throw_spdlog_ex("logger with name '" + logger_name + "' already exists");
  6.     }
  7. }
  8. SPDLOG_INLINE void registry::register_logger_(std::shared_ptr<logger> new_logger)
  9. {
  10.     auto logger_name = new_logger->name();
  11.     throw_if_exists_(logger_name);
  12.     loggers_[logger_name] = std::move(new_logger);
  13. }
复制代码
日志输出控制


  • 提供不同级别日志的输出接口
在 logger 类中,Spdlog 提供了不同级别日志的输出接口,包括 trace()、debug()、info()、warn()、error() 和 critical() 等。下面是 logger 类中提供的不同级别日志输出接口的代码示例:
  1. template<typename... Args>
  2. void trace(format_string_t<Args...> fmt, Args &&... args);
  3. template<typename... Args>
  4. void debug(format_string_t<Args...> fmt, Args &&... args);
  5. template<typename... Args>
  6. void info(format_string_t<Args...> fmt, Args &&... args);
  7. template<typename... Args>
  8. void warn(format_string_t<Args...> fmt, Args &&... args);
  9. template<typename... Args>
  10. void error(format_string_t<Args...> fmt, Args &&... args);
  11. template<typename... Args>
  12. void critical(format_string_t<Args...> fmt, Args &&... args);
复制代码
使用这些接口,可以根据不同的日志级别输出不同的日志信息,比如 logger.info("This is an info message."); 将输出一条信息级别为 info 的日志。

  • 日志输出级别控制
logger 提供了 set_level 接口来设置日志级别,这个级别可以是枚举类型 level 中的任何一个,比如 spdlog::set_level(spdlog::level::trace); 将设置日志级别为 trace,这样所有级别的日志都会被记录下来。如果想要只记录 info 级别及以上的日志,则可以使用 spdlog::set_level(spdlog::level::info);。
如果想要在运行时动态地设置日志级别,可以使用 set_level() 方法,例如 logger->set_level(spdlog::level::trace); 将设置当前 logger 的日志级别为 trace。
注意:如果想要关闭日志,则可以将日志级别设置为 off,例如 spdlog::set_level(spdlog::level::off);。
  1. enum class level
  2. {
  3.     trace,
  4.     debug,
  5.     info,
  6.     warn,
  7.     err,
  8.     critical,
  9.     off
  10. };
复制代码

  • 日志刷新控制
logger提供了一些控制日志刷新的方法,最重要的方法是flush()。当调用flush()方法时,Logger会将所有挂起的日志消息刷新到Sink中。Logger还提供了set_pattern()方法,用于设置日志格式化模式。这个方法可以用于修改日志的输出格式,例如添加时间戳、线程ID等信息。
  1. void flush();
复制代码

  • 日志格式设置
logger 提供了设置日志格式的方法 set_pattern 通过此方法可以设置包含的 skin的日志格式,具体的格式信息可以参考上一篇。
  1. // 日志格式设置
  2. void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
复制代码
通过格式字符串 pattern,会生成 formatter 实例,调用 skin的set_formatter 接口设置日志格式。
  1. SPDLOG_INLINE void logger::set_pattern(std::string pattern, pattern_time_type time_type)
  2. {
  3.     auto new_formatter = details::make_unique<pattern_formatter>(std::move(pattern), time_type);
  4.     set_formatter(std::move(new_formatter));
  5. }
  6. // set formatting for the sinks in this logger.
  7. // each sink will get a separate instance of the formatter object.
  8. SPDLOG_INLINE void logger::set_formatter(std::unique_ptr<formatter> f)
  9. {
  10.     for (auto it = sinks_.begin(); it != sinks_.end(); ++it)
  11.     {
  12.         if (std::next(it) == sinks_.end())
  13.         {
  14.             // last element - we can be move it.
  15.             (*it)->set_formatter(std::move(f));
  16.             break; // to prevent clang-tidy warning
  17.         }
  18.         else
  19.         {
  20.             (*it)->set_formatter(f->clone());
  21.         }
  22.     }
  23. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

金歌

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表