您的位置:首页 > 教育 > 锐评 > 网站备案要求_个人微信公众平台怎么用_国内疫情最新情况_怎么弄推广广告

网站备案要求_个人微信公众平台怎么用_国内疫情最新情况_怎么弄推广广告

2025/2/25 2:26:56 来源:https://blog.csdn.net/leedcanDD/article/details/144386792  浏览:    关键词:网站备案要求_个人微信公众平台怎么用_国内疫情最新情况_怎么弄推广广告
网站备案要求_个人微信公众平台怎么用_国内疫情最新情况_怎么弄推广广告

spdlog高性能日志系统

spdlog 是一个快速、简单、功能丰富的 C++ 日志库,专为现代 C++ 开发设计。它支持多种日志后端(如控制台、文件、syslog 等),并提供灵活的格式化和线程安全的日志输出。

1. 特点

  • 极高的性能:大量的编译时运算、使用fmt库提高格式化打印性能

  • 零成本的抽象:通过模板和内联函数,将运算放到编译时

  • 支持异步日志和同步日志

2. 问题

  1. 多线程使用日志库,跟同步和异步是否有关联?

    没有关联。多线程指的是日志使用者同时有多个,而同步和异步指的是打印日志的方式。

    在多线程情况下,如果往同一个文件中输出日志,日志库需要考虑线程安全,包括日志写入操作的线程安全和异步方式下日志消息队列的线程安全。

  2. 同一个线程处理,是不是就是同步?

    不一定。例如在协程中,IO操作是在同一个线程中处理的,但是中间发生了协程上下文切换,等epoll发出事件通知后才继续处理,所以是异步的。

3. 输出控制

3.1 多种日志级别

trace、debug、info、warn、error和critical

不同日志级别反应日志信息的不同重要程度。

最低日志级别:低于最低日志级别的日志将不会被打印。

3.2 多种输出目标

控制台、文件、通过网络发送到远程服务器等。

3.3 格式化输出

使用fmt进行格式化输出,比C++标准库和snprintf等性能高30%。

4. spdlog处理流程

在这里插入图片描述

日志时间乱序问题

如果是写入文件中,可以用命令行工具排序。如果输出到数据库,可以使用索引。

4.1 registry

使用了单例模式

SPDLOG_INLINE registry &registry::instance() {static registry s_instance;return s_instance;
}

registry中有一个thread_pool,负责异步写入日志,里面包含一个多生产者多消费者的阻塞队列。

4.2 logger

class SPDLOG_API logger {
public:void log();
protected:...virtual void sink_it_(const details::log_msg &msg);virtual void flush_();...
}

logger的sink_it_会调用所有sinks的log方法,后者会调用其自身的sink_it_方法,sink_it_会调用flush_方法。

4.3 sink

template <typename Mutex>
class basic_file_sink final : public base_sink<Mutex> {
public:explicit basic_file_sink(const filename_t &filename,bool truncate = false,const file_event_handlers &event_handlers = {});const filename_t &filename() const;void truncate();protected:void sink_it_(const details::log_msg &msg) override;void flush_() override;private:details::file_helper file_helper_;
};

主要是重写sink_it_和flush_两个方法。

5. spdlog的使用

5.1 创建logger

5.1.2 工厂方法创建

工厂方法

工厂方法是把创建对象的接口抽象出来,让子类负责创建具体的产品对象。一般当产品类的创建流程比较复杂、产品类的依赖关系比较复杂或者客户没有必要知道创建哪个具体的产品类时可以使用此设计模式。

工厂方法:

在这里插入图片描述

调用这个工厂方法来创建logger对象。

在这里插入图片描述

异步工厂的create

在这里插入图片描述

工厂方法的目的是方便对象的创建,尤其是当对象具有复杂的创建流程与依赖关系时。

