From 39cdd08a5475c63959174747a140de86c24e4849 Mon Sep 17 00:00:00 2001 From: gabime Date: Fri, 5 Aug 2016 03:56:40 +0300 Subject: [PATCH] no exceptions while logging --- README.md | 147 ++- example/example.cpp | 33 +- include/spdlog/common.h | 3 + include/spdlog/details/async_log_helper.h | 43 +- include/spdlog/details/async_logger_impl.h | 13 +- include/spdlog/details/logger_impl.h | 180 ++-- .../spdlog/details/pattern_formatter_impl.h | 975 +++++++++--------- include/spdlog/details/registry.h | 13 + include/spdlog/details/spdlog_impl.h | 7 + include/spdlog/logger.h | 15 +- include/spdlog/spdlog.h | 5 + tests/errors.cpp | 61 ++ tests/format.cpp | 19 +- tests/tests.vcxproj | 1 + tests/tests.vcxproj.filters | 3 + 15 files changed, 847 insertions(+), 671 deletions(-) create mode 100644 tests/errors.cpp diff --git a/README.md b/README.md index 89e61d39..ccf65816 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ Just copy the source [folder](https://github.com/gabime/spdlog/tree/master/inclu * Linux (gcc 4.8.1+, clang 3.5+) * Windows (visual studio 2013+, cygwin/mingw with g++ 4.9.1+) * Mac OSX (clang 3.5+) - * FreeBSD (gcc 4.8.1+) ##Features * Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below). @@ -58,22 +57,36 @@ Time needed to log 1,000,000 lines in asynchronous mode, i.e. the time it takes ## Usage Example ```c++ -#include +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// +// +// spdlog usage example +// #include "spdlog/spdlog.h" -int main(int, char* []) +#include +#include + +void async_example(); +void syslog_example(); +void user_defined_example(); +void err_handler_example(); + +namespace spd = spdlog; +int main(int, char*[]) { - namespace spd = spdlog; try { - // console logger (multithreaded and with color) + // Multithreaded color console auto console = spd::stdout_logger_mt("console", true); - console->info("Welcome to spdlog!") ; - console->info("An info message example {}..", 1); + console->info("Welcome to spdlog!"); + console->error("An info message example {}..", 1); - //Formatting examples - console->info("Easy padding in numbers like {:08d}", 12); - console->info("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); + // Formatting examples + console->warn("Easy padding in numbers like {:08d}", 12); + console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); console->info("Support for floats {:03.2f}", 1.23456); console->info("Positional args are {1} {0}..", "too", "supported"); @@ -81,72 +94,110 @@ int main(int, char* []) console->info("{:>30}", "right aligned"); console->info("{:^30}", "centered"); - // + spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function"); + // Runtime log levels - // spd::set_level(spd::level::info); //Set global log level to info console->debug("This message shold not be displayed!"); console->set_level(spd::level::debug); // Set specific logger's log level - console->debug("Now it should.."); - - // - // Create a basic multithreaded file logger (or "basic_logger_st" for single threaded logger) - // + console->debug("This message shold be displayed.."); + + // Create basic file logger (not rotated) auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt"); my_logger->info("Some log message"); - // + // Create a file rotating logger with 5mb size max and 3 rotated files - // auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3); - for(int i = 0; i < 10; ++i) - rotating_logger->info("{} * {} equals {:>10}", i, i, i*i); + for (int i = 0; i < 10; ++i) + rotating_logger->info("{} * {} equals {:>10}", i, i, i*i); - // - // Create a daily logger - a new file is created every day at 2:30am - // + // Create a daily logger - a new file is created every day on 2:30am auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30); + daily_logger->info(123.44); - // // Customize msg format for all messages - // spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***"); rotating_logger->info("This is another message with custom format"); - spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function"); - - // // Compile time debug or trace macros. // Enabled #ifdef SPDLOG_DEBUG_ON or #ifdef SPDLOG_TRACE_ON - // SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23); SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23); - - // + // Asynchronous logging is very fast.. // Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous.. - // - size_t q_size = 8192; //queue size must be power of 2 - spdlog::set_async_mode(q_size); - auto async_file= spd::daily_logger_st("async_file_logger", "logs/async_log.txt"); - async_file->info("This is async log..Should be very fast!"); + async_example(); - // - // syslog example. linux only.. - // - #ifdef __linux__ - std::string ident = "spdlog-example"; - auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID); - syslog_logger->warn("This is warning that will end up in syslog. This is Linux only!"); - #endif - + // syslog example. linux/osx only.. + syslog_example(); + + // log user-defined types example.. + user_defined_example(); + + // Change default log error handler + err_handler_example(); + + console->info("End of example. bye.."); + + // Release and close all loggers + spdlog::drop_all(); } + // Exceptions will only be thrown upon failed logger or sink construction (not during logging) catch (const spd::spdlog_ex& ex) { - std::cout << "Log failed: " << ex.what() << std::endl; - } + std::cout << "Log init failed: " << ex.what() << std::endl; + return 1; + } } +void async_example() +{ + size_t q_size = 4096; //queue size must be power of 2 + spdlog::set_async_mode(q_size); + auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt"); + for (int i = 0; i < 100; ++i) + async_file->info("Async message #{}{}", i); +} + +//syslog example (linux/osx only) +void syslog_example() +{ +#if defined (__linux__) || defined(__APPLE__) + std::string ident = "spdlog-example"; + auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID); + syslog_logger->warn("This is warning that will end up in syslog. This is Linux only!"); +#endif +} + +// user defined types logging by implementing operator<< +struct my_type +{ + int i; + template + friend OStream& operator<<(OStream& os, const my_type &c) + { + return os << "[my_type i="< // must be included +void user_defined_example() +{ + spd::get("console")->info("user defined type: {}", my_type { 14 }); +} + +// +//custom error handler +// +void err_handler_example() +{ + //can be set globaly or per logger (logger->set_error_handler(..)) + spdlog::set_error_handler([](const std::string& msg) { + std::cerr << "my err handler: " << msg << std::endl; + }); + spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3); +} ``` diff --git a/example/example.cpp b/example/example.cpp index ed9a506d..222eeb13 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -7,13 +7,13 @@ // #include "spdlog/spdlog.h" -#include // EXIT_FAILURE #include #include void async_example(); void syslog_example(); void user_defined_example(); +void err_handler_example(); namespace spd = spdlog; int main(int, char*[]) @@ -61,12 +61,11 @@ int main(int, char*[]) spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***"); rotating_logger->info("This is another message with custom format"); - // Compile time debug or trace macros. // Enabled #ifdef SPDLOG_DEBUG_ON or #ifdef SPDLOG_TRACE_ON SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23); SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23); - + // Asynchronous logging is very fast.. // Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous.. async_example(); @@ -77,27 +76,29 @@ int main(int, char*[]) // log user-defined types example.. user_defined_example(); + // Change default log error handler + err_handler_example(); + + console->info("End of example. bye.."); // Release and close all loggers spdlog::drop_all(); } - + // Exceptions will only be thrown upon failed logger or sink construction (not during logging) catch (const spd::spdlog_ex& ex) { - std::cout << "Log failed: " << ex.what() << std::endl; - return EXIT_FAILURE; - } - return EXIT_SUCCESS; + std::cout << "Log init failed: " << ex.what() << std::endl; + return 1; + } } - void async_example() { size_t q_size = 4096; //queue size must be power of 2 spdlog::set_async_mode(q_size); auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt"); for (int i = 0; i < 100; ++i) - async_file->info("Async message #{}", i); + async_file->info("Async message #{}{}", i); } //syslog example (linux/osx only) @@ -127,3 +128,15 @@ void user_defined_example() spd::get("console")->info("user defined type: {}", my_type { 14 }); } +// +//custom error handler +// +void err_handler_example() +{ + //can be set globaly or per logger(logger->set_error_handler(..)) + spdlog::set_error_handler([](const std::string& msg) { + std::cerr << "my err handler: " << msg << std::endl; + }); + spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3); +} + diff --git a/include/spdlog/common.h b/include/spdlog/common.h index 21399bc1..490deec7 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -11,6 +11,8 @@ #include #include #include +#include + #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #include #include @@ -58,6 +60,7 @@ using level_t = details::null_atomic_int; using level_t = std::atomic_int; #endif +using log_err_handler = std::function; //Log level enum namespace level diff --git a/include/spdlog/details/async_log_helper.h b/include/spdlog/details/async_log_helper.h index d257c994..5f50b73d 100644 --- a/include/spdlog/details/async_log_helper.h +++ b/include/spdlog/details/async_log_helper.h @@ -120,6 +120,7 @@ public: async_log_helper(formatter_ptr formatter, const std::vector& sinks, size_t queue_size, + const log_err_handler err_handler, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), @@ -142,14 +143,13 @@ private: // queue of messages to log q_type _q; + log_err_handler _err_handler; + bool _flush_requested; bool _terminate_requested; - - // last exception thrown from the worker thread - std::shared_ptr _last_workerthread_ex; - + // overflow policy const async_overflow_policy _overflow_policy; @@ -166,10 +166,7 @@ private: std::thread _worker_thread; void push_msg(async_msg&& new_msg); - - // throw last worker thread exception or if worker thread is not active - void throw_if_bad_worker(); - + // worker thread main loop void worker_loop(); @@ -181,7 +178,7 @@ private: // sleep,yield or return immediatly using the time passed since last message as a hint static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time); - + }; } } @@ -193,6 +190,7 @@ inline spdlog::details::async_log_helper::async_log_helper( formatter_ptr formatter, const std::vector& sinks, size_t queue_size, + log_err_handler err_handler, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, @@ -200,6 +198,7 @@ inline spdlog::details::async_log_helper::async_log_helper( _formatter(formatter), _sinks(sinks), _q(queue_size), + _err_handler(err_handler), _flush_requested(false), _terminate_requested(false), _overflow_policy(overflow_policy), @@ -232,8 +231,7 @@ inline void spdlog::details::async_log_helper::log(const details::log_msg& msg) } inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg) -{ - throw_if_bad_worker(); +{ if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg) { auto last_op_time = details::os::now(); @@ -263,14 +261,12 @@ inline void spdlog::details::async_log_helper::worker_loop() while(process_next_msg(last_pop, last_flush)); if (_worker_teardown_cb) _worker_teardown_cb(); } - catch (const std::exception& ex) - { - _last_workerthread_ex = std::make_shared(std::string("async_logger worker thread exception: ") + ex.what()); - } - catch (...) - { - _last_workerthread_ex = std::make_shared("async_logger worker thread exception"); - } + catch (const std::exception &ex) { + _err_handler(ex.what()); + } + catch (...) { + _err_handler("Unknown exception"); + } } // process next message in the queue @@ -362,15 +358,6 @@ inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_ return sleep_for(milliseconds(200)); } -// throw if the worker thread threw an exception or not active -inline void spdlog::details::async_log_helper::throw_if_bad_worker() -{ - if (_last_workerthread_ex) - { - auto ex = std::move(_last_workerthread_ex); - throw *ex; - } -} diff --git a/include/spdlog/details/async_logger_impl.h b/include/spdlog/details/async_logger_impl.h index e0af1985..7bd7c488 100644 --- a/include/spdlog/details/async_logger_impl.h +++ b/include/spdlog/details/async_logger_impl.h @@ -26,7 +26,7 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb) : logger(logger_name, begin, end), - _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb)) + _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb)) { } @@ -73,5 +73,14 @@ inline void spdlog::async_logger::_set_pattern(const std::string& pattern) inline void spdlog::async_logger::_sink_it(details::log_msg& msg) { - _async_log_helper->log(msg); + try + { + _async_log_helper->log(msg); + } + catch (const std::exception &ex) { + _err_handler(ex.what()); + } + catch (...) { + _err_handler("Unknown exception"); + } } diff --git a/include/spdlog/details/logger_impl.h b/include/spdlog/details/logger_impl.h index 751bb2cf..06aef705 100644 --- a/include/spdlog/details/logger_impl.h +++ b/include/spdlog/details/logger_impl.h @@ -6,35 +6,39 @@ #pragma once #include +#include #include #include + // create logger with given name, sinks and the default pattern formatter // all other ctors will call this one template -inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end) : - _name(logger_name), - _sinks(begin, end), - _formatter(std::make_shared("%+")) -{ - - // no support under vs2013 for member initialization for std::atomic - _level = level::info; - _flush_level = level::off; +inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end): + _name(logger_name), + _sinks(begin, end), + _formatter(std::make_shared("%+")) +{ + _level = level::info; + _flush_level = level::off; + _last_err_time = 0; + _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()) {} +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, -{ - single_sink -}) {} +inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink): + logger(logger_name, + { + single_sink + }) +{} inline spdlog::logger::~logger() = default; @@ -42,131 +46,143 @@ inline spdlog::logger::~logger() = default; inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter) { - _set_formatter(msg_formatter); + _set_formatter(msg_formatter); } inline void spdlog::logger::set_pattern(const std::string& pattern) { - _set_pattern(pattern); + _set_pattern(pattern); } template inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args) { - if (!should_log(lvl)) return; - - details::log_msg log_msg(&_name, lvl); - try - { - log_msg.raw.write(fmt, args...); - } - catch (fmt::FormatError &ex) - { - throw spdlog::spdlog_ex(std::string("format error in \"") + fmt + "\": " + ex.what()); - } - - _sink_it(log_msg); + if (!should_log(lvl)) return; + try { + details::log_msg log_msg(&_name, lvl); + log_msg.raw.write(fmt, args...); + _sink_it(log_msg); + } + catch (const std::exception &ex) { + _err_handler(ex.what()); + } + catch (...) { + _err_handler("Unknown exception"); + } } template inline void spdlog::logger::log(level::level_enum lvl, const char* msg) { - if (!should_log(lvl)) return; - - details::log_msg log_msg(&_name, lvl); - log_msg.raw << msg; - _sink_it(log_msg); + if (!should_log(lvl)) return; + try { + details::log_msg log_msg(&_name, lvl); + log_msg.raw << msg; + _sink_it(log_msg); + } + catch (const std::exception &ex) { + _err_handler(ex.what()); + } + catch (...) { + _err_handler("Unknown exception"); + } } template inline void spdlog::logger::log(level::level_enum lvl, const T& msg) { - if (!should_log(lvl)) return; - - details::log_msg log_msg(&_name, lvl); - log_msg.raw << msg; - _sink_it(log_msg); - + if (!should_log(lvl)) return; + try { + details::log_msg log_msg(&_name, lvl); + log_msg.raw << msg; + _sink_it(log_msg); + } + catch (const std::exception &ex) { + _err_handler(ex.what()); + } + catch (...) { + _err_handler("Unknown exception"); + } } template inline void spdlog::logger::trace(const char* fmt, const Args&... args) { - log(level::trace, fmt, args...); + log(level::trace, fmt, args...); } template inline void spdlog::logger::debug(const char* fmt, const Args&... args) { - log(level::debug, fmt, args...); + log(level::debug, fmt, args...); } template inline void spdlog::logger::info(const char* fmt, const Args&... args) { - log(level::info, fmt, args...); + log(level::info, fmt, args...); } template inline void spdlog::logger::warn(const char* fmt, const Args&... args) { - log(level::warn, fmt, args...); + log(level::warn, fmt, args...); } template inline void spdlog::logger::error(const char* fmt, const Args&... args) { - log(level::err, fmt, args...); + log(level::err, fmt, args...); } template inline void spdlog::logger::critical(const char* fmt, const Args&... args) { - log(level::critical, fmt, args...); + log(level::critical, fmt, args...); } template inline void spdlog::logger::trace(const T& msg) { - log(level::trace, msg); + log(level::trace, msg); } template inline void spdlog::logger::debug(const T& msg) { - log(level::debug, msg); + log(level::debug, msg); } template inline void spdlog::logger::info(const T& msg) { - log(level::info, msg); + log(level::info, msg); } template inline void spdlog::logger::warn(const T& msg) { - log(level::warn, msg); + log(level::warn, msg); } template inline void spdlog::logger::error(const T& msg) { - log(level::err, msg); + log(level::err, msg); } template inline void spdlog::logger::critical(const T& msg) { - log(level::critical, msg); + log(level::critical, msg); } @@ -177,27 +193,38 @@ inline void spdlog::logger::critical(const T& msg) // inline const std::string& spdlog::logger::name() const { - return _name; + return _name; } inline void spdlog::logger::set_level(spdlog::level::level_enum log_level) { - _level.store(log_level); + _level.store(log_level); } +inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler) +{ + _err_handler = 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); + _flush_level.store(log_level); } inline spdlog::level::level_enum spdlog::logger::level() const { - return static_cast(_level.load(std::memory_order_relaxed)); + return static_cast(_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); + return msg_level >= _level.load(std::memory_order_relaxed); } // @@ -205,26 +232,41 @@ inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) cons // inline void spdlog::logger::_sink_it(details::log_msg& msg) { - _formatter->format(msg); - for (auto &sink : _sinks) - sink->log(msg); - const auto flush_level = _flush_level.load(std::memory_order_relaxed); - if (msg.level >= flush_level) - flush(); + _formatter->format(msg); + for (auto &sink : _sinks) + sink->log(msg); + + const auto flush_level = _flush_level.load(std::memory_order_relaxed); + if (msg.level >= flush_level) + flush(); } inline void spdlog::logger::_set_pattern(const std::string& pattern) { - _formatter = std::make_shared(pattern); + _formatter = std::make_shared(pattern); } inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter) { - _formatter = msg_formatter; + _formatter = msg_formatter; } inline void spdlog::logger::flush() { - for (auto& sink : _sinks) - sink->flush(); + for (auto& sink : _sinks) + sink->flush(); +} + +inline void spdlog::logger::_default_err_handler(const std::string &msg) +{ + auto now = time(nullptr); + if (now - _last_err_time < 60) + return; + 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); + details::log_msg err_msg; + err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::eol); + sinks::stderr_sink_mt::instance()->log(err_msg); + _last_err_time = now; } diff --git a/include/spdlog/details/pattern_formatter_impl.h b/include/spdlog/details/pattern_formatter_impl.h index 5149da94..05b6231a 100644 --- a/include/spdlog/details/pattern_formatter_impl.h +++ b/include/spdlog/details/pattern_formatter_impl.h @@ -21,612 +21,603 @@ namespace spdlog { -namespace details -{ -class flag_formatter -{ -public: - virtual ~flag_formatter() {} - virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0; -}; + namespace details + { + class flag_formatter + { + public: + virtual ~flag_formatter() + {} + virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0; + }; -/////////////////////////////////////////////////////////////////////// -// name & level pattern appenders -/////////////////////////////////////////////////////////////////////// -namespace -{ -class name_formatter :public flag_formatter -{ - void format(details::log_msg& msg, const std::tm&) override - { - msg.formatted << *msg.logger_name; - } -}; -} + /////////////////////////////////////////////////////////////////////// + // name & level pattern appenders + /////////////////////////////////////////////////////////////////////// + namespace + { + 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); - } -}; + // 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); - } -}; + // 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 -/////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + // Date time pattern appenders + /////////////////////////////////////////////////////////////////////// -static const char* ampm(const tm& t) -{ - return t.tm_hour >= 12 ? "PM" : "AM"; -} + 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; -} + 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]; - } -}; + //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]; - } -}; + //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", "June", "July", "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]; - } -}; + //Abbreviated month + static const std::string months[]{ "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "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]; - } -}; + //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 seperated 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 2 ints seperated 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 seperated 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; -} + //write 3 ints seperated 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 :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; - } -}; + //Date and time representation (Thu Aug 23 15:35:46 2014) + class c_formatter: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 :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'); - } -}; + // year - 2 digit + class C_formatter: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 :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, '/'); - } -}; + // Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01 + class D_formatter: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 :public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << tm_time.tm_year + 1900; - } -}; + // year - 4 digit + class Y_formatter: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 :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'); - } -}; + // month 1-12 + class m_formatter: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 :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'); - } -}; + // day of month 1-31 + class d_formatter: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 :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 24 format 0-23 + class H_formatter: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 :public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << fmt::pad(to12h(tm_time), 2, '0'); - } -}; + // hours in 12 format 1-12 + class I_formatter: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 :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'); - } -}; + // minutes 0-59 + class M_formatter: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 :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'); - } -}; + // seconds 0-59 + class S_formatter: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 :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(duration).count() % 1000; - msg.formatted << fmt::pad(static_cast(millis), 3, '0'); - } -}; + // milliseconds + class e_formatter: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(duration).count() % 1000; + msg.formatted << fmt::pad(static_cast(millis), 3, '0'); + } + }; -// microseconds -class f_formatter :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(duration).count() % 1000000; - msg.formatted << fmt::pad(static_cast(micros), 6, '0'); - } -}; + // microseconds + class f_formatter: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(duration).count() % 1000000; + msg.formatted << fmt::pad(static_cast(micros), 6, '0'); + } + }; -// nanoseconds -class F_formatter :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(duration).count() % 1000000000; - msg.formatted << fmt::pad(static_cast(ns), 9, '0'); - } -}; + // nanoseconds + class F_formatter: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(duration).count() % 1000000000; + msg.formatted << fmt::pad(static_cast(ns), 9, '0'); + } + }; -// AM/PM -class p_formatter :public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << ampm(tm_time); - } -}; + // AM/PM + class p_formatter: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 :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); - } -}; + // 12 hour clock 02:55:02 pm + class r_formatter: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 :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, ':'); - } -}; + // 24-hour HH:MM time, equivalent to %H:%M + class R_formatter: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 :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 time format (HH:MM:SS), equivalent to %H:%M:%S + class T_formatter: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 :public flag_formatter -{ -public: - const std::chrono::seconds cache_refresh = std::chrono::seconds(5); + // ISO 8601 offset from UTC in timezone (+-HH:MM) + class z_formatter:public flag_formatter + { + public: + const std::chrono::seconds cache_refresh = std::chrono::seconds(5); - z_formatter() :_last_update(std::chrono::seconds(0)) {} - z_formatter(const z_formatter&) = delete; - z_formatter& operator=(const z_formatter&) = delete; + z_formatter():_last_update(std::chrono::seconds(0)) + {} + 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 - { + void format(details::log_msg& msg, const std::tm& tm_time) override + { #ifdef _WIN32 - int total_minutes = get_cached_offset(msg, tm_time); + 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); + // 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 - int h = total_minutes / 60; - int m = total_minutes % 60; - if (h >= 0) //minus sign will be printed anyway if negative - { - msg.formatted << '+'; - } - pad_n_join(msg.formatted, h, m, ':'); - } -private: - log_clock::time_point _last_update; - int _offset_minutes; - std::mutex _mutex; + int h = total_minutes / 60; + int m = total_minutes % 60; + if (h >= 0) //minus sign will be printed anyway if negative + { + msg.formatted << '+'; + } + pad_n_join(msg.formatted, h, m, ':'); + } + private: + log_clock::time_point _last_update; + int _offset_minutes; + std::mutex _mutex; - int get_cached_offset(const log_msg& msg, const std::tm& tm_time) - { - using namespace std::chrono; - std::lock_guard l(_mutex); - if (msg.time - _last_update >= cache_refresh) - { - _offset_minutes = os::utc_minutes_offset(tm_time); - _last_update = msg.time; - } - return _offset_minutes; - } -}; + int get_cached_offset(const log_msg& msg, const std::tm& tm_time) + { + using namespace std::chrono; + std::lock_guard 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 :public flag_formatter -{ - void format(details::log_msg& msg, const std::tm&) override - { - msg.formatted << msg.thread_id; - } -}; + //Thread id + class t_formatter:public flag_formatter + { + void format(details::log_msg& msg, const std::tm&) override + { + msg.formatted << msg.thread_id; + } + }; -class v_formatter :public flag_formatter -{ - void format(details::log_msg& msg, const std::tm&) override - { - msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size()); - } -}; + class v_formatter: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 :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; -}; + class ch_formatter: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 :public flag_formatter -{ -public: - aggregate_formatter() - {} - void add_ch(char ch) - { - _str += ch; - } - void format(details::log_msg& msg, const std::tm&) override - { - msg.formatted << _str; - } -private: - std::string _str; -}; + //aggregate user chars to display as is + class aggregate_formatter:public flag_formatter + { + public: + aggregate_formatter() + {} + void add_ch(char ch) + { + _str += ch; + } + void format(details::log_msg& msg, const std::tm&) override + { + msg.formatted << _str; + } + private: + std::string _str; + }; -// Full info formatter -// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v -class full_formatter :public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { + // Full info formatter + // pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v + class full_formatter: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(duration).count() % 1000; + auto duration = msg.time.time_since_epoch(); + auto millis = std::chrono::duration_cast(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(millis), - msg.logger_name, - level::to_str(msg.level), - msg.raw.str());*/ + /* 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(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(tm_time.tm_year + 1900) << '-' - << fmt::pad(static_cast(tm_time.tm_mon + 1), 2, '0') << '-' - << fmt::pad(static_cast(tm_time.tm_mday), 2, '0') << ' ' - << fmt::pad(static_cast(tm_time.tm_hour), 2, '0') << ':' - << fmt::pad(static_cast(tm_time.tm_min), 2, '0') << ':' - << fmt::pad(static_cast(tm_time.tm_sec), 2, '0') << '.' - << fmt::pad(static_cast(millis), 3, '0') << "] "; + // Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads) + msg.formatted << '[' << static_cast(tm_time.tm_year + 1900) << '-' + << fmt::pad(static_cast(tm_time.tm_mon + 1), 2, '0') << '-' + << fmt::pad(static_cast(tm_time.tm_mday), 2, '0') << ' ' + << fmt::pad(static_cast(tm_time.tm_hour), 2, '0') << ':' + << fmt::pad(static_cast(tm_time.tm_min), 2, '0') << ':' + << fmt::pad(static_cast(tm_time.tm_sec), 2, '0') << '.' + << fmt::pad(static_cast(millis), 3, '0') << "] "; -//no datetime needed + //no datetime needed #else - (void)tm_time; + (void)tm_time; #endif #ifndef SPDLOG_NO_NAME - msg.formatted << '[' << *msg.logger_name << "] "; + msg.formatted << '[' << *msg.logger_name << "] "; #endif - msg.formatted << '[' << level::to_str(msg.level) << "] "; - msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size()); - } -}; + msg.formatted << '[' << level::to_str(msg.level) << "] "; + msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size()); + } + }; -} + } } /////////////////////////////////////////////////////////////////////////////// // pattern_formatter inline impl /////////////////////////////////////////////////////////////////////////////// inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern) { - compile_pattern(pattern); + compile_pattern(pattern); } inline void spdlog::pattern_formatter::compile_pattern(const std::string& pattern) { - auto end = pattern.end(); - std::unique_ptr 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)); + auto end = pattern.end(); + std::unique_ptr 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 (++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(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)); - } + 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(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.push_back(std::unique_ptr(new details::name_formatter())); - break; + switch (flag) { + // logger name + case 'n': + _formatters.push_back(std::unique_ptr(new details::name_formatter())); + break; - case 'l': - _formatters.push_back(std::unique_ptr(new details::level_formatter())); - break; + case 'l': + _formatters.push_back(std::unique_ptr(new details::level_formatter())); + break; - case 'L': - _formatters.push_back(std::unique_ptr(new details::short_level_formatter())); - break; + case 'L': + _formatters.push_back(std::unique_ptr(new details::short_level_formatter())); + break; - case('t') : - _formatters.push_back(std::unique_ptr(new details::t_formatter())); - break; + case('t'): + _formatters.push_back(std::unique_ptr(new details::t_formatter())); + break; - case('v') : - _formatters.push_back(std::unique_ptr(new details::v_formatter())); - break; + case('v'): + _formatters.push_back(std::unique_ptr(new details::v_formatter())); + break; - case('a') : - _formatters.push_back(std::unique_ptr(new details::a_formatter())); - break; + case('a'): + _formatters.push_back(std::unique_ptr(new details::a_formatter())); + break; - case('A') : - _formatters.push_back(std::unique_ptr(new details::A_formatter())); - break; + case('A'): + _formatters.push_back(std::unique_ptr(new details::A_formatter())); + break; - case('b') : - case('h') : - _formatters.push_back(std::unique_ptr(new details::b_formatter())); - break; + case('b'): + case('h'): + _formatters.push_back(std::unique_ptr(new details::b_formatter())); + break; - case('B') : - _formatters.push_back(std::unique_ptr(new details::B_formatter())); - break; - case('c') : - _formatters.push_back(std::unique_ptr(new details::c_formatter())); - break; + case('B'): + _formatters.push_back(std::unique_ptr(new details::B_formatter())); + break; + case('c'): + _formatters.push_back(std::unique_ptr(new details::c_formatter())); + break; - case('C') : - _formatters.push_back(std::unique_ptr(new details::C_formatter())); - break; + case('C'): + _formatters.push_back(std::unique_ptr(new details::C_formatter())); + break; - case('Y') : - _formatters.push_back(std::unique_ptr(new details::Y_formatter())); - break; + case('Y'): + _formatters.push_back(std::unique_ptr(new details::Y_formatter())); + break; - case('D') : - case('x') : + case('D'): + case('x'): - _formatters.push_back(std::unique_ptr(new details::D_formatter())); - break; + _formatters.push_back(std::unique_ptr(new details::D_formatter())); + break; - case('m') : - _formatters.push_back(std::unique_ptr(new details::m_formatter())); - break; + case('m'): + _formatters.push_back(std::unique_ptr(new details::m_formatter())); + break; - case('d') : - _formatters.push_back(std::unique_ptr(new details::d_formatter())); - break; + case('d'): + _formatters.push_back(std::unique_ptr(new details::d_formatter())); + break; - case('H') : - _formatters.push_back(std::unique_ptr(new details::H_formatter())); - break; + case('H'): + _formatters.push_back(std::unique_ptr(new details::H_formatter())); + break; - case('I') : - _formatters.push_back(std::unique_ptr(new details::I_formatter())); - break; + case('I'): + _formatters.push_back(std::unique_ptr(new details::I_formatter())); + break; - case('M') : - _formatters.push_back(std::unique_ptr(new details::M_formatter())); - break; + case('M'): + _formatters.push_back(std::unique_ptr(new details::M_formatter())); + break; - case('S') : - _formatters.push_back(std::unique_ptr(new details::S_formatter())); - break; + case('S'): + _formatters.push_back(std::unique_ptr(new details::S_formatter())); + break; - case('e') : - _formatters.push_back(std::unique_ptr(new details::e_formatter())); - break; + case('e'): + _formatters.push_back(std::unique_ptr(new details::e_formatter())); + break; - case('f') : - _formatters.push_back(std::unique_ptr(new details::f_formatter())); - break; - case('F') : - _formatters.push_back(std::unique_ptr(new details::F_formatter())); - break; + case('f'): + _formatters.push_back(std::unique_ptr(new details::f_formatter())); + break; + case('F'): + _formatters.push_back(std::unique_ptr(new details::F_formatter())); + break; - case('p') : - _formatters.push_back(std::unique_ptr(new details::p_formatter())); - break; + case('p'): + _formatters.push_back(std::unique_ptr(new details::p_formatter())); + break; - case('r') : - _formatters.push_back(std::unique_ptr(new details::r_formatter())); - break; + case('r'): + _formatters.push_back(std::unique_ptr(new details::r_formatter())); + break; - case('R') : - _formatters.push_back(std::unique_ptr(new details::R_formatter())); - break; + case('R'): + _formatters.push_back(std::unique_ptr(new details::R_formatter())); + break; - case('T') : - case('X') : - _formatters.push_back(std::unique_ptr(new details::T_formatter())); - break; + case('T'): + case('X'): + _formatters.push_back(std::unique_ptr(new details::T_formatter())); + break; - case('z') : - _formatters.push_back(std::unique_ptr(new details::z_formatter())); - break; + case('z'): + _formatters.push_back(std::unique_ptr(new details::z_formatter())); + break; - case ('+'): - _formatters.push_back(std::unique_ptr(new details::full_formatter())); - break; + case ('+'): + _formatters.push_back(std::unique_ptr(new details::full_formatter())); + break; - default: //Unkown flag appears as is - _formatters.push_back(std::unique_ptr(new details::ch_formatter('%'))); - _formatters.push_back(std::unique_ptr(new details::ch_formatter(flag))); - break; - } + default: //Unkown flag appears as is + _formatters.push_back(std::unique_ptr(new details::ch_formatter('%'))); + _formatters.push_back(std::unique_ptr(new details::ch_formatter(flag))); + break; + } } inline void spdlog::pattern_formatter::format(details::log_msg& msg) { - try - { + #ifndef SPDLOG_NO_DATETIME - auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time)); + auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time)); #else - std::tm tm_time; + std::tm tm_time; #endif - for (auto &f : _formatters) - { - f->format(msg, tm_time); - } - //write eol - msg.formatted.write(details::os::eol, details::os::eol_size); - } - catch(const fmt::FormatError& e) - { - throw spdlog_ex(fmt::format("formatting error while processing format string: {}", e.what())); - } + for (auto &f : _formatters) { + f->format(msg, tm_time); + } + //write eol + msg.formatted.write(details::os::eol, details::os::eol_size); } diff --git a/include/spdlog/details/registry.h b/include/spdlog/details/registry.h index 7d744f89..94fa1d9b 100644 --- a/include/spdlog/details/registry.h +++ b/include/spdlog/details/registry.h @@ -60,7 +60,12 @@ public: if (_formatter) new_logger->set_formatter(_formatter); + if (_err_handler) + new_logger->set_error_handler(_err_handler); + new_logger->set_level(_level); + + //Add to registry _loggers[logger_name] = new_logger; return new_logger; @@ -112,6 +117,13 @@ public: _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& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb) { std::lock_guard lock(_mutex); @@ -149,6 +161,7 @@ private: std::unordered_map > _loggers; formatter_ptr _formatter; level::level_enum _level = level::info; + 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; diff --git a/include/spdlog/details/spdlog_impl.h b/include/spdlog/details/spdlog_impl.h index 59227e9b..6724e0fb 100644 --- a/include/spdlog/details/spdlog_impl.h +++ b/include/spdlog/details/spdlog_impl.h @@ -147,6 +147,13 @@ inline void spdlog::set_level(level::level_enum log_level) return details::registry::instance().set_level(log_level); } +inline void spdlog::set_error_handler(log_err_handler handler) +{ + return details::registry::instance().set_error_handler(handler); +} + + + inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb) { diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h index 63c6598b..b4c15f13 100644 --- a/include/spdlog/logger.h +++ b/include/spdlog/logger.h @@ -19,7 +19,6 @@ #include #include - namespace spdlog { @@ -52,14 +51,17 @@ public: template void warn(const T&); template void error(const T&); template void critical(const T&); - - + bool should_log(level::level_enum) const; void set_level(level::level_enum); level::level_enum level() const; const std::string& name() const; void set_pattern(const std::string&); - void set_formatter(formatter_ptr); + void set_formatter(formatter_ptr); + + // error handler + void set_error_handler(log_err_handler); + log_err_handler error_handler(); // automatically call flush() if message level >= log_level void flush_on(level::level_enum log_level); @@ -70,11 +72,16 @@ protected: virtual void _set_pattern(const std::string&); virtual void _set_formatter(formatter_ptr); + // default error handler: print the error to stderr with the max rate of 1 message/minute + virtual void _default_err_handler(const std::string &msg); + const std::string _name; std::vector _sinks; formatter_ptr _formatter; spdlog::level_t _level; spdlog::level_t _flush_level; + log_err_handler _err_handler; + std::atomic _last_err_time; }; } diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h index ca391d16..8c936c7e 100644 --- a/include/spdlog/spdlog.h +++ b/include/spdlog/spdlog.h @@ -41,6 +41,11 @@ void set_formatter(formatter_ptr f); // void set_level(level::level_enum log_level); +// +// Set global error handler +// +void set_error_handler(log_err_handler); + // // Turn on async mode (off by default) and set the queue size for each async_logger. // effective only for loggers created after this call. diff --git a/tests/errors.cpp b/tests/errors.cpp new file mode 100644 index 00000000..032aeb1d --- /dev/null +++ b/tests/errors.cpp @@ -0,0 +1,61 @@ +/* +* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE +*/ +#include "includes.h" + +#include + + +TEST_CASE("default_error_handler", "[errors]]") +{ + prepare_logdir(); + std::string filename = "logs/simple_log.txt"; + + auto logger = spdlog::create("logger", filename, true); + logger->set_pattern("%v"); + logger->info("Test message {} {}", 1); + logger->info("Test message {}", 2); + logger->flush(); + + REQUIRE(file_contents(filename) == std::string("Test message 2\n")); + REQUIRE(count_lines(filename) == 1); +} + + +struct custom_ex{}; +TEST_CASE("custom_error_handler", "[errors]]") +{ + prepare_logdir(); + std::string filename = "logs/simple_log.txt"; + auto logger = spdlog::create("logger", filename, true); + logger->set_error_handler([=](const std::string& msg) { + throw custom_ex(); + }); + logger->info("Good message #1"); + REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex); + logger->info("Good message #2"); + REQUIRE(count_lines(filename) == 2); +} + +TEST_CASE("async_error_handler", "[errors]]") +{ + prepare_logdir(); + std::string err_msg("log failed with some msg"); + spdlog::set_async_mode(128); + std::string filename = "logs/simple_async_log.txt"; + { + auto logger = spdlog::create("logger", filename, true); + logger->set_error_handler([=](const std::string& msg) { + std::ofstream ofs("logs/custom_err.txt"); + if (!ofs) throw std::runtime_error("Failed open logs/custom_err.txt"); + ofs << err_msg; + }); + logger->info("Good message #1"); + logger->info("Bad format msg {} {}", "xxx"); + logger->info("Good message #2"); + spdlog::drop("logger"); //force logger to drain the queue and shutdown + spdlog::set_sync_mode(); + } + REQUIRE(count_lines(filename) == 2); + REQUIRE(file_contents("logs/custom_err.txt") == err_msg); +} diff --git a/tests/format.cpp b/tests/format.cpp index 19d6bc52..adb8ba8b 100644 --- a/tests/format.cpp +++ b/tests/format.cpp @@ -49,24 +49,7 @@ TEST_CASE("log_levels", "[log_levels]") REQUIRE(log_info("Hello", spdlog::level::trace) == "Hello"); } -TEST_CASE("invalid_format", "[format]") -{ - - using namespace spdlog::sinks; - spdlog::logger null_logger("null_logger", std::make_shared()); - REQUIRE_THROWS_AS( - null_logger.info("{} {}", "first"), - spdlog::spdlog_ex); - - REQUIRE_THROWS_AS( - null_logger.info("{0:f}", "aads"), - spdlog::spdlog_ex); - - REQUIRE_THROWS_AS( - null_logger.info("{0:kk}", 123), - spdlog::spdlog_ex); - -} + diff --git a/tests/tests.vcxproj b/tests/tests.vcxproj index f05c1628..be667a68 100644 --- a/tests/tests.vcxproj +++ b/tests/tests.vcxproj @@ -125,6 +125,7 @@ + diff --git a/tests/tests.vcxproj.filters b/tests/tests.vcxproj.filters index 72ca5483..b9ede4e7 100644 --- a/tests/tests.vcxproj.filters +++ b/tests/tests.vcxproj.filters @@ -33,6 +33,9 @@ Source Files + + Source Files +