diff options
Diffstat (limited to 'lib/spdlog/details')
-rw-r--r-- | lib/spdlog/details/async_log_helper.h | 333 | ||||
-rw-r--r-- | lib/spdlog/details/async_logger_impl.h | 95 | ||||
-rw-r--r-- | lib/spdlog/details/file_helper.h | 151 | ||||
-rw-r--r-- | lib/spdlog/details/log_msg.h | 48 | ||||
-rw-r--r-- | lib/spdlog/details/logger_impl.h | 348 | ||||
-rw-r--r-- | lib/spdlog/details/mpmc_blocking_q.h | 84 | ||||
-rw-r--r-- | lib/spdlog/details/null_mutex.h | 45 | ||||
-rw-r--r-- | lib/spdlog/details/os.h | 431 | ||||
-rw-r--r-- | lib/spdlog/details/pattern_formatter_impl.h | 707 | ||||
-rw-r--r-- | lib/spdlog/details/registry.h | 263 | ||||
-rw-r--r-- | lib/spdlog/details/spdlog_impl.h | 278 |
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(); +} |