no exceptions while logging
This commit is contained in:
		
							parent
							
								
									2fc332abdc
								
							
						
					
					
						commit
						39cdd08a54
					
				
							
								
								
									
										147
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										147
									
								
								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 <iostream> | ||||
| // | ||||
| // 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 <iostream> | ||||
| #include <memory> | ||||
| 
 | ||||
| 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<typename OStream> | ||||
|     friend OStream& operator<<(OStream& os, const my_type &c) | ||||
|     { | ||||
|         return os << "[my_type i="<<c.i << "]"; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| #include <spdlog/fmt/ostr.h> // 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); | ||||
| } | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
|  | ||||
| @ -7,13 +7,13 @@ | ||||
| //
 | ||||
| #include "spdlog/spdlog.h" | ||||
| 
 | ||||
| #include <cstdlib> // EXIT_FAILURE
 | ||||
| #include <iostream> | ||||
| #include <memory> | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -11,6 +11,8 @@ | ||||
| #include <memory> | ||||
| #include <atomic> | ||||
| #include <exception> | ||||
| #include<functional> | ||||
| 
 | ||||
| #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) | ||||
| #include <codecvt> | ||||
| #include <locale> | ||||
| @ -58,6 +60,7 @@ using level_t = details::null_atomic_int; | ||||
| using level_t = std::atomic_int; | ||||
| #endif | ||||
| 
 | ||||
| using log_err_handler = std::function<void(const std::string &err_msg)>; | ||||
| 
 | ||||
| //Log level enum
 | ||||
