spdlog 代码分析

spdlog 源码解析

##记日志两种模式:

  1. 同步: 对于basic_file_sink, 日志每次调用fwrite写入到文件缓存,即使同步模式,也需要flush_every来定时flush,否则crash 时有丢失日志风险

  2. 异步:

  • log写日志就是把日志入一个循环queue。
  • 初始时会开启一个线程池,里面有n个线程,每个线程里做的工作就是从循环队列中取日志,写入到指定输出设备。当队列为空时,取不到数据时,会wait 10s时间,然后继续循环取数据。
  • 也需要flush_every来定时flush 数据

sink

sink 类实现多态,方便扩展.
对于继承的类,都是模板类

template<typename Mutex>
class base_sink : public sink
{
}

Mutex 可以空实现,但是这样无法保证日志的连贯有序性,打日志的场景感觉实用性不大。但是是个很棒的设计方法。可以在其他场景运用这种设计方法。

可变模板参数

创建logger的代码

  1. 外面创建logger的api,是个模板函数
auto my_logger = spdlog::basic_logger_mt<spdlog::default_factory>("file_logger1", "logs/basic-log.txt");
auto my_logger = spdlog::basic_logger_mt("file_logger1", "logs/basic-log.txt");//使用了默认的模板default_factory

创建logger的函数模板, 模板有默认值default_factory

template<typename Factory = default_factory>
inline std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false)
{
    return Factory::template create<sinks::basic_file_sink_mt>(logger_name, filename, truncate);
}

其实可以写成
return Factory::create<sinks::basic_file_sink_mt>(logger_name, filename, truncate);
写成 Factory::template 这种形式还没太明白这是什么语法

第2步调用的create方法,此方法为可变模板参数。

using default_factory = synchronous_factory;
struct synchronous_factory
{
    template<typename Sink, typename... SinkArgs>
    static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args)
    {
        auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
        auto new_logger = std::make_shared<logger>(std::move(logger_name), std::move(sink));
        details::registry::instance().initialize_logger(new_logger);
        return new_logger;
    }
};

其中步骤2中 create<sinks::basic_file_sink_mt>(logger_name, filename, truncate) 中 sinks::basic_file_sink_mt 对应词函数模板中的模板参数Sink,入参logger_name对应此函数中的入参 std::string logger_name; 入参filename, truncate 对应此函数中的SinkArgs 可变模板参数

  1. 一个例子
void print()
{
    std::cout << "empty" << std::endl;
}

//展开函数
template <class T, class ...Args>
void print(T head, Args... rest)
{
    std::cout << "parameter " << head << std::endl;
    print(rest...);
}

print(1, 2.2, 3, 4); 输出 1 2.2 3 4 empty
print(1, 2.2, 3, 4); 同上,参数T自动推导

如果把print中调用修改为print(rest…) 结果是什么样呢?这样就类型不会自动推导,所有入参 类型为int,输出2.2就会变为2

所以可变模板参数需要格外注意参数类型。

##periodic_worker

periodic_worker(const std::function<void()> &callback_fun, std::chrono::seconds interval)
   {
       active_ = (interval > std::chrono::seconds::zero());
       if (!active_)
       {
           return;
       }

       worker_thread_ = std::thread([this, callback_fun, interval]() {
           for (;;)
           {
               std::unique_lock<std::mutex> lock(this->mutex_);
               if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; }))
               {
                   return; // active_ == false, so exit this thread
               }
               callback_fun();
           }
       });
   }

   ~periodic_worker()
   {
       if (worker_thread_.joinable())
       {
           {
               std::lock_guard<std::mutex> lock(mutex_);
               active_ = false;
           }
           cv_.notify_one();
           worker_thread_.join();
       }
   }

通过条件量实现timmer. 析构时重置标记位active_,然后notify.

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值