#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/async.h>int main()
{spdlog::info("default setting");// 工厂方法创建loggerauto logger1 = spdlog::basic_logger_mt("sync_logger", "basic.txt");auto logger2 = spdlog::basic_logger_mt<spdlog::async_factory>("async_logger", "basic.txt");logger1->info("factory method setting");logger2->info("async factory method setting");spdlog::get("sync_logger")->error("there is an error");return 0;
}
5.1.3 手动创建

好处是方便直接为logger绑定多个sink。手动创建的流程和工厂方法中调用的create中的创建流程类似。

下面是手动创建一个sync logger的代码。

 // sync loggerauto sink1 = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();auto sink2 = std::make_shared<spdlog::sinks::basic_file_sink_mt>("manual.txt", true);auto logger3 = std::make_shared<spdlog::logger>("manual_logger", spdlog::sinks_init_list{sink1, sink2});spdlog::register_logger(logger3);logger3->info("good");

如果要手动创建一个async logger,就需要保证registry中的线程池已经被初始化,需要手动加锁检查:

auto &mutex = registry_inst.tp_mutex();std::lock_guard<std::recursive_mutex> tp_lock(mutex);auto tp = registry_inst.get_tp();if (tp == nullptr) {tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1U);registry_inst.set_tp(tp);}

之后,分别构造sink和logger即可

        auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink),std::move(tp), OverflowPolicy);registry_inst.initialize_logger(new_logger);

5.2 创建sink

使用了模板方法设计模式,将从log函数的骨架中抽象出sink_it_和flush_两个方法供子类实现。sink_it_负责把日志写到用户缓冲区,flush_负责把日志刷到内核缓冲区。

模板方法

模板方法定义了一个算法框架,并将其中容易变化的步骤抽象出来交给子类去实现。通过这种方式,模板方法允许子类在不改变算法结构的情况下重新定义算法的某些特定步骤。

该设计模式适用于当一个过程中的部分步骤容易发生变化的场景。

在这里插入图片描述

5.3 自定义格式化

参考spdlog wiki。

5.3.1 set_pattern
// 自定义输出格式spdlog::set_pattern("[%^L%$] %v");  // 全局logger3->set_pattern("[%Y/%m/%d %H:%M:%S] [%^%L%$] %v");    // logger范围sink1->set_pattern("[%Y/%m/%d %H:%M:%S] [%^%L%$] %v [OK]"); // sink范围logger3->info("test");
5.3.2 自定义pattern flags
#include "spdlog/pattern_formatter.h"
class my_formatter_flag : public spdlog::custom_flag_formatter
{
public:void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override{std::string some_txt = "custom-flag";dest.append(some_txt.data(), some_txt.data() + some_txt.size());}std::unique_ptr<custom_flag_formatter> clone() const override{return spdlog::details::make_unique<my_formatter_flag>();}
};void custom_flags_example()
{    auto formatter = std::make_unique<spdlog::pattern_formatter>();formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v");spdlog::set_formatter(std::move(formatter));
}

5.4 创建异步logger

5.4.1 使用async factory工厂
5.4.2 使用create_async

只是对前一个方法的简单封装:

template <typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&...sink_args) {return async_factory::create<Sink>(std::move(logger_name), 			std::forward<SinkArgs>(sink_args)...);
}
5.4.3 使用create_async_nb

创建非阻塞的异步日志,与前者的区别在于,其设置了日志消息的淘汰策略。

using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;template <typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&...sink_args) {return async_factory_nonblock::create<Sink>	(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
}
5.4.4 手动构造async_logger

参照:

在这里插入图片描述

这种方式过于繁杂,不推荐,即使想要自定义OverflowPolicy,也可以选择使用async_factory_impl

5.5 刷新策略

 // 刷新策略// 1. 手动flushlogger5->flush();   // 对于异步日志,只是将消息放进队列// 2. 条件flush,设置最小的触发flush的日志等级logger5->flush_on(spdlog::level::debug);  // 3. 间隔flush,会开启一个线程来每隔一段时间flush一次spdlog::flush_every(std::chrono::seconds(5));

学习参考

学习更多相关知识请参考零声 github。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com