| namespace level | ||||
|  | ||||
| @ -120,6 +120,7 @@ public: | ||||
|     async_log_helper(formatter_ptr formatter, | ||||
|                      const 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, | ||||
|                      const std::function<void()>& 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<spdlog_ex> _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<sink_ptr>& sinks, | ||||
|     size_t queue_size, | ||||
| 	log_err_handler err_handler, | ||||
|     const async_overflow_policy overflow_policy, | ||||
|     const std::function<void()>& 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<spdlog_ex>(std::string("async_logger worker thread exception: ") + ex.what()); | ||||
|     } | ||||
|     catch (...) | ||||
|     { | ||||
|         _last_workerthread_ex = std::make_shared<spdlog_ex>("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; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -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<void()>& 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"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -6,35 +6,39 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <spdlog/logger.h> | ||||
| #include <spdlog/sinks/stdout_sinks.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(const std::string& logger_name, const It& begin, const It& end) : | ||||
|     _name(logger_name), | ||||
|     _sinks(begin, end), | ||||
|     _formatter(std::make_shared<pattern_formatter>("%+")) | ||||
| { | ||||
| 
 | ||||
|     // 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<pattern_formatter>("%+")) | ||||
| {	 | ||||
| 	_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 <typename... Args> | ||||
| 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 <typename... Args> | ||||
| 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<typename T> | ||||
| 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 <typename... Args> | ||||
| inline void spdlog::logger::trace(const char* fmt, const Args&... args) | ||||
| { | ||||
|     log(level::trace, fmt, args...); | ||||
| 	log(level::trace, fmt, args...); | ||||
| } | ||||
| 
 | ||||
| template <typename... Args> | ||||
| inline void spdlog::logger::debug(const char* fmt, const Args&... args) | ||||
| { | ||||
|     log(level::debug, fmt, args...); | ||||
| 	log(level::debug, fmt, args...); | ||||
| } | ||||
| 
 | ||||
| template <typename... Args> | ||||
| inline void spdlog::logger::info(const char* fmt, const Args&... args) | ||||
| { | ||||
|     log(level::info, fmt, args...); | ||||
| 	log(level::info, fmt, args...); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename... Args> | ||||
| inline void spdlog::logger::warn(const char* fmt, const Args&... args) | ||||
| { | ||||
|     log(level::warn, fmt, args...); | ||||
| 	log(level::warn, fmt, args...); | ||||
| } | ||||
| 
 | ||||
| template <typename... Args> | ||||
| inline void spdlog::logger::error(const char* fmt, const Args&... args) | ||||
| { | ||||
|     log(level::err, fmt, args...); | ||||
| 	log(level::err, fmt, args...); | ||||
| } | ||||
| 
 | ||||
| template <typename... Args> | ||||
| inline void spdlog::logger::critical(const char* fmt, const Args&... args) | ||||
| { | ||||
|     log(level::critical, fmt, args...); | ||||
| 	log(level::critical, fmt, args...); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template<typename T> | ||||
| inline void spdlog::logger::trace(const T& msg) | ||||
| { | ||||
|     log(level::trace, msg); | ||||
| 	log(level::trace, msg); | ||||
| } | ||||
| 
 | ||||
| template<typename T> | ||||
| inline void spdlog::logger::debug(const T& msg) | ||||
| { | ||||
|     log(level::debug, msg); | ||||
| 	log(level::debug, msg); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template<typename T> | ||||
| inline void spdlog::logger::info(const T& msg) | ||||
| { | ||||
|     log(level::info, msg); | ||||
| 	log(level::info, msg); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template<typename T> | ||||
| inline void spdlog::logger::warn(const T& msg) | ||||
| { | ||||
|     log(level::warn, msg); | ||||
| 	log(level::warn, msg); | ||||
| } | ||||
| 
 | ||||
| template<typename T> | ||||
| inline void spdlog::logger::error(const T& msg) | ||||
| { | ||||
|     log(level::err, msg); | ||||
| 	log(level::err, msg); | ||||
| } | ||||
| 
 | ||||
| template<typename T> | ||||
| 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<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed)); | ||||
| 	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); | ||||
| 	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>(pattern); | ||||
| 	_formatter = std::make_shared<pattern_formatter>(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; | ||||
| } | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -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<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb) | ||||
|     { | ||||
|         std::lock_guard<Mutex> lock(_mutex); | ||||
| @ -149,6 +161,7 @@ private: | ||||
|     std::unordered_map <std::string, std::shared_ptr<logger>> _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; | ||||
|  | ||||
| @ -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<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb) | ||||
| { | ||||
|  | ||||
| @ -19,7 +19,6 @@ | ||||
| #include <memory> | ||||
| #include <string> | ||||
| 
 | ||||
| 
 | ||||
| namespace spdlog | ||||
| { | ||||
| 
 | ||||
| @ -52,14 +51,17 @@ public: | ||||
|     template <typename T> void warn(const T&); | ||||
|     template <typename T> void error(const T&); | ||||
|     template <typename T> 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<sink_ptr> _sinks; | ||||
|     formatter_ptr _formatter; | ||||
|     spdlog::level_t _level; | ||||
|     spdlog::level_t _flush_level; | ||||
| 	log_err_handler _err_handler;	 | ||||
| 	std::atomic<time_t> _last_err_time; | ||||
| }; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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.
 | ||||
|  | ||||
							
								
								
									
										61
									
								
								tests/errors.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								tests/errors.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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<iostream> | ||||
| 
 | ||||
| 
 | ||||
| TEST_CASE("default_error_handler", "[errors]]") | ||||
| { | ||||
| 	prepare_logdir(); | ||||
| 	std::string filename = "logs/simple_log.txt"; | ||||
| 
 | ||||
| 	auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("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<spdlog::sinks::simple_file_sink_mt>("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<spdlog::sinks::simple_file_sink_mt>("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); | ||||
| } | ||||
| @ -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<null_sink_st>()); | ||||
|     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); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -125,6 +125,7 @@ | ||||
|     </Link> | ||||
|   </ItemDefinitionGroup> | ||||
|   <ItemGroup> | ||||
|     <ClCompile Include="errors.cpp" /> | ||||
|     <ClCompile Include="file_helper.cpp" /> | ||||
|     <ClCompile Include="file_log.cpp" /> | ||||
|     <ClCompile Include="format.cpp" /> | ||||
|  | ||||
| @ -33,6 +33,9 @@ | ||||
|     <ClCompile Include="utils.cpp"> | ||||
|       <Filter>Source Files</Filter> | ||||
|     </ClCompile> | ||||
|     <ClCompile Include="errors.cpp"> | ||||
|       <Filter>Source Files</Filter> | ||||
|     </ClCompile> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClInclude Include="includes.h"> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user