山东网站建设运行工资,如何将自己做的网站放到网上,wordpress您的主题支持一个菜单,互联网投诉服务平台spdlog Loggers#xff1a;是 Spdlog 最基本的组件#xff0c;负责记录日志消息。在 Spdlog 中#xff0c;一个 Logger 对象代表着一个日志记录器#xff0c;应用程序可以使用 Logger 对象记录不同级别的日志消息Sinks#xff1a;决定了日志消息的输出位置。在 Spdlog 中是 Spdlog 最基本的组件负责记录日志消息。在 Spdlog 中一个 Logger 对象代表着一个日志记录器应用程序可以使用 Logger 对象记录不同级别的日志消息Sinks决定了日志消息的输出位置。在 Spdlog 中一个 Sink 对象代表着一个输出位置例如控制台、文件、网络等。应用程序可以将不同的日志消息发送到不同的 Sink 中Formatters负责将日志消息转换为特定格式。在 Spdlog 中一个 Formatter 对象代表着一个消息格式器应用程序可以使用不同的 Formatter 对象将日志消息转换为不同的格式Async Logger是 Spdlog 的异步记录器它负责将日志消息异步地写入到目标 Sink 中。当应用程序调用 Logger 对象记录一个日志消息时该消息会被加入到一个队列中然后异步地写入目标 Sink 中。这样可以避免多个线程同时访问 Sink从而确保线程安全性Registry用于管理 Spdlog 的所有组件。在 Spdlog 中所有的 Loggers、Sinks、Formatters 和 Async Logger 都在一个全局注册表中注册Registry 用于管理这些组件
spdlog 记录日志的流程
当应用程序调用spdlog 记录日志时spdlog 的调用流程如下
获取一个 Logger 对象使用该 Logger 对象记录一个日志消息该消息包括日志级别、时间戳、线程 ID、文件名和行号等信息将日志消息传递给 Formatter将消息转换为特定格式将格式化后的消息传递给 Async LoggerAsync Logger 将消息写入目标 Sink完成日志记录。
源码结构
.
├── CMakeLists.txt
├── bench
├── cmake
├── example
│ ├── CMakeLists.txt
│ └── example.cpp
├── include
│ └── spdlog
│ ├── async.h // *.h 异步模式日志库接口等实现
│ ├── async_logger-inl.h
│ ├── async_logger.h
│ ├── cfg
│ ├── common-inl.h
│ ├── common.h
│ ├── details // 功能函数目录
│ │ ├── registry-inl.h
│ │ ├── registry.h // 管理所有logger的获取、创建、销毁等
│ │ ├── mpmc_blocking_q.h // 多生产多消费者队列
│ │ ├── circular_q.h // 循环队列
│ │ ├── thread_pool-inl.h
│ │ ├── thread_pool.h // 线程池
│ │ └── synchronous_factory.h
│ ├── fmt // {fmt} 库目录
│ ├── formatter.h
│ ├── fwd.h
│ ├── logger-inl.h
│ ├── logger.h
│ ├── mdc.h
│ ├── pattern_formatter-inl.h
│ ├── pattern_formatter.h
│ ├── sinks // 落地文件格式实现
│ │ ├── android_sink.h
│ │ ├── ansicolor_sink-inl.h
│ │ ├── ansicolor_sink.h
│ │ ├── base_sink-inl.h
│ │ ├── base_sink.h
│ │ ├── basic_file_sink-inl.h
│ │ ├── basic_file_sink.h
│ │ ├── callback_sink.h
│ │ ├── daily_file_sink.h
│ │ ├── dist_sink.h
│ │ ├── dup_filter_sink.h
│ │ ├── hourly_file_sink.h
│ │ ├── kafka_sink.h
│ │ ├── mongo_sink.h
│ │ ├── msvc_sink.h
│ │ ├── null_sink.h
│ │ ├── ostream_sink.h
│ │ ├── qt_sinks.h
│ │ ├── ringbuffer_sink.h
│ │ ├── rotating_file_sink-inl.h
│ │ ├── rotating_file_sink.h
│ │ ├── sink-inl.h
│ │ ├── sink.h // 所有sink的基类
│ │ ├── stdout_color_sinks-inl.h
│ │ ├── stdout_color_sinks.h
│ │ ├── stdout_sinks-inl.h
│ │ ├── stdout_sinks.h
│ │ ├── syslog_sink.h
│ │ ├── systemd_sink.h
│ │ ├── tcp_sink.h
│ │ ├── udp_sink.h
│ │ ├── win_eventlog_sink.h
│ │ ├── wincolor_sink-inl.h
│ │ └── wincolor_sink.h
│ ├── spdlog-inl.h
│ ├── spdlog.h
│ ├── stopwatch.h
│ ├── tweakme.h
│ └── version.h
├── src
│ ├── async.cpp // .cpp 文件组成编译模块生成静态库使用
│ ├── bundled_fmtlib_format.cpp
│ ├── cfg.cpp
│ ├── color_sinks.cpp
│ ├── file_sinks.cpp
│ ├── spdlog.cpp
│ └── stdout_sinks.cpp
└── tests // 测试代码其中
spdlog/spdlog.h 为日志库接口提供日志宏的属性控制函数spdlog/logger.h 为日志管理器为前后端连接的枢纽spdlog/async.h 为异步模式接口spdlog/sinks/base_sink.h 为日志文件格式基类后面所有的日志文件格式都是继承该类来实现不同功能spdlog/sinks/registry.h 用于注册所有的logger及一些默认的属性如日志格式、日志写入等级
spdlog编译方式
spdlog的源码被分在xxx.h和xxx-inl.h文件中其中xxx.h只有函数和类的声明而实现都以inline的方式写在了xxx-inl.h中。spdlog提供了header-only version和compiled version2种方式, 通过SPDLOG_HEADER_ONLY宏定义可以调节.h文件中是否包含了其实现的代码如果包含了那就是header-only version。如果不包含那它就是compiled version中普通的头文件。如果是compiled version形式则其源码中包含xxx-inl.h实现就可以了。SPDLOG_COMPILED_LIB和SPDLOG_HEADER_ONLY宏定义:
// spdlog/common.h
#ifdef SPDLOG_COMPILED_LIB#undef SPDLOG_HEADER_ONLY#define SPDLOG_INLINE
#else // !defined(SPDLOG_COMPILED_LIB)#define SPDLOG_API#define SPDLOG_HEADER_ONLY#define SPDLOG_INLINE inline
#endif // #ifdef SPDLOG_COMPILED_LIB// async.cpp
#ifndef SPDLOG_COMPILED_LIB#error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif#include spdlog/async.h // 正常async头文件包含类和接口声明
// xxx-inl.h 具体实现
#include spdlog/async_logger-inl.h
#include spdlog/details/periodic_worker-inl.h
#include spdlog/details/thread_pool-inl.h// cfg.cpp
#ifndef SPDLOG_COMPILED_LIB#error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif
#include spdlog/cfg/helpers-inl.hspdlog 接口
spdlog::debug(), 默认的日志对象使用默认的日志信息格式输出至 stdoutlogger-debug(), 指定日志对象进行日志记录输出至该日志对象对应的文件中SPDLOG_LOGGER_DEBUG(logger), SPDLOG_DEBUG(), 使用宏对以上两种接口进行包装产生的日志格式包含 文件、函数、行 支持的文件类型 标准输出带颜色的标准输出默认基本文件可设定时间的滚动文件可设定大小的滚动文件过滤重复的日志syslog 日志
spdlog 默认使用同步模式也可以设置异步模式异步模式会创建一个线程池线程池大小可以自行设置默认为1该线程池所有者为 details::registry::instance(). 后台的大小可以设置的 多生产者多消费者队列 默认为阻塞模式也可以设置为非阻塞。
sync-logger 的调用过程
// 调用例如spdlog::info(Welcome to spdlog!);
// 或者spdlog::info(num);
template typename T void info(const T msg)
{ log(level::info, msg); } // 1. 确定log等级为info// 调用例如spdlog::info(Support for floats {:03.2f}, 1.23456);
// 或者spdlog::info(Positional args are {1} {0}.., too, supported);
template typename... Args void info(format_string_tArgs... fmt, Args ...args)
{ log(level::info, fmt, std::forwardArgs(args)...); } // 1. 确定log等级为infotemplate typename T void log(level::level_enum lvl, const T msg)
{ log(source_loc{}, lvl, msg); } // 2. 确定日志调用的位置文件、函数名、行号template typename... Args
void log(level::level_enum lvl, format_string_tArgs... fmt, Args ...args)
{ log(source_loc{}, lvl, fmt, std::forwardArgs(args)...); } // 2. 确定日志调用的位置文件、函数名、行号template typename T void log(source_loc loc, level::level_enum lvl, const T msg)
{ log(loc, lvl, {}, msg); } // spdlog::info(num);可以等价为spdlog::info({}, num);这里加了一个“{}”template typename... Args
void log(source_loc loc, level::level_enum lvl, format_string_tArgs... fmt, Args ...args)
{ log_(loc, lvl, details::to_string_view(fmt), std::forwardArgs(args)...); } // 3. log_日志输出// 4.
template typename... Args
void log_(source_loc loc, level::level_enum lvl, string_view_t fmt, Args ...args) {bool log_enabled should_log(lvl); // 是否需要记录日志bool traceback_enabled tracer_.enabled(); // 是否需要tracebackif (!log_enabled !traceback_enabled) { return; } memory_buf_t buf;// 使用第三方的fmt库做格式化例如将(num {:.2f}, 1.23456) 格式化成 num 1.23fmt::vformat_to(fmt::appender(buf), fmt, fmt::make_format_args(args...));details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));// log_it_(log_msg, log_enabled, traceback_enabled);
}// 5.
void logger::log_it_(const spdlog::details::log_msg log_msg,bool log_enabled,bool traceback_enabled) {if (log_enabled) { sink_it_(log_msg); }if (traceback_enabled) { tracer_.push_back(log_msg); }
}
// 6.
void logger::sink_it_(const details::log_msg msg) {// 遍历sinks_中的所有sink并把msg交给每个sink去处理for (auto sink : sinks_) {if (sink-should_log(msg.level)) { sink-log(msg); }}// should_flush_: 通过判断msg的等级和flush_level_的关系来决定是不是立即将msg写到文件或终端if (should_flush_(msg)) { flush_(); }
}
// 7. 让所有sink都进行一次flush操作
void logger::flush_() {for (auto sink : sinks_) { sink-flush(); // 将缓冲区的内容进一步写入到文件或者控制台等最终目的地}
}async-logger 的调用过程
async-logger的代码在asyn_logger.h和async_looger-inl.h中对应async_logger类。async_logger继承自logger前面关于接受日志内容整理log_msg对象中的工作照常做将对sink的调用包括sink-log(msg)和sink-flush()都交由线程池去执行了由此便实现了异步。代码如下
void spdlog::async_logger::sink_it_(const details::log_msg msg)
{if (auto pool_ptr thread_pool_.lock()) {pool_ptr-post_log(shared_from_this(), msg, overflow_policy_);}else {throw_spdlog_ex(async log: thread pool doesnt exist anymore);}
}
// thread_pool_ 的声明
std::weak_ptrdetails::thread_pool thread_pool_;线程池里面要有一个多生产多消费的线程安全队列用来存放日志内容。可以有多个async_logger即生产者向里面生产日志又同时又多个线程即消费者从里面消费日志。这个队列的容量应该是有限的当队列满了之后向里面生产日志可以有不同的策略spdlog提供了三种策略阻塞、丢弃新日志和丢弃旧日志。为方便实现这个需求用循环队列来实现。
循环队列
循环队列的代码在circular_q.h中实现
circular_q应设计成类模板使其能够支持各种数据类型circular_q中实际存数据的std::vectorT vec_的大小应该比circular_q能存的数据大小多一个这样才能队列是满的还是空的
多生产多消费的线程安全队列
template typename T
class mpmc_blocking_queue {...// try to enqueue and block if no room leftvoid enqueue(T item) {std::unique_lockstd::mutex lock(queue_mutex_);pop_cv_.wait(lock, [this] { return !this-q_.full(); }); // 阻塞式等待,直到q_非满状态q_.push_back(std::move(item));push_cv_.notify_one();}// blocking dequeue without a timeout.void dequeue(T popped_item) {std::unique_lockstd::mutex lock(queue_mutex_);push_cv_.wait(lock, [this] { return !this-q_.empty(); }); // 阻塞式等待popped_item std::move(q_.front());q_.pop_front();pop_cv_.notify_one();}private:std::mutex queue_mutex_;std::condition_variable push_cv_;std::condition_variable pop_cv_;spdlog::details::circular_qT q_;std::atomicsize_t discard_counter_{0};
}spdlog线程池
thread_pool使用了mpmc_blocking_queue多生产者-多消费者阻塞队列来缓存日志消息。这个队列允许多个前端线程生产者同时向队列中添加日志消息也允许多个后端线程消费者同时从队列中取出消息。前端线程是指用户调用日志记录功能的线程。当用户调用异步日志记录方法时日志消息会被封装成 async_msg 对象并放入 mpmc_blocking_queue 队列中。thread_pool 内部维护了一组后端线程这些线程从 mpmc_blocking_queue 队列中取出日志消息并进行处理。实际上是调用 async_logger::backend_sink_it_ 方法将日志消息写入到预先注册的 sink日志输出目标如文件、控制台等 #mermaid-svg-Z9DtGmIJ4MRZW4eB {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .error-icon{fill:#552222;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .marker.cross{stroke:#333333;}#mermaid-svg-Z9DtGmIJ4MRZW4eB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Z9DtGmIJ4MRZW4eB g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-Z9DtGmIJ4MRZW4eB g.classGroup text .title{font-weight:bolder;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .nodeLabel,#mermaid-svg-Z9DtGmIJ4MRZW4eB .edgeLabel{color:#131300;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .label text{fill:#131300;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .classTitle{font-weight:bolder;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .node rect,#mermaid-svg-Z9DtGmIJ4MRZW4eB .node circle,#mermaid-svg-Z9DtGmIJ4MRZW4eB .node ellipse,#mermaid-svg-Z9DtGmIJ4MRZW4eB .node polygon,#mermaid-svg-Z9DtGmIJ4MRZW4eB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-Z9DtGmIJ4MRZW4eB g.clickable{cursor:pointer;}#mermaid-svg-Z9DtGmIJ4MRZW4eB g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-Z9DtGmIJ4MRZW4eB g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .dashed-line{stroke-dasharray:3;}#mermaid-svg-Z9DtGmIJ4MRZW4eB #compositionStart,#mermaid-svg-Z9DtGmIJ4MRZW4eB .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Z9DtGmIJ4MRZW4eB #compositionEnd,#mermaid-svg-Z9DtGmIJ4MRZW4eB .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Z9DtGmIJ4MRZW4eB #dependencyStart,#mermaid-svg-Z9DtGmIJ4MRZW4eB .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Z9DtGmIJ4MRZW4eB #dependencyStart,#mermaid-svg-Z9DtGmIJ4MRZW4eB .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Z9DtGmIJ4MRZW4eB #extensionStart,#mermaid-svg-Z9DtGmIJ4MRZW4eB .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Z9DtGmIJ4MRZW4eB #extensionEnd,#mermaid-svg-Z9DtGmIJ4MRZW4eB .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Z9DtGmIJ4MRZW4eB #aggregationStart,#mermaid-svg-Z9DtGmIJ4MRZW4eB .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Z9DtGmIJ4MRZW4eB #aggregationEnd,#mermaid-svg-Z9DtGmIJ4MRZW4eB .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Z9DtGmIJ4MRZW4eB .edgeTerminals{font-size:11px;}#mermaid-svg-Z9DtGmIJ4MRZW4eB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} log_msg string_view_t logger_name level::level_enum level log_clock::time_point time size_t thread_id source_loc source; string_view_t payload; mutable size_t color_range_start mutable size_t color_range_end log_msg(log_clock::time_point log_time, source_loc loc,string_view_t logger_name,level::level_enum lvl, string_view_t msg) log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg) log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg) log_msg_buffer -memory_buf_t buffer log_msg_buffer() explicit log_msg_buffer(const log_msg orig_msg) log_msg_buffer(const log_msg_buffer other) log_msg_buffer(log_msg_buffer other) log_msg_buffer operator(const log_msg_buffer other) log_msg_buffer operator(log_msg_buffer other) -void update_string_views() async_msg async_msg_type msg_type async_logger_ptr worker_ptr std::promise flush_promise async_msg() ~async_msg() async_msg(const async_msg ) async_msg(async_logger_ptr worker, async_msg_type the_type, const details::log_msg m) async_msg(async_logger_ptr worker, async_msg_type the_type) async_msg(async_logger_ptr worker, async_msg_type the_type, std::promise promise) explicit async_msg(async_msg_type the_type) thread_pool -q_type q_ -std::vector threads_ thread_pool(size_t q_max_items, size_t threads_n, std::function on_thread_start, std::function on_thread_stop) thread_pool(size_t q_max_items, size_t threads_n, std::function on_thread_start) thread_pool(size_t q_max_items, size_t threads_n) void post_log(async_logger_ptr worker_ptr, const details::log_msg msg, async_overflow_policy overflow_policy) std::future post_flush(async_logger_ptr worker_ptr, async_overflow_policy overflow_policy) size_t overrun_counter() void reset_overrun_counter() size_t discard_counter() void reset_discard_counter() size_t queue_size() -void post_async_msg_(async_msg new_msg, async_overflow_policy overflow_policy) -void worker_loop_() -bool process_next_msg_() SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items,size_t threads_n,std::functionvoid() on_thread_start,std::functionvoid() on_thread_stop): q_(q_max_items) {if (threads_n 0 || threads_n 1000) {throw_spdlog_ex(spdlog::thread_pool(): invalid threads_n param (valid range is 1-1000));}for (size_t i 0; i threads_n; i) {threads_.emplace_back([this, on_thread_start, on_thread_stop] {on_thread_start();this-thread_pool::worker_loop_();on_thread_stop();});}
}
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items,size_t threads_n,std::functionvoid() on_thread_start): thread_pool(q_max_items, threads_n, on_thread_start, [] {}) {}
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n): thread_pool(q_max_items, threads_n, [] {}, [] {}) {}void SPDLOG_INLINE thread_pool::worker_loop_() {while (process_next_msg_()) {}
}// process next message in the queue, return true if this thread should
// still be active (while no terminate msg was received)
bool SPDLOG_INLINE thread_pool::process_next_msg_() {async_msg incoming_async_msg;q_.dequeue(incoming_async_msg);switch (incoming_async_msg.msg_type) {case async_msg_type::log: {incoming_async_msg.worker_ptr-backend_sink_it_(incoming_async_msg);return true;}case async_msg_type::flush: {incoming_async_msg.worker_ptr-backend_flush_();incoming_async_msg.flush_promise.set_value();return true;}case async_msg_type::terminate: {return false;}default: {assert(false);}}return true;
}std::futurevoid SPDLOG_INLINE thread_pool::post_flush(async_logger_ptr worker_ptr,async_overflow_policy overflow_policy) {std::promisevoid promise;std::futurevoid future promise.get_future();post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush, std::move(promise)), overflow_policy);return future;
}
void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg new_msg, async_overflow_policy overflow_policy) {if (overflow_policy async_overflow_policy::block) {q_.enqueue(std::move(new_msg));} else if (overflow_policy async_overflow_policy::overrun_oldest) {q_.enqueue_nowait(std::move(new_msg));} else {assert(overflow_policy async_overflow_policy::discard_new);q_.enqueue_if_have_room(std::move(new_msg));}
}spdlog sink
sink接收log_msg对象并通过formatter将对象中所含有的信息转换成字符串最后将字符串输出到指定的地方例如控制台、文件等甚至通过tcp/udp将字符串发送到指定的地方
sink 类图 #mermaid-svg-kQdZUmgvrm3UW9Vo {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-kQdZUmgvrm3UW9Vo .error-icon{fill:#552222;}#mermaid-svg-kQdZUmgvrm3UW9Vo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kQdZUmgvrm3UW9Vo .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-kQdZUmgvrm3UW9Vo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kQdZUmgvrm3UW9Vo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kQdZUmgvrm3UW9Vo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kQdZUmgvrm3UW9Vo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kQdZUmgvrm3UW9Vo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kQdZUmgvrm3UW9Vo .marker.cross{stroke:#333333;}#mermaid-svg-kQdZUmgvrm3UW9Vo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kQdZUmgvrm3UW9Vo g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-kQdZUmgvrm3UW9Vo g.classGroup text .title{font-weight:bolder;}#mermaid-svg-kQdZUmgvrm3UW9Vo .nodeLabel,#mermaid-svg-kQdZUmgvrm3UW9Vo .edgeLabel{color:#131300;}#mermaid-svg-kQdZUmgvrm3UW9Vo .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-kQdZUmgvrm3UW9Vo .label text{fill:#131300;}#mermaid-svg-kQdZUmgvrm3UW9Vo .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-kQdZUmgvrm3UW9Vo .classTitle{font-weight:bolder;}#mermaid-svg-kQdZUmgvrm3UW9Vo .node rect,#mermaid-svg-kQdZUmgvrm3UW9Vo .node circle,#mermaid-svg-kQdZUmgvrm3UW9Vo .node ellipse,#mermaid-svg-kQdZUmgvrm3UW9Vo .node polygon,#mermaid-svg-kQdZUmgvrm3UW9Vo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kQdZUmgvrm3UW9Vo .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-kQdZUmgvrm3UW9Vo g.clickable{cursor:pointer;}#mermaid-svg-kQdZUmgvrm3UW9Vo g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-kQdZUmgvrm3UW9Vo g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-kQdZUmgvrm3UW9Vo .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-kQdZUmgvrm3UW9Vo .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-kQdZUmgvrm3UW9Vo .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-kQdZUmgvrm3UW9Vo .dashed-line{stroke-dasharray:3;}#mermaid-svg-kQdZUmgvrm3UW9Vo #compositionStart,#mermaid-svg-kQdZUmgvrm3UW9Vo .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-kQdZUmgvrm3UW9Vo #compositionEnd,#mermaid-svg-kQdZUmgvrm3UW9Vo .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-kQdZUmgvrm3UW9Vo #dependencyStart,#mermaid-svg-kQdZUmgvrm3UW9Vo .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-kQdZUmgvrm3UW9Vo #dependencyStart,#mermaid-svg-kQdZUmgvrm3UW9Vo .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-kQdZUmgvrm3UW9Vo #extensionStart,#mermaid-svg-kQdZUmgvrm3UW9Vo .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-kQdZUmgvrm3UW9Vo #extensionEnd,#mermaid-svg-kQdZUmgvrm3UW9Vo .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-kQdZUmgvrm3UW9Vo #aggregationStart,#mermaid-svg-kQdZUmgvrm3UW9Vo .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-kQdZUmgvrm3UW9Vo #aggregationEnd,#mermaid-svg-kQdZUmgvrm3UW9Vo .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-kQdZUmgvrm3UW9Vo .edgeTerminals{font-size:11px;}#mermaid-svg-kQdZUmgvrm3UW9Vo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} sink #level_t level_ level::trace; virtual void log(const details::log_msg msg) virtual void flush() virtual void set_pattern(const std::string pattern) virtual void set_formatter(std::unique_ptr sink_formatter) void set_level(level::level_enum log_level) level::level_enum level() bool should_log(level::level_enum msg_level) base_sinkMutex #std::unique_ptr formatter_ #Mutex mutex_ void log(const details::log_msg msg) void flush() void set_pattern(const std::string pattern) void set_formatter(std::unique_ptr sink_formatter) #virtual void sink_it_(const details::log_msg msg) #virtual void flush_() #virtual void set_pattern_(const std::string pattern) #virtual void set_formatter_(std::unique_ptr sink_formatter) basic_file_sinkMutex -details::file_helper file_helper_ #void sink_it_(const details::log_msg msg) #void flush_() daily_file_sinkMutex、FileNameCalc -filename_t base_filename_; -int rotation_h_; -int rotation_m_; -log_clock::time_point rotation_tp_; -details::file_helper file_helper_; -bool truncate_; -uint16_t max_files_; -details::circular_q filenames_q_; daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate false, uint16_t max_files 0, const file_event_handlers event_handlers) filename_t filename() #void sink_it_(const details::log_msg msg) #void flush_() -void init_filenames_q_() -tm now_tm(log_clock::time_point tp) -log_clock::time_point next_rotation_tp_() -void delete_old_() hourly_file_sinkMutex、FileNameCalc -filename_t base_filename_; -log_clock::time_point rotation_tp_; -details::file_helper file_helper_; -bool truncate_; -uint16_t max_files_; -details::circular_q filenames_q_; -bool remove_init_file_; hourly_file_sink(filename_t base_filename, bool truncate false, uint16_t max_files 0, const file_event_handlers event_handlers) filename_t filename() #void sink_it_(const details::log_msg msg) #void flush_() -void init_filenames_q_() -tm now_tm(log_clock::time_point tp) -log_clock::time_point next_rotation_tp_() -void delete_old_() rotating_file_sinkMutex -filename_t base_filename_; -std::size_t max_size_; -std::size_t max_files_; -std::size_t current_size_; -details::file_helper file_helper_; rotating_file_sink(filename_t base_filename, std::size_t max_size,std::size_t max_files,bool rotate_on_open false,const file_event_handlers event_handlers) filename_t calc_filename(const filename_t filename, std::size_t index) filename_t filename() #void sink_it_(const details::log_msg msg) #void flush_() -void rotate_() -bool rename_file_(const filename_t src_filename, const filename_t target_filename) dist_sinkMutex #std::vector sinks_ explicit dist_sink(std::vector sinks) void add_sink(std::shared_ptr sub_sink) void remove_sink(std::shared_ptr sub_sink) void set_sinks(std::vector sinks) std::vector sinks() #void sink_it_(const details::log_msg msg) #void flush_() #void set_pattern_(const std::string pattern) #void set_formatter_(std::unique_ptr sink_formatter) dup_filter_sinkMutex #std::chrono::microseconds max_skip_duration_; #log_clock::time_point last_msg_time_; #std::string last_msg_payload_; #size_t skip_counter_ 0; #level::level_enum log_level_; explicit dup_filter_sink~Rep,Period~(std::chrono::duration max_skip_duration, level::level_enum notification_level level::info) #void sink_it_(const details::log_msg msg) #bool filter_(const details::log_msg msg) stdout_sink_baseConsoleMutex #mutex_t mutex_; #FILE *file_; #std::unique_ptr formatter_; explicit stdout_sink_base(FILE *file) void log(const details::log_msg msg) void flush() void set_pattern(const std::string pattern) void set_formatter(std::unique_ptr sink_formatter) stdout_sinkConsoleMutex stdout_sink() stderr_sinkConsoleMutex stderr_sink() 源码剖析
logger factory
// synchronous_factory.h
// Default logger factory- creates synchronous loggers
struct synchronous_factory {template typename Sink, typename... SinkArgsstatic std::shared_ptrspdlog::logger create(std::string logger_name, SinkArgs ...args) {auto sink std::make_sharedSink(std::forwardSinkArgs(args)...);auto new_logger std::make_sharedspdlog::logger(std::move(logger_name), std::move(sink));details::registry::instance().initialize_logger(new_logger);return new_logger;}
};// stdout_sinks factory
template typename Factory spdlog::synchronous_factory
std::shared_ptrlogger stdout_logger_mt(const std::string logger_name);template typename Factory spdlog::synchronous_factory
std::shared_ptrlogger stdout_logger_st(const std::string logger_name);template typename Factory spdlog::synchronous_factory
std::shared_ptrlogger stderr_logger_mt(const std::string logger_name);template typename Factory spdlog::synchronous_factory
std::shared_ptrlogger stderr_logger_st(const std::string logger_name);// stdout_color_sinks
// stdout_color_mt: 多线程mult thread stdout_color_st 单线程single thread
template typename Factory
SPDLOG_INLINE std::shared_ptrlogger stdout_color_mt(const std::string logger_name, color_mode mode) {return Factory::template createsinks::stdout_color_sink_mt(logger_name, mode);
}template typename Factory
SPDLOG_INLINE std::shared_ptrlogger stdout_color_st(const std::string logger_name, color_mode mode) {return Factory::template createsinks::stdout_color_sink_st(logger_name, mode);
}template typename Factory
SPDLOG_INLINE std::shared_ptrlogger stderr_color_mt(const std::string logger_name, color_mode mode) {return Factory::template createsinks::stderr_color_sink_mt(logger_name, mode);
}template typename Factory
SPDLOG_INLINE std::shared_ptrlogger stderr_color_st(const std::string logger_name, color_mode mode) {return Factory::template createsinks::stderr_color_sink_st(logger_name, mode);
}// basic_file_sink factory
template typename Factory spdlog::synchronous_factory
inline std::shared_ptrlogger basic_logger_mt(const std::string logger_name,const filename_t filename,bool truncate false,const file_event_handlers event_handlers {}) {return Factory::template createsinks::basic_file_sink_mt(logger_name, filename, truncate, event_handlers);
}template typename Factory spdlog::synchronous_factory
inline std::shared_ptrlogger basic_logger_st(const std::string logger_name,const filename_t filename,bool truncate false,const file_event_handlers event_handlers {}) {return Factory::template createsinks::basic_file_sink_st(logger_name, filename, truncate, event_handlers);
}// daily_file_sink factory
template typename Factory spdlog::synchronous_factory
inline std::shared_ptrlogger daily_logger_mt(const std::string logger_name,const filename_t filename,int hour 0,int minute 0,bool truncate false,uint16_t max_files 0,const file_event_handlers event_handlers {}) {return Factory::template createsinks::daily_file_sink_mt(logger_name, filename, hour, minute, truncate, max_files, event_handlers);
}template typename Factory spdlog::synchronous_factory
inline std::shared_ptrlogger daily_logger_format_mt(const std::string logger_name,const filename_t filename,int hour 0,int minute 0,bool truncate false,uint16_t max_files 0,const file_event_handlers event_handlers {}) {return Factory::template createsinks::daily_file_format_sink_mt(logger_name, filename, hour, minute, truncate, max_files, event_handlers);
}template typename Factory spdlog::synchronous_factory
inline std::shared_ptrlogger daily_logger_st(const std::string logger_name,const filename_t filename,int hour 0,int minute 0,bool truncate false,uint16_t max_files 0,const file_event_handlers event_handlers {}) {return Factory::template createsinks::daily_file_sink_st(logger_name, filename, hour, minute, truncate, max_files, event_handlers);
}template typename Factory spdlog::synchronous_factory
inline std::shared_ptrlogger daily_logger_format_st(const std::string logger_name,const filename_t filename,int hour 0,int minute 0,bool truncate false,uint16_t max_files 0,const file_event_handlers event_handlers {}) {return Factory::template createsinks::daily_file_format_sink_st(logger_name, filename, hour, minute, truncate, max_files, event_handlers);
}// hourly_file_sink
template typename Factory spdlog::synchronous_factory
inline std::shared_ptrlogger hourly_logger_mt(const std::string logger_name,const filename_t filename,bool truncate false,uint16_t max_files 0,const file_event_handlers event_handlers {}) {return Factory::template createsinks::hourly_file_sink_mt(logger_name, filename, truncate, max_files, event_handlers);
}template typename Factory spdlog::synchronous_factory
inline std::shared_ptrlogger hourly_logger_st(const std::string logger_name,const filename_t filename,bool truncate false,uint16_t max_files 0,const file_event_handlers event_handlers {}) {return Factory::template createsinks::hourly_file_sink_st(logger_name, filename, truncate, max_files, event_handlers);
}
// rotating_file_sink
template typename Factory spdlog::synchronous_factory
inline std::shared_ptrlogger rotating_logger_mt(const std::string logger_name,const filename_t filename,size_t max_file_size,size_t max_files,bool rotate_on_open false,const file_event_handlers event_handlers {}) {return Factory::template createsinks::rotating_file_sink_mt(logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers);
}template typename Factory spdlog::synchronous_factory
inline std::shared_ptrlogger rotating_logger_st(const std::string logger_name,const filename_t filename,size_t max_file_size,size_t max_files,bool rotate_on_open false,const file_event_handlers event_handlers {}) {return Factory::template createsinks::rotating_file_sink_st(logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers);
}以stdout_logger为例看整个logger创建过程
using stdout_sink_mt stdout_sinkdetails::console_mutex; // 有锁对应多线程版本
using stdout_sink_st stdout_sinkdetails::console_nullmutex; // 无锁对应单线程版本// 1. 调用工厂方法
template typename Factory
SPDLOG_INLINE std::shared_ptrlogger stdout_logger_mt(const std::string logger_name) {return Factory::template createsinks::stdout_sink_mt(logger_name);
}
// 2. 创建logger之后将其注册进registry并返回logger
struct synchronous_factory {template typename Sink, typename... SinkArgsstatic std::shared_ptrspdlog::logger create(std::string logger_name, SinkArgs ...args) {auto sink std::make_sharedSink(std::forwardSinkArgs(args)...);auto new_logger std::make_sharedspdlog::logger(std::move(logger_name), std::move(sink)); // 1. 构造loggerdetails::registry::instance().initialize_logger(new_logger); // 2. 调用registry的initialize_logger方法中return new_logger;}
};
// 3.
SPDLOG_INLINE void registry::initialize_logger(std::shared_ptrlogger new_logger) {std::lock_guardstd::mutex lock(logger_map_mutex_);new_logger-set_formatter(formatter_-clone());if (err_handler_) {new_logger-set_error_handler(err_handler_);}// set new level according to previously configured level or default levelauto it log_levels_.find(new_logger-name());auto new_level it ! log_levels_.end() ? it-second : global_log_level_;new_logger-set_level(new_level);new_logger-flush_on(flush_level_);if (backtrace_n_messages_ 0) {new_logger-enable_backtrace(backtrace_n_messages_);}if (automatic_registration_) {register_logger_(std::move(new_logger));}
}registry
Loggers registry of unique name-logger pointer. An attempt to create a logger with an already existing name will result with spdlog_ex exception.If user requests a non existing logger, nullptr will be returned. This class is thread safe
// std::unordered_mapstd::string, std::shared_ptrlogger loggers_;// create default logger (ansicolor_stdout_sink_mt)
registry::registry() : formatter_(new pattern_formatter()) {auto color_sink std::make_sharedsinks::ansicolor_stdout_sink_mt();const char *default_logger_name ;default_logger_ std::make_sharedspdlog::logger(default_logger_name, std::move(color_sink));loggers_[default_logger_name] default_logger_;spdlog::info(“Welcome to spdlog!”)调用分析
// spdlog/spdlog.h
template typename... Args
inline void info(format_string_tArgs... fmt, Args ...args) {default_logger_raw()-info(fmt, std::forwardArgs(args)...);
}spdlog::logger *default_logger_raw() {return details::registry::instance().get_default_raw();
}// spdlog/details/register-inl.h
registry registry::instance() {static registry s_instance;return s_instance;
}// spdlog/details/register-inl.h
logger *registry::get_default_raw() { return default_logger_.get(); // default_logger_ 由registry模块初始化
}附件
超详细spdlog源码解析上 spdlog源码解读(三)