summaryrefslogtreecommitdiff
path: root/lib/spdlog/details
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spdlog/details')
-rw-r--r--lib/spdlog/details/async_log_helper.h333
-rw-r--r--lib/spdlog/details/async_logger_impl.h95
-rw-r--r--lib/spdlog/details/file_helper.h151
-rw-r--r--lib/spdlog/details/log_msg.h48
-rw-r--r--lib/spdlog/details/logger_impl.h348
-rw-r--r--lib/spdlog/details/mpmc_blocking_q.h84
-rw-r--r--lib/spdlog/details/null_mutex.h45
-rw-r--r--lib/spdlog/details/os.h431
-rw-r--r--lib/spdlog/details/pattern_formatter_impl.h707
-rw-r--r--lib/spdlog/details/registry.h263
-rw-r--r--lib/spdlog/details/spdlog_impl.h278
11 files changed, 2783 insertions, 0 deletions
diff --git a/lib/spdlog/details/async_log_helper.h b/lib/spdlog/details/async_log_helper.h
new file mode 100644
index 0000000..49789b9
--- /dev/null
+++ b/lib/spdlog/details/async_log_helper.h
@@ -0,0 +1,333 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+// async log helper :
+// Process logs asynchronously using a back thread.
+//
+// If the internal queue of log messages reaches its max size,
+// then the client call will block until there is more room.
+//
+
+#pragma once
+
+#include "../common.h"
+#include "../details/log_msg.h"
+#include "../details/mpmc_blocking_q.h"
+#include "../details/os.h"
+#include "../formatter.h"
+#include "../sinks/sink.h"
+
+#include <chrono>
+#include <condition_variable>
+#include <exception>
+#include <functional>
+#include <memory>
+#include <string>
+#include <thread>
+#include <utility>
+#include <vector>
+
+namespace spdlog {
+namespace details {
+
+class async_log_helper
+{
+ // Async msg to move to/from the queue
+ // Movable only. should never be copied
+ enum class async_msg_type
+ {
+ log,
+ flush,
+ terminate
+ };
+
+ struct async_msg
+ {
+ level::level_enum level;
+ log_clock::time_point time;
+ size_t thread_id;
+ std::string txt;
+ async_msg_type msg_type;
+ size_t msg_id;
+
+ async_msg() = default;
+ ~async_msg() = default;
+
+ explicit async_msg(async_msg_type m_type)
+ : level(level::info)
+ , thread_id(0)
+ , msg_type(m_type)
+ , msg_id(0)
+ {
+ }
+
+ async_msg(async_msg &&other) = default;
+ async_msg &operator=(async_msg &&other) = default;
+
+ // never copy or assign. should only be moved..
+ async_msg(const async_msg &) = delete;
+ async_msg &operator=(const async_msg &other) = delete;
+
+ // construct from log_msg
+ explicit async_msg(const details::log_msg &m)
+ : level(m.level)
+ , time(m.time)
+ , thread_id(m.thread_id)
+ , txt(m.raw.data(), m.raw.size())
+ , msg_type(async_msg_type::log)
+ , msg_id(m.msg_id)
+ {
+ }
+
+ // copy into log_msg
+ void fill_log_msg(log_msg &msg, std::string *logger_name)
+ {
+ msg.logger_name = logger_name;
+ msg.level = level;
+ msg.time = time;
+ msg.thread_id = thread_id;
+ msg.raw.clear();
+ msg.raw << txt;
+ msg.msg_id = msg_id;
+ }
+ };
+
+public:
+ using item_type = async_msg;
+ using q_type = details::mpmc_bounded_queue<item_type>;
+
+ using clock = std::chrono::steady_clock;
+
+ async_log_helper(std::string logger_name, formatter_ptr formatter, std::vector<sink_ptr> sinks, size_t queue_size,
+ const log_err_handler err_handler, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
+ std::function<void()> worker_warmup_cb = nullptr,
+ const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(),
+ std::function<void()> worker_teardown_cb = nullptr);
+
+ void log(const details::log_msg &msg);
+
+ // stop logging and join the back thread
+ ~async_log_helper();
+
+ async_log_helper(const async_log_helper &) = delete;
+ async_log_helper &operator=(const async_log_helper &) = delete;
+
+ void set_formatter(formatter_ptr msg_formatter);
+
+ void flush();
+
+ void set_error_handler(spdlog::log_err_handler err_handler);
+
+private:
+ std::string _logger_name;
+ formatter_ptr _formatter;
+ std::vector<std::shared_ptr<sinks::sink>> _sinks;
+
+ // queue of messages to log
+ q_type _q;
+
+ log_err_handler _err_handler;
+
+ std::chrono::time_point<log_clock> _last_flush;
+
+ // overflow policy
+ const async_overflow_policy _overflow_policy;
+
+ // worker thread warmup callback - one can set thread priority, affinity, etc
+ const std::function<void()> _worker_warmup_cb;
+
+ // auto periodic sink flush parameter
+ const std::chrono::milliseconds _flush_interval_ms;
+
+ // worker thread teardown callback
+ const std::function<void()> _worker_teardown_cb;
+
+ std::mutex null_mutex_;
+ // null_mutex null_mutex_;
+ std::condition_variable_any not_empty_cv_;
+ std::condition_variable_any not_full_cv_;
+
+ // worker thread
+ std::thread _worker_thread;
+
+ void enqueue_msg(async_msg &&new_msg, async_overflow_policy policy);
+
+ // worker thread main loop
+ void worker_loop();
+
+ // dequeue next message from the queue and process it.
+ // return false if termination of the queue is required
+ bool process_next_msg();
+
+ void handle_flush_interval();
+
+ void flush_sinks();
+};
+} // namespace details
+} // namespace spdlog
+
+///////////////////////////////////////////////////////////////////////////////
+// async_sink class implementation
+///////////////////////////////////////////////////////////////////////////////
+inline spdlog::details::async_log_helper::async_log_helper(std::string logger_name, formatter_ptr formatter, std::vector<sink_ptr> sinks,
+ size_t queue_size, log_err_handler err_handler, const async_overflow_policy overflow_policy, std::function<void()> worker_warmup_cb,
+ const std::chrono::milliseconds &flush_interval_ms, std::function<void()> worker_teardown_cb)
+ : _logger_name(std::move(logger_name))
+ , _formatter(std::move(formatter))
+ , _sinks(std::move(sinks))
+ , _q(queue_size)
+ , _err_handler(std::move(err_handler))
+ , _last_flush(os::now())
+ , _overflow_policy(overflow_policy)
+ , _worker_warmup_cb(std::move(worker_warmup_cb))
+ , _flush_interval_ms(flush_interval_ms)
+ , _worker_teardown_cb(std::move(worker_teardown_cb))
+{
+ _worker_thread = std::thread(&async_log_helper::worker_loop, this);
+}
+
+// send to the worker thread terminate message, and join it.
+inline spdlog::details::async_log_helper::~async_log_helper()
+{
+ try
+ {
+ enqueue_msg(async_msg(async_msg_type::terminate), async_overflow_policy::block_retry);
+ _worker_thread.join();
+ }
+ catch (...) // don't crash in destructor
+ {
+ }
+}
+
+// try to push and block until succeeded (if the policy is not to discard when the queue is full)
+inline void spdlog::details::async_log_helper::log(const details::log_msg &msg)
+{
+ enqueue_msg(async_msg(msg), _overflow_policy);
+}
+
+inline void spdlog::details::async_log_helper::enqueue_msg(details::async_log_helper::async_msg &&new_msg, async_overflow_policy policy)
+{
+
+ // block until succeeded pushing to the queue
+ if (policy == async_overflow_policy::block_retry)
+ {
+ _q.enqueue(std::move(new_msg));
+ }
+ else
+ {
+ _q.enqueue_nowait(std::move(new_msg));
+ }
+}
+
+// optionally wait for the queue be empty and request flush from the sinks
+inline void spdlog::details::async_log_helper::flush()
+{
+ enqueue_msg(async_msg(async_msg_type::flush), _overflow_policy);
+}
+
+inline void spdlog::details::async_log_helper::worker_loop()
+{
+ if (_worker_warmup_cb)
+ {
+ _worker_warmup_cb();
+ }
+ auto active = true;
+ while (active)
+ {
+ try
+ {
+ active = process_next_msg();
+ }
+ SPDLOG_CATCH_AND_HANDLE
+ }
+ if (_worker_teardown_cb)
+ {
+ _worker_teardown_cb();
+ }
+}
+
+// process next message in the queue
+// return true if this thread should still be active (while no terminate msg was received)
+inline bool spdlog::details::async_log_helper::process_next_msg()
+{
+ async_msg incoming_async_msg;
+ bool dequeued = _q.dequeue_for(incoming_async_msg, std::chrono::seconds(2));
+ if (!dequeued)
+ {
+ handle_flush_interval();
+ return true;
+ }
+
+ switch (incoming_async_msg.msg_type)
+ {
+ case async_msg_type::flush:
+ flush_sinks();
+ return true;
+
+ case async_msg_type::terminate:
+ flush_sinks();
+ return false;
+
+ default:
+ log_msg incoming_log_msg;
+ incoming_async_msg.fill_log_msg(incoming_log_msg, &_logger_name);
+ _formatter->format(incoming_log_msg);
+ for (auto &s : _sinks)
+ {
+ if (s->should_log(incoming_log_msg.level))
+ {
+ try
+ {
+ s->log(incoming_log_msg);
+ }
+ SPDLOG_CATCH_AND_HANDLE
+ }
+ }
+ handle_flush_interval();
+ return true;
+ }
+ assert(false);
+ return true; // should not be reached
+}
+
+inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter)
+{
+ _formatter = std::move(msg_formatter);
+}
+
+inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler)
+{
+ _err_handler = std::move(err_handler);
+}
+
+// flush all sinks if _flush_interval_ms has expired.
+inline void spdlog::details::async_log_helper::handle_flush_interval()
+{
+ if (_flush_interval_ms == std::chrono::milliseconds::zero())
+ {
+ return;
+ }
+ auto delta = details::os::now() - _last_flush;
+ ;
+ if (delta >= _flush_interval_ms)
+ {
+ flush_sinks();
+ }
+}
+
+// flush all sinks if _flush_interval_ms has expired. only called if queue is empty
+inline void spdlog::details::async_log_helper::flush_sinks()
+{
+
+ for (auto &s : _sinks)
+ {
+ try
+ {
+ s->flush();
+ }
+ SPDLOG_CATCH_AND_HANDLE
+ }
+ _last_flush = os::now();
+}
diff --git a/lib/spdlog/details/async_logger_impl.h b/lib/spdlog/details/async_logger_impl.h
new file mode 100644
index 0000000..33ab4b9
--- /dev/null
+++ b/lib/spdlog/details/async_logger_impl.h
@@ -0,0 +1,95 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+// Async Logger implementation
+// Use an async_sink (queue per logger) to perform the logging in a worker thread
+
+#include "../async_logger.h"
+#include "../details/async_log_helper.h"
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <string>
+
+template<class It>
+inline spdlog::async_logger::async_logger(const std::string &logger_name, const It &begin, const It &end, size_t queue_size,
+ const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
+ const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb)
+ : logger(logger_name, begin, end)
+ , _async_log_helper(new details::async_log_helper(logger_name, _formatter, _sinks, queue_size, _err_handler, overflow_policy,
+ worker_warmup_cb, flush_interval_ms, worker_teardown_cb))
+{
+}
+
+inline spdlog::async_logger::async_logger(const std::string &logger_name, sinks_init_list sinks_list, size_t queue_size,
+ const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
+ const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb)
+ : async_logger(logger_name, sinks_list.begin(), sinks_list.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms,
+ worker_teardown_cb)
+{
+}
+
+inline spdlog::async_logger::async_logger(const std::string &logger_name, sink_ptr single_sink, size_t queue_size,
+ const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
+ const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb)
+ : async_logger(
+ logger_name, {std::move(single_sink)}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb)
+{
+}
+
+inline void spdlog::async_logger::flush()
+{
+ _async_log_helper->flush();
+}
+
+// Error handler
+inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler)
+{
+ _err_handler = err_handler;
+ _async_log_helper->set_error_handler(err_handler);
+}
+inline spdlog::log_err_handler spdlog::async_logger::error_handler()
+{
+ return _err_handler;
+}
+
+inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
+{
+ _formatter = msg_formatter;
+ _async_log_helper->set_formatter(_formatter);
+}
+
+inline void spdlog::async_logger::_set_pattern(const std::string &pattern, pattern_time_type pattern_time)
+{
+ _formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
+ _async_log_helper->set_formatter(_formatter);
+}
+
+inline void spdlog::async_logger::_sink_it(details::log_msg &msg)
+{
+ try
+ {
+#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
+ _incr_msg_counter(msg);
+#endif
+ _async_log_helper->log(msg);
+ if (_should_flush_on(msg))
+ {
+ _async_log_helper->flush(); // do async flush
+ }
+ }
+ catch (const std::exception &ex)
+ {
+ _err_handler(ex.what());
+ }
+ catch (...)
+ {
+ _err_handler("Unknown exception in logger " + _name);
+ throw;
+ }
+}
diff --git a/lib/spdlog/details/file_helper.h b/lib/spdlog/details/file_helper.h
new file mode 100644
index 0000000..d30a79b
--- /dev/null
+++ b/lib/spdlog/details/file_helper.h
@@ -0,0 +1,151 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+// Helper class for file sink
+// When failing to open a file, retry several times(5) with small delay between the tries(10 ms)
+// Throw spdlog_ex exception on errors
+
+#include "../details/log_msg.h"
+#include "../details/os.h"
+
+#include <cerrno>
+#include <chrono>
+#include <cstdio>
+#include <string>
+#include <thread>
+#include <tuple>
+
+namespace spdlog {
+namespace details {
+
+class file_helper
+{
+
+public:
+ const int open_tries = 5;
+ const int open_interval = 10;
+
+ explicit file_helper() = default;
+
+ file_helper(const file_helper &) = delete;
+ file_helper &operator=(const file_helper &) = delete;
+
+ ~file_helper()
+ {
+ close();
+ }
+
+ void open(const filename_t &fname, bool truncate = false)
+ {
+ close();
+ auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
+ _filename = fname;
+ for (int tries = 0; tries < open_tries; ++tries)
+ {
+ if (!os::fopen_s(&_fd, fname, mode))
+ {
+ return;
+ }
+
+ details::os::sleep_for_millis(open_interval);
+ }
+
+ throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno);
+ }
+
+ void reopen(bool truncate)
+ {
+ if (_filename.empty())
+ {
+ throw spdlog_ex("Failed re opening file - was not opened before");
+ }
+ open(_filename, truncate);
+ }
+
+ void flush()
+ {
+ std::fflush(_fd);
+ }
+
+ void close()
+ {
+ if (_fd != nullptr)
+ {
+ std::fclose(_fd);
+ _fd = nullptr;
+ }
+ }
+
+ void write(const log_msg &msg)
+ {
+ size_t msg_size = msg.formatted.size();
+ auto data = msg.formatted.data();
+ if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
+ {
+ throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
+ }
+ }
+
+ size_t size() const
+ {
+ if (_fd == nullptr)
+ {
+ throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
+ }
+ return os::filesize(_fd);
+ }
+
+ const filename_t &filename() const
+ {
+ return _filename;
+ }
+
+ static bool file_exists(const filename_t &fname)
+ {
+ return os::file_exists(fname);
+ }
+
+ //
+ // return file path and its extension:
+ //
+ // "mylog.txt" => ("mylog", ".txt")
+ // "mylog" => ("mylog", "")
+ // "mylog." => ("mylog.", "")
+ // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
+ //
+ // the starting dot in filenames is ignored (hidden files):
+ //
+ // ".mylog" => (".mylog". "")
+ // "my_folder/.mylog" => ("my_folder/.mylog", "")
+ // "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
+ static std::tuple<filename_t, filename_t> split_by_extenstion(const spdlog::filename_t &fname)
+ {
+ auto ext_index = fname.rfind('.');
+
+ // no valid extension found - return whole path and empty string as extension
+ if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
+ {
+ return std::make_tuple(fname, spdlog::filename_t());
+ }
+
+ // treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
+ auto folder_index = fname.rfind(details::os::folder_sep);
+ if (folder_index != fname.npos && folder_index >= ext_index - 1)
+ {
+ return std::make_tuple(fname, spdlog::filename_t());
+ }
+
+ // finally - return a valid base and extension tuple
+ return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
+ }
+
+private:
+ FILE *_fd{nullptr};
+ filename_t _filename;
+};
+} // namespace details
+} // namespace spdlog
diff --git a/lib/spdlog/details/log_msg.h b/lib/spdlog/details/log_msg.h
new file mode 100644
index 0000000..1d079aa
--- /dev/null
+++ b/lib/spdlog/details/log_msg.h
@@ -0,0 +1,48 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include "../common.h"
+#include "../details/os.h"
+
+#include <string>
+#include <utility>
+
+namespace spdlog {
+namespace details {
+struct log_msg
+{
+ log_msg() = default;
+ log_msg(const std::string *loggers_name, level::level_enum lvl)
+ : logger_name(loggers_name)
+ , level(lvl)
+ {
+#ifndef SPDLOG_NO_DATETIME
+ time = os::now();
+#endif
+
+#ifndef SPDLOG_NO_THREAD_ID
+ thread_id = os::thread_id();
+#endif
+ }
+
+ log_msg(const log_msg &other) = delete;
+ log_msg &operator=(log_msg &&other) = delete;
+ log_msg(log_msg &&other) = delete;
+
+ const std::string *logger_name{nullptr};
+ level::level_enum level;
+ log_clock::time_point time;
+ size_t thread_id;
+ fmt::MemoryWriter raw;
+ fmt::MemoryWriter formatted;
+ size_t msg_id{0};
+ // wrap this range with color codes
+ size_t color_range_start{0};
+ size_t color_range_end{0};
+};
+} // namespace details
+} // namespace spdlog
diff --git a/lib/spdlog/details/logger_impl.h b/lib/spdlog/details/logger_impl.h
new file mode 100644
index 0000000..084ecdd
--- /dev/null
+++ b/lib/spdlog/details/logger_impl.h
@@ -0,0 +1,348 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include "../logger.h"
+
+#include <memory>
+#include <string>
+
+// create logger with given name, sinks and the default pattern formatter
+// all other ctors will call this one
+template<class It>
+inline spdlog::logger::logger(std::string logger_name, const It &begin, const It &end)
+ : _name(std::move(logger_name))
+ , _sinks(begin, end)
+ , _formatter(std::make_shared<pattern_formatter>("%+"))
+ , _level(level::info)
+ , _flush_level(level::off)
+ , _last_err_time(0)
+ , _msg_counter(1) // message counter will start from 1. 0-message id will be reserved for controll messages
+{
+ _err_handler = [this](const std::string &msg) { this->_default_err_handler(msg); };
+}
+
+// ctor with sinks as init list
+inline spdlog::logger::logger(const std::string &logger_name, sinks_init_list sinks_list)
+ : logger(logger_name, sinks_list.begin(), sinks_list.end())
+{
+}
+
+// ctor with single sink
+inline spdlog::logger::logger(const std::string &logger_name, spdlog::sink_ptr single_sink)
+ : logger(logger_name, {std::move(single_sink)})
+{
+}
+
+inline spdlog::logger::~logger() = default;
+
+inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter)
+{
+ _set_formatter(std::move(msg_formatter));
+}
+
+inline void spdlog::logger::set_pattern(const std::string &pattern, pattern_time_type pattern_time)
+{
+ _set_pattern(pattern, pattern_time);
+}
+
+template<typename... Args>
+inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args)
+{
+ if (!should_log(lvl))
+ {
+ return;
+ }
+
+ try
+ {
+ details::log_msg log_msg(&_name, lvl);
+
+#if defined(SPDLOG_FMT_PRINTF)
+ fmt::printf(log_msg.raw, fmt, args...);
+#else
+ log_msg.raw.write(fmt, args...);
+#endif
+ _sink_it(log_msg);
+ }
+ SPDLOG_CATCH_AND_HANDLE
+}
+
+template<typename... Args>
+inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
+{
+ if (!should_log(lvl))
+ {
+ return;
+ }
+ try
+ {
+ details::log_msg log_msg(&_name, lvl);
+ log_msg.raw << msg;
+ _sink_it(log_msg);
+ }
+ SPDLOG_CATCH_AND_HANDLE
+}
+
+template<typename T>
+inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
+{
+ if (!should_log(lvl))
+ {
+ return;
+ }
+ try
+ {
+ details::log_msg log_msg(&_name, lvl);
+ log_msg.raw << msg;
+ _sink_it(log_msg);
+ }
+ SPDLOG_CATCH_AND_HANDLE
+}
+
+template<typename Arg1, typename... Args>
+inline void spdlog::logger::trace(const char *fmt, const Arg1 &arg1, const Args &... args)
+{
+ log(level::trace, fmt, arg1, args...);
+}
+
+template<typename Arg1, typename... Args>
+inline void spdlog::logger::debug(const char *fmt, const Arg1 &arg1, const Args &... args)
+{
+ log(level::debug, fmt, arg1, args...);
+}
+
+template<typename Arg1, typename... Args>
+inline void spdlog::logger::info(const char *fmt, const Arg1 &arg1, const Args &... args)
+{
+ log(level::info, fmt, arg1, args...);
+}
+
+template<typename Arg1, typename... Args>
+inline void spdlog::logger::warn(const char *fmt, const Arg1 &arg1, const Args &... args)
+{
+ log(level::warn, fmt, arg1, args...);
+}
+
+template<typename Arg1, typename... Args>
+inline void spdlog::logger::error(const char *fmt, const Arg1 &arg1, const Args &... args)
+{
+ log(level::err, fmt, arg1, args...);
+}
+
+template<typename Arg1, typename... Args>
+inline void spdlog::logger::critical(const char *fmt, const Arg1 &arg1, const Args &... args)
+{
+ log(level::critical, fmt, arg1, args...);
+}
+
+template<typename T>
+inline void spdlog::logger::trace(const T &msg)
+{
+ log(level::trace, msg);
+}
+
+template<typename T>
+inline void spdlog::logger::debug(const T &msg)
+{
+ log(level::debug, msg);
+}
+
+template<typename T>
+inline void spdlog::logger::info(const T &msg)
+{
+ log(level::info, msg);
+}
+
+template<typename T>
+inline void spdlog::logger::warn(const T &msg)
+{
+ log(level::warn, msg);
+}
+
+template<typename T>
+inline void spdlog::logger::error(const T &msg)
+{
+ log(level::err, msg);
+}
+
+template<typename T>
+inline void spdlog::logger::critical(const T &msg)
+{
+ log(level::critical, msg);
+}
+
+#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
+#include <codecvt>
+#include <locale>
+
+template<typename... Args>
+inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *msg)
+{
+ std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
+
+ log(lvl, conv.to_bytes(msg));
+}
+
+template<typename... Args>
+inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
+{
+ fmt::WMemoryWriter wWriter;
+
+ wWriter.write(fmt, args...);
+ log(lvl, wWriter.c_str());
+}
+
+template<typename... Args>
+inline void spdlog::logger::trace(const wchar_t *fmt, const Args &... args)
+{
+ log(level::trace, fmt, args...);
+}
+
+template<typename... Args>
+inline void spdlog::logger::debug(const wchar_t *fmt, const Args &... args)
+{
+ log(level::debug, fmt, args...);
+}
+
+template<typename... Args>
+inline void spdlog::logger::info(const wchar_t *fmt, const Args &... args)
+{
+ log(level::info, fmt, args...);
+}
+
+template<typename... Args>
+inline void spdlog::logger::warn(const wchar_t *fmt, const Args &... args)
+{
+ log(level::warn, fmt, args...);
+}
+
+template<typename... Args>
+inline void spdlog::logger::error(const wchar_t *fmt, const Args &... args)
+{
+ log(level::err, fmt, args...);
+}
+
+template<typename... Args>
+inline void spdlog::logger::critical(const wchar_t *fmt, const Args &... args)
+{
+ log(level::critical, fmt, args...);
+}
+
+#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
+
+//
+// name and level
+//
+inline const std::string &spdlog::logger::name() const
+{
+ return _name;
+}
+
+inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
+{
+ _level.store(log_level);
+}
+
+inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler)
+{
+ _err_handler = std::move(err_handler);
+}
+
+inline spdlog::log_err_handler spdlog::logger::error_handler()
+{
+ return _err_handler;
+}
+
+inline void spdlog::logger::flush_on(level::level_enum log_level)
+{
+ _flush_level.store(log_level);
+}
+
+inline spdlog::level::level_enum spdlog::logger::level() const
+{
+ return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
+}
+
+inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const
+{
+ return msg_level >= _level.load(std::memory_order_relaxed);
+}
+
+//
+// protected virtual called at end of each user log call (if enabled) by the line_logger
+//
+inline void spdlog::logger::_sink_it(details::log_msg &msg)
+{
+#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
+ _incr_msg_counter(msg);
+#endif
+ _formatter->format(msg);
+ for (auto &sink : _sinks)
+ {
+ if (sink->should_log(msg.level))
+ {
+ sink->log(msg);
+ }
+ }
+
+ if (_should_flush_on(msg))
+ {
+ flush();
+ }
+}
+
+inline void spdlog::logger::_set_pattern(const std::string &pattern, pattern_time_type pattern_time)
+{
+ _formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
+}
+
+inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter)
+{
+ _formatter = std::move(msg_formatter);
+}
+
+inline void spdlog::logger::flush()
+{
+ try
+ {
+ for (auto &sink : _sinks)
+ {
+ sink->flush();
+ }
+ }
+ SPDLOG_CATCH_AND_HANDLE
+}
+
+inline void spdlog::logger::_default_err_handler(const std::string &msg)
+{
+ auto now = time(nullptr);
+ if (now - _last_err_time < 60)
+ {
+ return;
+ }
+ _last_err_time = now;
+ auto tm_time = details::os::localtime(now);
+ char date_buf[100];
+ std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
+ fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg);
+}
+
+inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
+{
+ const auto flush_level = _flush_level.load(std::memory_order_relaxed);
+ return (msg.level >= flush_level) && (msg.level != level::off);
+}
+
+inline void spdlog::logger::_incr_msg_counter(details::log_msg &msg)
+{
+ msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
+}
+
+inline const std::vector<spdlog::sink_ptr> &spdlog::logger::sinks() const
+{
+ return _sinks;
+}
diff --git a/lib/spdlog/details/mpmc_blocking_q.h b/lib/spdlog/details/mpmc_blocking_q.h
new file mode 100644
index 0000000..d4a8a41
--- /dev/null
+++ b/lib/spdlog/details/mpmc_blocking_q.h
@@ -0,0 +1,84 @@
+#pragma once
+
+//
+// Copyright(c) 2018 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+// async log helper :
+// multi producer-multi consumer blocking queue
+// enqueue(..) - will block until room found to put the new message
+// enqueue_nowait(..) - will return immediatly with false if no room left in the queue
+// dequeue_for(..) - will block until the queue is not empty or timeout passed
+
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+
+namespace spdlog {
+namespace details {
+
+template<typename T>
+class mpmc_bounded_queue
+{
+public:
+ using item_type = T;
+ explicit mpmc_bounded_queue(size_t max_items)
+ : max_items_(max_items)
+ {
+ }
+
+ // try to enqueue and block if no room left
+ void enqueue(T &&item)
+ {
+ {
+ std::unique_lock<std::mutex> lock(queue_mutex_);
+ pop_cv_.wait(lock, [this] { return this->q_.size() < this->max_items_; });
+ q_.push(std::move(item));
+ }
+ push_cv_.notify_one();
+ }
+
+ // try to enqueue and return immdeialty false if no room left
+ bool enqueue_nowait(T &&item)
+ {
+ {
+ std::unique_lock<std::mutex> lock(queue_mutex_);
+ if (q_.size() == this->max_items_)
+ {
+ return false;
+ }
+ q_.push(std::forward<T>(item));
+ }
+ push_cv_.notify_one();
+ return true;
+ }
+
+ // try to dequeue item. if no item found. wait upto timeout and try again
+ // Return true, if succeeded dequeue item, false otherwise
+ bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
+ {
+ {
+ std::unique_lock<std::mutex> lock(queue_mutex_);
+ if (!push_cv_.wait_for(lock, wait_duration, [this] { return this->q_.size() > 0; }))
+ {
+ return false;
+ }
+
+ popped_item = std::move(q_.front());
+ q_.pop();
+ }
+ pop_cv_.notify_one();
+ return true;
+ }
+
+private:
+ size_t max_items_;
+ std::mutex queue_mutex_;
+ std::condition_variable push_cv_;
+ std::condition_variable pop_cv_;
+
+ std::queue<T> q_;
+};
+} // namespace details
+} // namespace spdlog
diff --git a/lib/spdlog/details/null_mutex.h b/lib/spdlog/details/null_mutex.h
new file mode 100644
index 0000000..3f495bd
--- /dev/null
+++ b/lib/spdlog/details/null_mutex.h
@@ -0,0 +1,45 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include <atomic>
+// null, no cost dummy "mutex" and dummy "atomic" int
+
+namespace spdlog {
+namespace details {
+struct null_mutex
+{
+ void lock() {}
+ void unlock() {}
+ bool try_lock()
+ {
+ return true;
+ }
+};
+
+struct null_atomic_int
+{
+ int value;
+ null_atomic_int() = default;
+
+ explicit null_atomic_int(int val)
+ : value(val)
+ {
+ }
+
+ int load(std::memory_order) const
+ {
+ return value;
+ }
+
+ void store(int val)
+ {
+ value = val;
+ }
+};
+
+} // namespace details
+} // namespace spdlog
diff --git a/lib/spdlog/details/os.h b/lib/spdlog/details/os.h
new file mode 100644
index 0000000..28eb53c
--- /dev/null
+++ b/lib/spdlog/details/os.h
@@ -0,0 +1,431 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+#pragma once
+
+#include "../common.h"
+
+#include <algorithm>
+#include <chrono>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <functional>
+#include <string>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <thread>
+
+#ifdef _WIN32
+
+#ifndef NOMINMAX
+#define NOMINMAX // prevent windows redefining min/max
+#endif
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <io.h> // _get_osfhandle and _isatty support
+#include <process.h> // _get_pid support
+#include <windows.h>
+
+#ifdef __MINGW32__
+#include <share.h>
+#endif
+
+#else // unix
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifdef __linux__
+#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
+
+#elif __FreeBSD__
+#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
+#endif
+
+#endif // unix
+
+#ifndef __has_feature // Clang - feature checking macros.
+#define __has_feature(x) 0 // Compatibility with non-clang compilers.
+#endif
+
+namespace spdlog {
+namespace details {
+namespace os {
+
+inline spdlog::log_clock::time_point now()
+{
+
+#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
+ timespec ts;
+ ::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
+ return std::chrono::time_point<log_clock, typename log_clock::duration>(
+ std::chrono::duration_cast<typename log_clock::duration>(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
+
+#else
+ return log_clock::now();
+#endif
+}
+inline std::tm localtime(const std::time_t &time_tt)
+{
+
+#ifdef _WIN32
+ std::tm tm;
+ localtime_s(&tm, &time_tt);
+#else
+ std::tm tm;
+ localtime_r(&time_tt, &tm);
+#endif
+ return tm;
+}
+
+inline std::tm localtime()
+{
+ std::time_t now_t = time(nullptr);
+ return localtime(now_t);
+}
+
+inline std::tm gmtime(const std::time_t &time_tt)
+{
+
+#ifdef _WIN32
+ std::tm tm;
+ gmtime_s(&tm, &time_tt);
+#else
+ std::tm tm;
+ gmtime_r(&time_tt, &tm);
+#endif
+ return tm;
+}
+
+inline std::tm gmtime()
+{
+ std::time_t now_t = time(nullptr);
+ return gmtime(now_t);
+}
+inline bool operator==(const std::tm &tm1, const std::tm &tm2)
+{
+ return (tm1.tm_sec == tm2.tm_sec && tm1.tm_min == tm2.tm_min && tm1.tm_hour == tm2.tm_hour && tm1.tm_mday == tm2.tm_mday &&
+ tm1.tm_mon == tm2.tm_mon && tm1.tm_year == tm2.tm_year && tm1.tm_isdst == tm2.tm_isdst);
+}
+
+inline bool operator!=(const std::tm &tm1, const std::tm &tm2)
+{
+ return !(tm1 == tm2);
+}
+
+// eol definition
+#if !defined(SPDLOG_EOL)
+#ifdef _WIN32
+#define SPDLOG_EOL "\r\n"
+#else
+#define SPDLOG_EOL "\n"
+#endif
+#endif
+
+SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
+
+// folder separator
+#ifdef _WIN32
+SPDLOG_CONSTEXPR static const char folder_sep = '\\';
+#else
+SPDLOG_CONSTEXPR static const char folder_sep = '/';
+#endif
+
+inline void prevent_child_fd(FILE *f)
+{
+
+#ifdef _WIN32
+#if !defined(__cplusplus_winrt)
+ auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
+ if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
+ throw spdlog_ex("SetHandleInformation failed", errno);
+#endif
+#else
+ auto fd = fileno(f);
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
+ {
+ throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
+ }
+#endif
+}
+
+// fopen_s on non windows for writing
+inline bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
+{
+#ifdef _WIN32
+#ifdef SPDLOG_WCHAR_FILENAMES
+ *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
+#else
+ *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
+#endif
+#else // unix
+ *fp = fopen((filename.c_str()), mode.c_str());
+#endif
+
+#ifdef SPDLOG_PREVENT_CHILD_FD
+ if (*fp != nullptr)
+ {
+ prevent_child_fd(*fp);
+ }
+#endif
+ return *fp == nullptr;
+}
+
+inline int remove(const filename_t &filename)
+{
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+ return _wremove(filename.c_str());
+#else
+ return std::remove(filename.c_str());
+#endif
+}
+
+inline int rename(const filename_t &filename1, const filename_t &filename2)
+{
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+ return _wrename(filename1.c_str(), filename2.c_str());
+#else
+ return std::rename(filename1.c_str(), filename2.c_str());
+#endif
+}
+
+// Return if file exists
+inline bool file_exists(const filename_t &filename)
+{
+#ifdef _WIN32
+#ifdef SPDLOG_WCHAR_FILENAMES
+ auto attribs = GetFileAttributesW(filename.c_str());
+#else
+ auto attribs = GetFileAttributesA(filename.c_str());
+#endif
+ return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
+#else // common linux/unix all have the stat system call
+ struct stat buffer;
+ return (stat(filename.c_str(), &buffer) == 0);
+#endif
+}
+
+// Return file size according to open FILE* object
+inline size_t filesize(FILE *f)
+{
+ if (f == nullptr)
+ {
+ throw spdlog_ex("Failed getting file size. fd is null");
+ }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ int fd = _fileno(f);
+#if _WIN64 // 64 bits
+ struct _stat64 st;
+ if (_fstat64(fd, &st) == 0)
+ {
+ return st.st_size;
+ }
+
+#else // windows 32 bits
+ long ret = _filelength(fd);
+ if (ret >= 0)
+ {
+ return static_cast<size_t>(ret);
+ }
+#endif
+
+#else // unix
+ int fd = fileno(f);
+ // 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
+#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__)
+ struct stat64 st;
+ if (fstat64(fd, &st) == 0)
+ {
+ return static_cast<size_t>(st.st_size);
+ }
+#else // unix 32 bits or cygwin
+ struct stat st;
+
+ if (fstat(fd, &st) == 0)
+ {
+ return static_cast<size_t>(st.st_size);
+ }
+#endif
+#endif
+ throw spdlog_ex("Failed getting file size from fd", errno);
+}
+
+// Return utc offset in minutes or throw spdlog_ex on failure
+inline int utc_minutes_offset(const std::tm &tm = details::os::localtime())
+{
+
+#ifdef _WIN32
+#if _WIN32_WINNT < _WIN32_WINNT_WS08
+ TIME_ZONE_INFORMATION tzinfo;
+ auto rv = GetTimeZoneInformation(&tzinfo);
+#else
+ DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
+ auto rv = GetDynamicTimeZoneInformation(&tzinfo);
+#endif
+ if (rv == TIME_ZONE_ID_INVALID)
+ throw spdlog::spdlog_ex("Failed getting timezone info. ", errno);
+
+ int offset = -tzinfo.Bias;
+ if (tm.tm_isdst)
+ {
+ offset -= tzinfo.DaylightBias;
+ }
+ else
+ {
+ offset -= tzinfo.StandardBias;
+ }
+ return offset;
+#else
+
+#if defined(sun) || defined(__sun) || defined(_AIX)
+ // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
+ struct helper
+ {
+ static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime())
+ {
+ int local_year = localtm.tm_year + (1900 - 1);
+ int gmt_year = gmtm.tm_year + (1900 - 1);
+
+ long int days = (
+ // difference in day of year
+ localtm.tm_yday -
+ gmtm.tm_yday
+
+ // + intervening leap days
+ + ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
+ ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
+
+ // + difference in years * 365 */
+ + (long int)(local_year - gmt_year) * 365);
+
+ long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
+ long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
+ long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
+
+ return secs;
+ }
+ };
+
+ auto offset_seconds = helper::calculate_gmt_offset(tm);
+#else
+ auto offset_seconds = tm.tm_gmtoff;
+#endif
+
+ return static_cast<int>(offset_seconds / 60);
+#endif
+}
+
+// Return current thread id as size_t
+// It exists because the std::this_thread::get_id() is much slower(especially under VS 2013)
+inline size_t _thread_id()
+{
+#ifdef _WIN32
+ return static_cast<size_t>(::GetCurrentThreadId());
+#elif __linux__
+#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
+#define SYS_gettid __NR_gettid
+#endif
+ return static_cast<size_t>(syscall(SYS_gettid));
+#elif __FreeBSD__
+ long tid;
+ thr_self(&tid);
+ return static_cast<size_t>(tid);
+#elif __APPLE__
+ uint64_t tid;
+ pthread_threadid_np(nullptr, &tid);
+ return static_cast<size_t>(tid);
+#else // Default to standard C++11 (other Unix)
+ return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
+#endif
+}
+
+// Return current thread id as size_t (from thread local storage)
+inline size_t thread_id()
+{
+#if defined(SPDLOG_DISABLE_TID_CACHING) || (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt) || \
+ (defined(__clang__) && !__has_feature(cxx_thread_local))
+ return _thread_id();
+#else // cache thread id in tls
+ static thread_local const size_t tid = _thread_id();
+ return tid;
+#endif
+}
+
+// This is avoid msvc issue in sleep_for that happens if the clock changes.
+// See https://github.com/gabime/spdlog/issues/609
+inline void sleep_for_millis(int milliseconds)
+{
+#if defined(_WIN32)
+ ::Sleep(milliseconds);
+#else
+ std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
+#endif
+}
+
+// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+#define SPDLOG_FILENAME_T(s) L##s
+inline std::string filename_to_str(const filename_t &filename)
+{
+ std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c;
+ return c.to_bytes(filename);
+}
+#else
+#define SPDLOG_FILENAME_T(s) s
+inline std::string filename_to_str(const filename_t &filename)
+{
+ return filename;
+}
+#endif
+
+inline int pid()
+{
+
+#ifdef _WIN32
+ return static_cast<int>(::GetCurrentProcessId());
+#else
+ return static_cast<int>(::getpid());
+#endif
+}
+
+// Determine if the terminal supports colors
+// Source: https://github.com/agauniyal/rang/
+inline bool is_color_terminal()
+{
+#ifdef _WIN32
+ return true;
+#else
+ static constexpr const char *Terms[] = {
+ "ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"};
+
+ const char *env_p = std::getenv("TERM");
+ if (env_p == nullptr)
+ {
+ return false;
+ }
+
+ static const bool result =
+ std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
+ return result;
+#endif
+}
+
+// Detrmine if the terminal attached
+// Source: https://github.com/agauniyal/rang/
+inline bool in_terminal(FILE *file)
+{
+
+#ifdef _WIN32
+ return _isatty(_fileno(file)) != 0;
+#else
+ return isatty(fileno(file)) != 0;
+#endif
+}
+} // namespace os
+} // namespace details
+} // namespace spdlog
diff --git a/lib/spdlog/details/pattern_formatter_impl.h b/lib/spdlog/details/pattern_formatter_impl.h
new file mode 100644
index 0000000..e3d7087
--- /dev/null
+++ b/lib/spdlog/details/pattern_formatter_impl.h
@@ -0,0 +1,707 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include "../details/log_msg.h"
+#include "../details/os.h"
+#include "../fmt/fmt.h"
+#include "../formatter.h"
+
+#include <array>
+#include <chrono>
+#include <ctime>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <utility>
+#include <vector>
+
+namespace spdlog {
+namespace details {
+class flag_formatter
+{
+public:
+ virtual ~flag_formatter() = default;
+ virtual void format(details::log_msg &msg, const std::tm &tm_time) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////
+// name & level pattern appenders
+///////////////////////////////////////////////////////////////////////
+class name_formatter : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &) override
+ {
+ msg.formatted << *msg.logger_name;
+ }
+};
+
+// log level appender
+class level_formatter : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &) override
+ {
+ msg.formatted << level::to_str(msg.level);
+ }
+};
+
+// short log level appender
+class short_level_formatter : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &) override
+ {
+ msg.formatted << level::to_short_str(msg.level);
+ }
+};
+
+///////////////////////////////////////////////////////////////////////
+// Date time pattern appenders
+///////////////////////////////////////////////////////////////////////
+
+static const char *ampm(const tm &t)
+{
+ return t.tm_hour >= 12 ? "PM" : "AM";
+}
+
+static int to12h(const tm &t)
+{
+ return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
+}
+
+// Abbreviated weekday name
+static const std::string days[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+class a_formatter : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ msg.formatted << days[tm_time.tm_wday];
+ }
+};
+
+// Full weekday name
+static const std::string full_days[]{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
+class A_formatter : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ msg.formatted << full_days[tm_time.tm_wday];
+ }
+};
+
+// Abbreviated month
+static const std::string months[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"};
+class b_formatter : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ msg.formatted << months[tm_time.tm_mon];
+ }
+};
+
+// Full month name
+static const std::string full_months[]{
+ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
+class B_formatter : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ msg.formatted << full_months[tm_time.tm_mon];
+ }
+};
+
+// write 2 ints separated by sep with padding of 2
+static fmt::MemoryWriter &pad_n_join(fmt::MemoryWriter &w, int v1, int v2, char sep)
+{
+ w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0');
+ return w;
+}
+
+// write 3 ints separated by sep with padding of 2
+static fmt::MemoryWriter &pad_n_join(fmt::MemoryWriter &w, int v1, int v2, int v3, char sep)
+{
+ w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0');
+ return w;
+}
+
+// Date and time representation (Thu Aug 23 15:35:46 2014)
+class c_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ msg.formatted << days[tm_time.tm_wday] << ' ' << months[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' ';
+ pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900;
+ }
+};
+
+// year - 2 digit
+class C_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ msg.formatted << fmt::pad(tm_time.tm_year % 100, 2, '0');
+ }
+};
+
+// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
+class D_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ pad_n_join(msg.formatted, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_year % 100, '/');
+ }
+};
+
+// year - 4 digit
+class Y_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ msg.formatted << tm_time.tm_year + 1900;
+ }
+};
+
+// month 1-12
+class m_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ msg.formatted << fmt::pad(tm_time.tm_mon + 1, 2, '0');
+ }
+};
+
+// day of month 1-31
+class d_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ msg.formatted << fmt::pad(tm_time.tm_mday, 2, '0');
+ }
+};
+
+// hours in 24 format 0-23
+class H_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ msg.formatted << fmt::pad(tm_time.tm_hour, 2, '0');
+ }
+};
+
+// hours in 12 format 1-12
+class I_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ msg.formatted << fmt::pad(to12h(tm_time), 2, '0');
+ }
+};
+
+// minutes 0-59
+class M_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ msg.formatted << fmt::pad(tm_time.tm_min, 2, '0');
+ }
+};
+
+// seconds 0-59
+class S_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ msg.formatted << fmt::pad(tm_time.tm_sec, 2, '0');
+ }
+};
+
+// milliseconds
+class e_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &) override
+ {
+ auto duration = msg.time.time_since_epoch();
+ auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
+ msg.formatted << fmt::pad(static_cast<int>(millis), 3, '0');
+ }
+};
+
+// microseconds
+class f_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &) override
+ {
+ auto duration = msg.time.time_since_epoch();
+ auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count() % 1000000;
+ msg.formatted << fmt::pad(static_cast<int>(micros), 6, '0');
+ }
+};
+
+// nanoseconds
+class F_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &) override
+ {
+ auto duration = msg.time.time_since_epoch();
+ auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000;
+ msg.formatted << fmt::pad(static_cast<int>(ns), 9, '0');
+ }
+};
+
+class E_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &) override
+ {
+ auto duration = msg.time.time_since_epoch();
+ auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
+ msg.formatted << seconds;
+ }
+};
+
+// AM/PM
+class p_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ msg.formatted << ampm(tm_time);
+ }
+};
+
+// 12 hour clock 02:55:02 pm
+class r_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ pad_n_join(msg.formatted, to12h(tm_time), tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << ampm(tm_time);
+ }
+};
+
+// 24-hour HH:MM time, equivalent to %H:%M
+class R_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, ':');
+ }
+};
+
+// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
+class T_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+ pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':');
+ }
+};
+
+// ISO 8601 offset from UTC in timezone (+-HH:MM)
+class z_formatter SPDLOG_FINAL : public flag_formatter
+{
+public:
+ const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
+
+ z_formatter() = default;
+ z_formatter(const z_formatter &) = delete;
+ z_formatter &operator=(const z_formatter &) = delete;
+
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+#ifdef _WIN32
+ int total_minutes = get_cached_offset(msg, tm_time);
+#else
+ // No need to chache under gcc,
+ // it is very fast (already stored in tm.tm_gmtoff)
+ int total_minutes = os::utc_minutes_offset(tm_time);
+#endif
+ bool is_negative = total_minutes < 0;
+ char sign;
+ if (is_negative)
+ {
+ total_minutes = -total_minutes;
+ sign = '-';
+ }
+ else
+ {
+ sign = '+';
+ }
+
+ int h = total_minutes / 60;
+ int m = total_minutes % 60;
+ msg.formatted << sign;
+ pad_n_join(msg.formatted, h, m, ':');
+ }
+
+private:
+ log_clock::time_point _last_update{std::chrono::seconds(0)};
+ int _offset_minutes{0};
+ std::mutex _mutex;
+
+ int get_cached_offset(const log_msg &msg, const std::tm &tm_time)
+ {
+ std::lock_guard<std::mutex> l(_mutex);
+ if (msg.time - _last_update >= cache_refresh)
+ {
+ _offset_minutes = os::utc_minutes_offset(tm_time);
+ _last_update = msg.time;
+ }
+ return _offset_minutes;
+ }
+};
+
+// Thread id
+class t_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &) override
+ {
+ msg.formatted << msg.thread_id;
+ }
+};
+
+// Current pid
+class pid_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &) override
+ {
+ msg.formatted << details::os::pid();
+ }
+};
+
+// message counter formatter
+class i_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &) override
+ {
+ msg.formatted << fmt::pad(msg.msg_id, 6, '0');
+ }
+};
+
+class v_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &) override
+ {
+ msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
+ }
+};
+
+class ch_formatter SPDLOG_FINAL : public flag_formatter
+{
+public:
+ explicit ch_formatter(char ch)
+ : _ch(ch)
+ {
+ }
+ void format(details::log_msg &msg, const std::tm &) override
+ {
+ msg.formatted << _ch;
+ }
+
+private:
+ char _ch;
+};
+
+// aggregate user chars to display as is
+class aggregate_formatter SPDLOG_FINAL : public flag_formatter
+{
+public:
+ aggregate_formatter() = default;
+
+ void add_ch(char ch)
+ {
+ _str += ch;
+ }
+ void format(details::log_msg &msg, const std::tm &) override
+ {
+ msg.formatted << _str;
+ }
+
+private:
+ std::string _str;
+};
+
+// mark the color range. expect it to be in the form of "%^colored text%$"
+class color_start_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &) override
+ {
+ msg.color_range_start = msg.formatted.size();
+ }
+};
+class color_stop_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &) override
+ {
+ msg.color_range_end = msg.formatted.size();
+ }
+};
+
+// Full info formatter
+// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
+class full_formatter SPDLOG_FINAL : public flag_formatter
+{
+ void format(details::log_msg &msg, const std::tm &tm_time) override
+ {
+#ifndef SPDLOG_NO_DATETIME
+ auto duration = msg.time.time_since_epoch();
+ auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
+
+ /* Slower version(while still very fast - about 3.2 million lines/sec under 10 threads),
+ msg.formatted.write("[{:d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}.{:03d}] [{}] [{}] {} ",
+ tm_time.tm_year + 1900,
+ tm_time.tm_mon + 1,
+ tm_time.tm_mday,
+ tm_time.tm_hour,
+ tm_time.tm_min,
+ tm_time.tm_sec,
+ static_cast<int>(millis),
+ msg.logger_name,
+ level::to_str(msg.level),
+ msg.raw.str());*/
+
+ // Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads)
+ msg.formatted << '[' << static_cast<unsigned int>(tm_time.tm_year + 1900) << '-'
+ << fmt::pad(static_cast<unsigned int>(tm_time.tm_mon + 1), 2, '0') << '-'
+ << fmt::pad(static_cast<unsigned int>(tm_time.tm_mday), 2, '0') << ' '
+ << fmt::pad(static_cast<unsigned int>(tm_time.tm_hour), 2, '0') << ':'
+ << fmt::pad(static_cast<unsigned int>(tm_time.tm_min), 2, '0') << ':'
+ << fmt::pad(static_cast<unsigned int>(tm_time.tm_sec), 2, '0') << '.'
+ << fmt::pad(static_cast<unsigned int>(millis), 3, '0') << "] ";
+
+ // no datetime needed
+#else
+ (void)tm_time;
+#endif
+
+#ifndef SPDLOG_NO_NAME
+ msg.formatted << '[' << *msg.logger_name << "] ";
+#endif
+
+ msg.formatted << '[';
+ // wrap the level name with color
+ msg.color_range_start = msg.formatted.size();
+ msg.formatted << level::to_str(msg.level);
+ msg.color_range_end = msg.formatted.size();
+ msg.formatted << "] " << fmt::StringRef(msg.raw.data(), msg.raw.size());
+ }
+};
+
+} // namespace details
+} // namespace spdlog
+///////////////////////////////////////////////////////////////////////////////
+// pattern_formatter inline impl
+///////////////////////////////////////////////////////////////////////////////
+inline spdlog::pattern_formatter::pattern_formatter(const std::string &pattern, pattern_time_type pattern_time, std::string eol)
+ : _eol(std::move(eol))
+ , _pattern_time(pattern_time)
+{
+ compile_pattern(pattern);
+}
+
+inline void spdlog::pattern_formatter::compile_pattern(const std::string &pattern)
+{
+ auto end = pattern.end();
+ std::unique_ptr<details::aggregate_formatter> user_chars;
+ for (auto it = pattern.begin(); it != end; ++it)
+ {
+ if (*it == '%')
+ {
+ if (user_chars) // append user chars found so far
+ {
+ _formatters.push_back(std::move(user_chars));
+ }
+ // if(
+ if (++it != end)
+ {
+ handle_flag(*it);
+ }
+ else
+ {
+ break;
+ }
+ }
+ else // chars not following the % sign should be displayed as is
+ {
+ if (!user_chars)
+ {
+ user_chars = std::unique_ptr<details::aggregate_formatter>(new details::aggregate_formatter());
+ }
+ user_chars->add_ch(*it);
+ }
+ }
+ if (user_chars) // append raw chars found so far
+ {
+ _formatters.push_back(std::move(user_chars));
+ }
+}
+inline void spdlog::pattern_formatter::handle_flag(char flag)
+{
+ switch (flag)
+ {
+ // logger name
+ case 'n':
+ _formatters.emplace_back(new details::name_formatter());
+ break;
+
+ case 'l':
+ _formatters.emplace_back(new details::level_formatter());
+ break;
+
+ case 'L':
+ _formatters.emplace_back(new details::short_level_formatter());
+ break;
+
+ case ('t'):
+ _formatters.emplace_back(new details::t_formatter());
+ break;
+
+ case ('v'):
+ _formatters.emplace_back(new details::v_formatter());
+ break;
+
+ case ('a'):
+ _formatters.emplace_back(new details::a_formatter());
+ break;
+
+ case ('A'):
+ _formatters.emplace_back(new details::A_formatter());
+ break;
+
+ case ('b'):
+ case ('h'):
+ _formatters.emplace_back(new details::b_formatter());
+ break;
+
+ case ('B'):
+ _formatters.emplace_back(new details::B_formatter());
+ break;
+ case ('c'):
+ _formatters.emplace_back(new details::c_formatter());
+ break;
+
+ case ('C'):
+ _formatters.emplace_back(new details::C_formatter());
+ break;
+
+ case ('Y'):
+ _formatters.emplace_back(new details::Y_formatter());
+ break;
+
+ case ('D'):
+ case ('x'):
+
+ _formatters.emplace_back(new details::D_formatter());
+ break;
+
+ case ('m'):
+ _formatters.emplace_back(new details::m_formatter());
+ break;
+
+ case ('d'):
+ _formatters.emplace_back(new details::d_formatter());
+ break;
+
+ case ('H'):
+ _formatters.emplace_back(new details::H_formatter());
+ break;
+
+ case ('I'):
+ _formatters.emplace_back(new details::I_formatter());
+ break;
+
+ case ('M'):
+ _formatters.emplace_back(new details::M_formatter());
+ break;
+
+ case ('S'):
+ _formatters.emplace_back(new details::S_formatter());
+ break;
+
+ case ('e'):
+ _formatters.emplace_back(new details::e_formatter());
+ break;
+
+ case ('f'):
+ _formatters.emplace_back(new details::f_formatter());
+ break;
+ case ('F'):
+ _formatters.emplace_back(new details::F_formatter());
+ break;
+
+ case ('E'):
+ _formatters.emplace_back(new details::E_formatter());
+ break;
+
+ case ('p'):
+ _formatters.emplace_back(new details::p_formatter());
+ break;
+
+ case ('r'):
+ _formatters.emplace_back(new details::r_formatter());
+ break;
+
+ case ('R'):
+ _formatters.emplace_back(new details::R_formatter());
+ break;
+
+ case ('T'):
+ case ('X'):
+ _formatters.emplace_back(new details::T_formatter());
+ break;
+
+ case ('z'):
+ _formatters.emplace_back(new details::z_formatter());
+ break;
+
+ case ('+'):
+ _formatters.emplace_back(new details::full_formatter());
+ break;
+
+ case ('P'):
+ _formatters.emplace_back(new details::pid_formatter());
+ break;
+
+ case ('i'):
+ _formatters.emplace_back(new details::i_formatter());
+ break;
+
+ case ('^'):
+ _formatters.emplace_back(new details::color_start_formatter());
+ break;
+
+ case ('$'):
+ _formatters.emplace_back(new details::color_stop_formatter());
+ break;
+
+ default: // Unknown flag appears as is
+ _formatters.emplace_back(new details::ch_formatter('%'));
+ _formatters.emplace_back(new details::ch_formatter(flag));
+ break;
+ }
+}
+
+inline std::tm spdlog::pattern_formatter::get_time(details::log_msg &msg)
+{
+ if (_pattern_time == pattern_time_type::local)
+ {
+ return details::os::localtime(log_clock::to_time_t(msg.time));
+ }
+ return details::os::gmtime(log_clock::to_time_t(msg.time));
+}
+
+inline void spdlog::pattern_formatter::format(details::log_msg &msg)
+{
+
+#ifndef SPDLOG_NO_DATETIME
+ auto tm_time = get_time(msg);
+#else
+ std::tm tm_time;
+#endif
+ for (auto &f : _formatters)
+ {
+ f->format(msg, tm_time);
+ }
+ // write eol
+ msg.formatted << _eol;
+}
diff --git a/lib/spdlog/details/registry.h b/lib/spdlog/details/registry.h
new file mode 100644
index 0000000..614220d
--- /dev/null
+++ b/lib/spdlog/details/registry.h
@@ -0,0 +1,263 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+// Loggers registy of unique name->logger pointer
+// An attempt to create a logger with an already existing name will be ignored
+// If user requests a non existing logger, nullptr will be returned
+// This class is thread safe
+
+#include "../async_logger.h"
+#include "../common.h"
+#include "../details/null_mutex.h"
+#include "../logger.h"
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+
+namespace spdlog {
+namespace details {
+template<class Mutex>
+class registry_t
+{
+public:
+ registry_t<Mutex>(const registry_t<Mutex> &) = delete;
+ registry_t<Mutex> &operator=(const registry_t<Mutex> &) = delete;
+
+ void register_logger(std::shared_ptr<logger> logger)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ auto logger_name = logger->name();
+ throw_if_exists(logger_name);
+ _loggers[logger_name] = logger;
+ }
+
+ std::shared_ptr<logger> get(const std::string &logger_name)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ auto found = _loggers.find(logger_name);
+ return found == _loggers.end() ? nullptr : found->second;
+ }
+
+ template<class It>
+ std::shared_ptr<logger> create(const std::string &logger_name, const It &sinks_begin, const It &sinks_end)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ throw_if_exists(logger_name);
+ std::shared_ptr<logger> new_logger;
+ if (_async_mode)
+ {
+ new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy,
+ _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb);
+ }
+ else
+ {
+ new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end);
+ }
+
+ if (_formatter)
+ {
+ new_logger->set_formatter(_formatter);
+ }
+
+ if (_err_handler)
+ {
+ new_logger->set_error_handler(_err_handler);
+ }
+
+ new_logger->set_level(_level);
+ new_logger->flush_on(_flush_level);
+
+ // Add to registry
+ _loggers[logger_name] = new_logger;
+ return new_logger;
+ }
+
+ template<class It>
+ std::shared_ptr<async_logger> create_async(const std::string &logger_name, size_t queue_size,
+ const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
+ const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb, const It &sinks_begin,
+ const It &sinks_end)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ throw_if_exists(logger_name);
+ auto new_logger = std::make_shared<async_logger>(
+ logger_name, sinks_begin, sinks_end, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
+
+ if (_formatter)
+ {
+ new_logger->set_formatter(_formatter);
+ }
+
+ if (_err_handler)
+ {
+ new_logger->set_error_handler(_err_handler);
+ }
+
+ new_logger->set_level(_level);
+ new_logger->flush_on(_flush_level);
+
+ // Add to registry
+ _loggers[logger_name] = new_logger;
+ return new_logger;
+ }
+
+ void apply_all(std::function<void(std::shared_ptr<logger>)> fun)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ for (auto &l : _loggers)
+ {
+ fun(l.second);
+ }
+ }
+
+ void drop(const std::string &logger_name)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ _loggers.erase(logger_name);
+ }
+
+ void drop_all()
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ _loggers.clear();
+ }
+
+ std::shared_ptr<logger> create(const std::string &logger_name, sinks_init_list sinks)
+ {
+ return create(logger_name, sinks.begin(), sinks.end());
+ }
+
+ std::shared_ptr<logger> create(const std::string &logger_name, sink_ptr sink)
+ {
+ return create(logger_name, {sink});
+ }
+
+ std::shared_ptr<async_logger> create_async(const std::string &logger_name, size_t queue_size,
+ const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
+ const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb, sinks_init_list sinks)
+ {
+ return create_async(
+ logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks.begin(), sinks.end());
+ }
+
+ std::shared_ptr<async_logger> create_async(const std::string &logger_name, size_t queue_size,
+ const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
+ const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb, sink_ptr sink)
+ {
+ return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, {sink});
+ }
+
+ void formatter(formatter_ptr f)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ _formatter = f;
+ for (auto &l : _loggers)
+ {
+ l.second->set_formatter(_formatter);
+ }
+ }
+
+ void set_pattern(const std::string &pattern)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ _formatter = std::make_shared<pattern_formatter>(pattern);
+ for (auto &l : _loggers)
+ {
+ l.second->set_formatter(_formatter);
+ }
+ }
+
+ void set_level(level::level_enum log_level)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ for (auto &l : _loggers)
+ {
+ l.second->set_level(log_level);
+ }
+ _level = log_level;
+ }
+
+ void flush_on(level::level_enum log_level)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ for (auto &l : _loggers)
+ {
+ l.second->flush_on(log_level);
+ }
+ _flush_level = log_level;
+ }
+
+ void set_error_handler(log_err_handler handler)
+ {
+ for (auto &l : _loggers)
+ {
+ l.second->set_error_handler(handler);
+ }
+ _err_handler = handler;
+ }
+
+ void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
+ const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ _async_mode = true;
+ _async_q_size = q_size;
+ _overflow_policy = overflow_policy;
+ _worker_warmup_cb = worker_warmup_cb;
+ _flush_interval_ms = flush_interval_ms;
+ _worker_teardown_cb = worker_teardown_cb;
+ }
+
+ void set_sync_mode()
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ _async_mode = false;
+ }
+
+ static registry_t<Mutex> &instance()
+ {
+ static registry_t<Mutex> s_instance;
+ return s_instance;
+ }
+
+private:
+ registry_t<Mutex>() = default;
+
+ void throw_if_exists(const std::string &logger_name)
+ {
+ if (_loggers.find(logger_name) != _loggers.end())
+ {
+ throw spdlog_ex("logger with name '" + logger_name + "' already exists");
+ }
+ }
+
+ Mutex _mutex;
+ std::unordered_map<std::string, std::shared_ptr<logger>> _loggers;
+ formatter_ptr _formatter;
+ level::level_enum _level = level::info;
+ level::level_enum _flush_level = level::off;
+ log_err_handler _err_handler;
+ bool _async_mode = false;
+ size_t _async_q_size = 0;
+ async_overflow_policy _overflow_policy = async_overflow_policy::block_retry;
+ std::function<void()> _worker_warmup_cb;
+ std::chrono::milliseconds _flush_interval_ms{std::chrono::milliseconds::zero()};
+ std::function<void()> _worker_teardown_cb;
+};
+
+#ifdef SPDLOG_NO_REGISTRY_MUTEX
+using registry = registry_t<spdlog::details::null_mutex>;
+#else
+using registry = registry_t<std::mutex>;
+#endif
+
+} // namespace details
+} // namespace spdlog
diff --git a/lib/spdlog/details/spdlog_impl.h b/lib/spdlog/details/spdlog_impl.h
new file mode 100644
index 0000000..4c36383
--- /dev/null
+++ b/lib/spdlog/details/spdlog_impl.h
@@ -0,0 +1,278 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+//
+// Global registry functions
+//
+#include "../details/registry.h"
+#include "../sinks/file_sinks.h"
+#include "../sinks/stdout_sinks.h"
+#include "../spdlog.h"
+#ifdef SPDLOG_ENABLE_SYSLOG
+#include "../sinks/syslog_sink.h"
+#endif
+
+#if defined _WIN32 && !defined(__cplusplus_winrt)
+#include "../sinks/wincolor_sink.h"
+#else
+#include "../sinks/ansicolor_sink.h"
+#endif
+
+#ifdef __ANDROID__
+#include "../sinks/android_sink.h"
+#endif
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <string>
+
+inline void spdlog::register_logger(std::shared_ptr<logger> logger)
+{
+ return details::registry::instance().register_logger(std::move(logger));
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::get(const std::string &name)
+{
+ return details::registry::instance().get(name);
+}
+
+inline void spdlog::drop(const std::string &name)
+{
+ details::registry::instance().drop(name);
+}
+
+// Create multi/single threaded simple file logger
+inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate)
+{
+ return create<spdlog::sinks::simple_file_sink_mt>(logger_name, filename, truncate);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate)
+{
+ return create<spdlog::sinks::simple_file_sink_st>(logger_name, filename, truncate);
+}
+
+// Create multi/single threaded rotating file logger
+inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(
+ const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
+{
+ return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(
+ const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
+{
+ return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
+}
+
+// Create file logger which creates new file at midnight):
+inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(
+ const std::string &logger_name, const filename_t &filename, int hour, int minute)
+{
+ return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, hour, minute);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(
+ const std::string &logger_name, const filename_t &filename, int hour, int minute)
+{
+ return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, hour, minute);
+}
+
+//
+// stdout/stderr loggers
+//
+inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string &logger_name)
+{
+ return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance());
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string &logger_name)
+{
+ return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance());
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string &logger_name)
+{
+ return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance());
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string &logger_name)
+{
+ return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance());
+}
+
+//
+// stdout/stderr color loggers
+//
+#if defined _WIN32 && !defined(__cplusplus_winrt)
+
+inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string &logger_name)
+{
+ auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>();
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string &logger_name)
+{
+ auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_st>();
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string &logger_name)
+{
+ auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_mt>();
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string &logger_name)
+{
+ auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_st>();
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
+
+#else // ansi terminal colors
+
+inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string &logger_name)
+{
+ auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string &logger_name)
+{
+ auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>();
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string &logger_name)
+{
+ auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>();
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string &logger_name)
+{
+ auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_st>();
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
+#endif
+
+#ifdef SPDLOG_ENABLE_SYSLOG
+// Create syslog logger
+inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(
+ const std::string &logger_name, const std::string &syslog_ident, int syslog_option, int syslog_facility)
+{
+ return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option, syslog_facility);
+}
+#endif
+
+#ifdef __ANDROID__
+inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string &logger_name, const std::string &tag)
+{
+ return create<spdlog::sinks::android_sink>(logger_name, tag);
+}
+#endif
+
+// Create and register a logger a single sink
+inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, const spdlog::sink_ptr &sink)
+{
+ return details::registry::instance().create(logger_name, sink);
+}
+
+// Create logger with multiple sinks
+inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, spdlog::sinks_init_list sinks)
+{
+ return details::registry::instance().create(logger_name, sinks);
+}
+
+template<typename Sink, typename... Args>
+inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, Args... args)
+{
+ sink_ptr sink = std::make_shared<Sink>(args...);
+ return details::registry::instance().create(logger_name, {sink});
+}
+
+template<class It>
+inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, const It &sinks_begin, const It &sinks_end)
+{
+ return details::registry::instance().create(logger_name, sinks_begin, sinks_end);
+}
+
+// Create and register an async logger with a single sink
+inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string &logger_name, const sink_ptr &sink, size_t queue_size,
+ const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
+ const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb)
+{
+ return details::registry::instance().create_async(
+ logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sink);
+}
+
+// Create and register an async logger with multiple sinks
+inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string &logger_name, sinks_init_list sinks, size_t queue_size,
+ const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
+ const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb)
+{
+ return details::registry::instance().create_async(
+ logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks);
+}
+
+template<class It>
+inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string &logger_name, const It &sinks_begin, const It &sinks_end,
+ size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
+ const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb)
+{
+ return details::registry::instance().create_async(
+ logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks_begin, sinks_end);
+}
+
+inline void spdlog::set_formatter(spdlog::formatter_ptr f)
+{
+ details::registry::instance().formatter(std::move(f));
+}
+
+inline void spdlog::set_pattern(const std::string &format_string)
+{
+ return details::registry::instance().set_pattern(format_string);
+}
+
+inline void spdlog::set_level(level::level_enum log_level)
+{
+ return details::registry::instance().set_level(log_level);
+}
+
+inline void spdlog::flush_on(level::level_enum log_level)
+{
+ return details::registry::instance().flush_on(log_level);
+}
+
+inline void spdlog::set_error_handler(log_err_handler handler)
+{
+ return details::registry::instance().set_error_handler(std::move(handler));
+}
+
+inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy,
+ const std::function<void()> &worker_warmup_cb, const std::chrono::milliseconds &flush_interval_ms,
+ const std::function<void()> &worker_teardown_cb)
+{
+ details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
+}
+
+inline void spdlog::set_sync_mode()
+{
+ details::registry::instance().set_sync_mode();
+}
+
+inline void spdlog::apply_all(std::function<void(std::shared_ptr<logger>)> fun)
+{
+ details::registry::instance().apply_all(std::move(fun));
+}
+
+inline void spdlog::drop_all()
+{
+ details::registry::instance().drop_all();
+}