astyle
This commit is contained in:
		
							parent
							
								
									5a8cecdfb6
								
							
						
					
					
						commit
						0c276beaaf
					
				| @ -64,9 +64,9 @@ public: | ||||
|     //Warning: this can potentialy last forever as we wait it to complete
 | ||||
|     void flush() override; | ||||
| 
 | ||||
| 	// Error handler
 | ||||
| 	virtual void set_error_handler(log_err_handler) override; | ||||
| 	virtual log_err_handler error_handler() override; | ||||
|     // Error handler
 | ||||
|     virtual void set_error_handler(log_err_handler) override; | ||||
|     virtual log_err_handler error_handler() override; | ||||
| 
 | ||||
| protected: | ||||
|     void _sink_it(details::log_msg& msg) override; | ||||
|  | ||||
| @ -32,244 +32,252 @@ | ||||
| 
 | ||||
| namespace spdlog | ||||
| { | ||||
| 	namespace details | ||||
| 	{ | ||||
| namespace details | ||||
| { | ||||
| 
 | ||||
| 		class async_log_helper | ||||
| 		{ | ||||
| 			// Async msg to move to/from the queue
 | ||||
| 			// Movable only. should never be copied
 | ||||
| 			enum class async_msg_type | ||||
| 			{ | ||||
| 				log, | ||||
| 				flush, | ||||
| 				terminate | ||||
| 			}; | ||||
| 			struct async_msg | ||||
| 			{ | ||||
| 				std::string logger_name; | ||||
| 				level::level_enum level; | ||||
| 				log_clock::time_point time; | ||||
| 				size_t thread_id; | ||||
| 				std::string txt; | ||||
| 				async_msg_type msg_type; | ||||
| class async_log_helper | ||||
| { | ||||
|     // Async msg to move to/from the queue
 | ||||
|     // Movable only. should never be copied
 | ||||
|     enum class async_msg_type | ||||
|     { | ||||
|         log, | ||||
|         flush, | ||||
|         terminate | ||||
|     }; | ||||
|     struct async_msg | ||||
|     { | ||||
|         std::string logger_name; | ||||
|         level::level_enum level; | ||||
|         log_clock::time_point time; | ||||
|         size_t thread_id; | ||||
|         std::string txt; | ||||
|         async_msg_type msg_type; | ||||
| 
 | ||||
| 				async_msg() = default; | ||||
| 				~async_msg() = default; | ||||
|         async_msg() = default; | ||||
|         ~async_msg() = default; | ||||
| 
 | ||||
| 
 | ||||
| 				async_msg(async_msg&& other) SPDLOG_NOEXCEPT: | ||||
| 				logger_name(std::move(other.logger_name)), | ||||
| 					level(std::move(other.level)), | ||||
| 					time(std::move(other.time)), | ||||
| 					txt(std::move(other.txt)), | ||||
| 					msg_type(std::move(other.msg_type)) | ||||
| 				{} | ||||
| async_msg(async_msg&& other) SPDLOG_NOEXCEPT: | ||||
|         logger_name(std::move(other.logger_name)), | ||||
|                     level(std::move(other.level)), | ||||
|                     time(std::move(other.time)), | ||||
|                     txt(std::move(other.txt)), | ||||
|                     msg_type(std::move(other.msg_type)) | ||||
|         {} | ||||
| 
 | ||||
| 				async_msg(async_msg_type m_type):msg_type(m_type) | ||||
| 				{} | ||||
|         async_msg(async_msg_type m_type):msg_type(m_type) | ||||
|         {} | ||||
| 
 | ||||
| 				async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT | ||||
| 				{ | ||||
| 					logger_name = std::move(other.logger_name); | ||||
| 					level = other.level; | ||||
| 					time = std::move(other.time); | ||||
| 					thread_id = other.thread_id; | ||||
| 					txt = std::move(other.txt); | ||||
| 					msg_type = other.msg_type; | ||||
| 					return *this; | ||||
| 				} | ||||
|         async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT | ||||
|         { | ||||
|             logger_name = std::move(other.logger_name); | ||||
|             level = other.level; | ||||
|             time = std::move(other.time); | ||||
|             thread_id = other.thread_id; | ||||
|             txt = std::move(other.txt); | ||||
|             msg_type = other.msg_type; | ||||
|             return *this; | ||||
|         } | ||||
| 
 | ||||
| 				// never copy or assign. should only be moved..
 | ||||
| 				async_msg(const async_msg&) = delete; | ||||
| 				async_msg& operator=(const async_msg& other) = delete; | ||||
|         // never copy or assign. should only be moved..
 | ||||
|         async_msg(const async_msg&) = delete; | ||||
|         async_msg& operator=(const async_msg& other) = delete; | ||||
| 
 | ||||
| 				// construct from log_msg
 | ||||
| 				async_msg(const details::log_msg& m): | ||||
| 					level(m.level), | ||||
| 					time(m.time), | ||||
| 					thread_id(m.thread_id), | ||||
| 					txt(m.raw.data(), m.raw.size()), | ||||
| 					msg_type(async_msg_type::log) | ||||
| 				{ | ||||
|         // construct from log_msg
 | ||||
|         async_msg(const details::log_msg& m): | ||||
|             level(m.level), | ||||
|             time(m.time), | ||||
|             thread_id(m.thread_id), | ||||
|             txt(m.raw.data(), m.raw.size()), | ||||
|             msg_type(async_msg_type::log) | ||||
|         { | ||||
| #ifndef SPDLOG_NO_NAME | ||||
| 					logger_name = *m.logger_name; | ||||
|             logger_name = *m.logger_name; | ||||
| #endif | ||||
| 				} | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| 				// copy into log_msg
 | ||||
| 				void fill_log_msg(log_msg &msg) | ||||
| 				{ | ||||
| 					msg.logger_name = &logger_name; | ||||
| 					msg.level = level; | ||||
| 					msg.time = time; | ||||
| 					msg.thread_id = thread_id; | ||||
| 					msg.raw << txt; | ||||
| 				} | ||||
| 			}; | ||||
|         // copy into log_msg
 | ||||
|         void fill_log_msg(log_msg &msg) | ||||
|         { | ||||
|             msg.logger_name = &logger_name; | ||||
|             msg.level = level; | ||||
|             msg.time = time; | ||||
|             msg.thread_id = thread_id; | ||||
|             msg.raw << txt; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
| 		public: | ||||
| public: | ||||
| 
 | ||||
| 			using item_type = async_msg; | ||||
| 			using q_type = details::mpmc_bounded_queue<item_type>; | ||||
|     using item_type = async_msg; | ||||
|     using q_type = details::mpmc_bounded_queue<item_type>; | ||||
| 
 | ||||
| 			using clock = std::chrono::steady_clock; | ||||
|     using clock = std::chrono::steady_clock; | ||||
| 
 | ||||
| 
 | ||||
| 			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(), | ||||
| 				const std::function<void()>& worker_teardown_cb = nullptr); | ||||
|     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(), | ||||
|                      const std::function<void()>& worker_teardown_cb = nullptr); | ||||
| 
 | ||||
| 			void log(const details::log_msg& msg); | ||||
|     void log(const details::log_msg& msg); | ||||
| 
 | ||||
| 			// stop logging and join the back thread
 | ||||
| 			~async_log_helper(); | ||||
|     // stop logging and join the back thread
 | ||||
|     ~async_log_helper(); | ||||
| 
 | ||||
| 			void set_formatter(formatter_ptr); | ||||
|     void set_formatter(formatter_ptr); | ||||
| 
 | ||||
| 			void flush(bool wait_for_q); | ||||
|     void flush(bool wait_for_q); | ||||
| 
 | ||||
| 			void set_error_handler(spdlog::log_err_handler err_handler); | ||||
|     void set_error_handler(spdlog::log_err_handler err_handler); | ||||
| 
 | ||||
| 		private: | ||||
| 			formatter_ptr _formatter; | ||||
| 			std::vector<std::shared_ptr<sinks::sink>> _sinks; | ||||
| private: | ||||
|     formatter_ptr _formatter; | ||||
|     std::vector<std::shared_ptr<sinks::sink>> _sinks; | ||||
| 
 | ||||
| 			// queue of messages to log
 | ||||
| 			q_type _q; | ||||
|     // queue of messages to log
 | ||||
|     q_type _q; | ||||
| 
 | ||||
| 			log_err_handler _err_handler; | ||||
|     log_err_handler _err_handler; | ||||
| 
 | ||||
| 			bool _flush_requested; | ||||
|     bool _flush_requested; | ||||
| 
 | ||||
| 			bool _terminate_requested; | ||||
|     bool _terminate_requested; | ||||
| 
 | ||||
| 
 | ||||
| 			// overflow policy
 | ||||
| 			const async_overflow_policy _overflow_policy; | ||||
|     // overflow policy
 | ||||
|     const async_overflow_policy _overflow_policy; | ||||
| 
 | ||||
| 			// worker thread warmup callback - one can set thread priority, affinity, etc
 | ||||
| 			const std::function<void()> _worker_warmup_cb; | ||||
|     // worker thread warmup callback - one can set thread priority, affinity, etc
 | ||||
|     const std::function<void()> _worker_warmup_cb; | ||||
| 
 | ||||
| 			// auto periodic sink flush parameter
 | ||||
| 			const std::chrono::milliseconds _flush_interval_ms; | ||||
|     // auto periodic sink flush parameter
 | ||||
|     const std::chrono::milliseconds _flush_interval_ms; | ||||
| 
 | ||||
| 			// worker thread teardown callback
 | ||||
| 			const std::function<void()> _worker_teardown_cb; | ||||
|     // worker thread teardown callback
 | ||||
|     const std::function<void()> _worker_teardown_cb; | ||||
| 
 | ||||
| 			// worker thread
 | ||||
| 			std::thread _worker_thread; | ||||
|     // worker thread
 | ||||
|     std::thread _worker_thread; | ||||
| 
 | ||||
| 			void push_msg(async_msg&& new_msg); | ||||
|     void push_msg(async_msg&& new_msg); | ||||
| 
 | ||||
| 			// worker thread main loop
 | ||||
| 			void worker_loop(); | ||||
|     // worker thread main loop
 | ||||
|     void worker_loop(); | ||||
| 
 | ||||
| 			// pop next message from the queue and process it. will set the last_pop to the pop time
 | ||||
| 			// return false if termination of the queue is required
 | ||||
| 			bool process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush); | ||||
|     // pop next message from the queue and process it. will set the last_pop to the pop time
 | ||||
|     // return false if termination of the queue is required
 | ||||
|     bool process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush); | ||||
| 
 | ||||
| 			void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush); | ||||
|     void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush); | ||||
| 
 | ||||
| 			// 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); | ||||
|     // 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); | ||||
| 
 | ||||
| 			// wait until the queue is empty
 | ||||
| 			void wait_empty_q(); | ||||
|     // wait until the queue is empty
 | ||||
|     void wait_empty_q(); | ||||
| 
 | ||||
| 		}; | ||||
| 	} | ||||
| }; | ||||
| } | ||||
| } | ||||
| 
 | ||||
| ///////////////////////////////////////////////////////////////////////////////
 | ||||
| // async_sink class implementation
 | ||||
| ///////////////////////////////////////////////////////////////////////////////
 | ||||
| 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, | ||||
| 	const std::function<void()>& worker_teardown_cb): | ||||
| 	_formatter(formatter), | ||||
| 	_sinks(sinks), | ||||
| 	_q(queue_size), | ||||
| 	_err_handler(err_handler), | ||||
| 	_flush_requested(false), | ||||
| 	_terminate_requested(false), | ||||
| 	_overflow_policy(overflow_policy), | ||||
| 	_worker_warmup_cb(worker_warmup_cb), | ||||
| 	_flush_interval_ms(flush_interval_ms), | ||||
| 	_worker_teardown_cb(worker_teardown_cb), | ||||
| 	_worker_thread(&async_log_helper::worker_loop, this) | ||||
|     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, | ||||
|     const std::function<void()>& worker_teardown_cb): | ||||
|     _formatter(formatter), | ||||
|     _sinks(sinks), | ||||
|     _q(queue_size), | ||||
|     _err_handler(err_handler), | ||||
|     _flush_requested(false), | ||||
|     _terminate_requested(false), | ||||
|     _overflow_policy(overflow_policy), | ||||
|     _worker_warmup_cb(worker_warmup_cb), | ||||
|     _flush_interval_ms(flush_interval_ms), | ||||
|     _worker_teardown_cb(worker_teardown_cb), | ||||
|     _worker_thread(&async_log_helper::worker_loop, this) | ||||
| {} | ||||
| 
 | ||||
| // Send to the worker thread termination message(level=off)
 | ||||
| // and wait for it to finish gracefully
 | ||||
| inline spdlog::details::async_log_helper::~async_log_helper() | ||||
| { | ||||
| 	try { | ||||
| 		push_msg(async_msg(async_msg_type::terminate)); | ||||
| 		_worker_thread.join(); | ||||
| 	} | ||||
| 	catch (...) // don't crash in destructor
 | ||||
| 	{ | ||||
| 	} | ||||
|     try | ||||
|     { | ||||
|         push_msg(async_msg(async_msg_type::terminate)); | ||||
|         _worker_thread.join(); | ||||
|     } | ||||
|     catch (...) // don't crash in destructor
 | ||||
|     { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| //Try to push and block until succeeded (if the policy is not to discard when the queue is full)
 | ||||
| inline void spdlog::details::async_log_helper::log(const details::log_msg& msg) | ||||
| { | ||||
| 	push_msg(async_msg(msg)); | ||||
|     push_msg(async_msg(msg)); | ||||
| } | ||||
| 
 | ||||
| inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg) | ||||
| { | ||||
| 	if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg) { | ||||
| 		auto last_op_time = details::os::now(); | ||||
| 		auto now = last_op_time; | ||||
| 		do { | ||||
| 			now = details::os::now(); | ||||
| 			sleep_or_yield(now, last_op_time); | ||||
| 		} while (!_q.enqueue(std::move(new_msg))); | ||||
| 	} | ||||
|     if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg) | ||||
|     { | ||||
|         auto last_op_time = details::os::now(); | ||||
|         auto now = last_op_time; | ||||
|         do | ||||
|         { | ||||
|             now = details::os::now(); | ||||
|             sleep_or_yield(now, last_op_time); | ||||
|         } | ||||
|         while (!_q.enqueue(std::move(new_msg))); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // optionally wait for the queue be empty and request flush from the sinks
 | ||||
| inline void spdlog::details::async_log_helper::flush(bool wait_for_q) | ||||
| { | ||||
| 	push_msg(async_msg(async_msg_type::flush)); | ||||
| 	if (wait_for_q) | ||||
| 		wait_empty_q(); //return only make after the above flush message was processed
 | ||||
|     push_msg(async_msg(async_msg_type::flush)); | ||||
|     if (wait_for_q) | ||||
|         wait_empty_q(); //return only make after the above flush message was processed
 | ||||
| } | ||||
| 
 | ||||
| inline void spdlog::details::async_log_helper::worker_loop() | ||||
| { | ||||
| 	if (_worker_warmup_cb) _worker_warmup_cb(); | ||||
| 	auto last_pop = details::os::now(); | ||||
| 	auto last_flush = last_pop; | ||||
| 	auto active = true; | ||||
| 	while (active) { | ||||
| 		try { | ||||
| 			active = process_next_msg(last_pop, last_flush); | ||||
| 		} | ||||
| 		catch (const std::exception &ex) { | ||||
| 			_err_handler(ex.what()); | ||||
| 		} | ||||
| 		catch (...) { | ||||
| 			_err_handler("Unknown exception"); | ||||
| 		} | ||||
| 	} | ||||
| 	if (_worker_teardown_cb) _worker_teardown_cb(); | ||||
|     if (_worker_warmup_cb) _worker_warmup_cb(); | ||||
|     auto last_pop = details::os::now(); | ||||
|     auto last_flush = last_pop; | ||||
|     auto active = true; | ||||
|     while (active) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             active = process_next_msg(last_pop, last_flush); | ||||
|         } | ||||
|         catch (const std::exception &ex) | ||||
|         { | ||||
|             _err_handler(ex.what()); | ||||
|         } | ||||
|         catch (...) | ||||
|         { | ||||
|             _err_handler("Unknown exception"); | ||||
|         } | ||||
|     } | ||||
|     if (_worker_teardown_cb) _worker_teardown_cb(); | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -278,98 +286,105 @@ inline void spdlog::details::async_log_helper::worker_loop() | ||||
| // return true if this thread should still be active (while no terminate msg was received)
 | ||||
| inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush) | ||||
| { | ||||
| 	async_msg incoming_async_msg; | ||||
|     async_msg incoming_async_msg; | ||||
| 
 | ||||
| 	if (_q.dequeue(incoming_async_msg)) { | ||||
| 		last_pop = details::os::now(); | ||||
| 		switch (incoming_async_msg.msg_type) { | ||||
| 		case async_msg_type::flush: | ||||
| 			_flush_requested = true; | ||||
| 			break; | ||||
|     if (_q.dequeue(incoming_async_msg)) | ||||
|     { | ||||
|         last_pop = details::os::now(); | ||||
|         switch (incoming_async_msg.msg_type) | ||||
|         { | ||||
|         case async_msg_type::flush: | ||||
|             _flush_requested = true; | ||||
|             break; | ||||
| 
 | ||||
| 		case async_msg_type::terminate: | ||||
| 			_flush_requested = true; | ||||
| 			_terminate_requested = true; | ||||
| 			break; | ||||
|         case async_msg_type::terminate: | ||||
|             _flush_requested = true; | ||||
|             _terminate_requested = true; | ||||
|             break; | ||||
| 
 | ||||
| 		default: | ||||
| 			log_msg incoming_log_msg; | ||||
| 			incoming_async_msg.fill_log_msg(incoming_log_msg); | ||||
| 			_formatter->format(incoming_log_msg); | ||||
| 			for (auto &s : _sinks) { | ||||
| 				if (s->should_log(incoming_log_msg.level)) { | ||||
| 					s->log(incoming_log_msg); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		return true; | ||||
| 	} | ||||
|         default: | ||||
|             log_msg incoming_log_msg; | ||||
|             incoming_async_msg.fill_log_msg(incoming_log_msg); | ||||
|             _formatter->format(incoming_log_msg); | ||||
|             for (auto &s : _sinks) | ||||
|             { | ||||
|                 if (s->should_log(incoming_log_msg.level)) | ||||
|                 { | ||||
|                     s->log(incoming_log_msg); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
| 	// Handle empty queue..
 | ||||
| 	// This is the only place where the queue can terminate or flush to avoid losing messages already in the queue
 | ||||
| 	else { | ||||
| 		auto now = details::os::now(); | ||||
| 		handle_flush_interval(now, last_flush); | ||||
| 		sleep_or_yield(now, last_pop); | ||||
| 		return !_terminate_requested; | ||||
| 	} | ||||
|     // Handle empty queue..
 | ||||
|     // This is the only place where the queue can terminate or flush to avoid losing messages already in the queue
 | ||||
|     else | ||||
|     { | ||||
|         auto now = details::os::now(); | ||||
|         handle_flush_interval(now, last_flush); | ||||
|         sleep_or_yield(now, last_pop); | ||||
|         return !_terminate_requested; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // flush all sinks if _flush_interval_ms has expired
 | ||||
| inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush) | ||||
| { | ||||
| 	auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms); | ||||
| 	if (should_flush) { | ||||
| 		for (auto &s : _sinks) | ||||
| 			s->flush(); | ||||
| 		now = last_flush = details::os::now(); | ||||
| 		_flush_requested = false; | ||||
| 	} | ||||
|     auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms); | ||||
|     if (should_flush) | ||||
|     { | ||||
|         for (auto &s : _sinks) | ||||
|             s->flush(); | ||||
|         now = last_flush = details::os::now(); | ||||
|         _flush_requested = false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter) | ||||
| { | ||||
| 	_formatter = msg_formatter; | ||||
|     _formatter = msg_formatter; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // spin, yield or sleep. use the time passed since last message as a hint
 | ||||
| inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_clock::time_point& now, const spdlog::log_clock::time_point& last_op_time) | ||||
| { | ||||
| 	using namespace std::this_thread; | ||||
| 	using std::chrono::milliseconds; | ||||
| 	using std::chrono::microseconds; | ||||
|     using namespace std::this_thread; | ||||
|     using std::chrono::milliseconds; | ||||
|     using std::chrono::microseconds; | ||||
| 
 | ||||
| 	auto time_since_op = now - last_op_time; | ||||
|     auto time_since_op = now - last_op_time; | ||||
| 
 | ||||
| 	// spin upto 50 micros
 | ||||
| 	if (time_since_op <= microseconds(50)) | ||||
| 		return; | ||||
|     // spin upto 50 micros
 | ||||
|     if (time_since_op <= microseconds(50)) | ||||
|         return; | ||||
| 
 | ||||
| 	// yield upto 150 micros
 | ||||
| 	if (time_since_op <= microseconds(100)) | ||||
| 		return std::this_thread::yield(); | ||||
|     // yield upto 150 micros
 | ||||
|     if (time_since_op <= microseconds(100)) | ||||
|         return std::this_thread::yield(); | ||||
| 
 | ||||
| 	// sleep for 20 ms upto 200 ms
 | ||||
| 	if (time_since_op <= milliseconds(200)) | ||||
| 		return sleep_for(milliseconds(20)); | ||||
|     // sleep for 20 ms upto 200 ms
 | ||||
|     if (time_since_op <= milliseconds(200)) | ||||
|         return sleep_for(milliseconds(20)); | ||||
| 
 | ||||
| 	// sleep for 200 ms
 | ||||
| 	return sleep_for(milliseconds(200)); | ||||
|     // sleep for 200 ms
 | ||||
|     return sleep_for(milliseconds(200)); | ||||
| } | ||||
| 
 | ||||
| // wait for the queue to be empty
 | ||||
| inline void spdlog::details::async_log_helper::wait_empty_q() | ||||
| { | ||||
| 	auto last_op = details::os::now(); | ||||
| 	while (_q.approx_size() > 0) { | ||||
| 		sleep_or_yield(details::os::now(), last_op); | ||||
| 	} | ||||
|     auto last_op = details::os::now(); | ||||
|     while (_q.approx_size() > 0) | ||||
|     { | ||||
|         sleep_or_yield(details::os::now(), last_op); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler) | ||||
| { | ||||
| 	_err_handler = err_handler; | ||||
|     _err_handler = err_handler; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -60,13 +60,13 @@ inline void spdlog::async_logger::flush() | ||||
| // Error handler
 | ||||
| inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler) | ||||
| { | ||||
| 	_err_handler = err_handler; | ||||
| 	_async_log_helper->set_error_handler(err_handler); | ||||
| 	 | ||||
|     _err_handler = err_handler; | ||||
|     _async_log_helper->set_error_handler(err_handler); | ||||
| 
 | ||||
| } | ||||
| inline spdlog::log_err_handler spdlog::async_logger::error_handler() | ||||
| { | ||||
| 	return _err_handler; | ||||
|     return _err_handler; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -13,74 +13,85 @@ | ||||
| #include "format.h" | ||||
| #include <ostream> | ||||
| 
 | ||||
| namespace fmt { | ||||
| namespace fmt | ||||
| { | ||||
| 
 | ||||
| namespace internal { | ||||
| namespace internal | ||||
| { | ||||
| 
 | ||||
| template <class Char> | ||||
| class FormatBuf : public std::basic_streambuf<Char> { | ||||
|  private: | ||||
|   typedef typename std::basic_streambuf<Char>::int_type int_type; | ||||
|   typedef typename std::basic_streambuf<Char>::traits_type traits_type; | ||||
| class FormatBuf : public std::basic_streambuf<Char> | ||||
| { | ||||
| private: | ||||
|     typedef typename std::basic_streambuf<Char>::int_type int_type; | ||||
|     typedef typename std::basic_streambuf<Char>::traits_type traits_type; | ||||
| 
 | ||||
|   Buffer<Char> &buffer_; | ||||
|   Char *start_; | ||||
|     Buffer<Char> &buffer_; | ||||
|     Char *start_; | ||||
| 
 | ||||
|  public: | ||||
|   FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0]) { | ||||
|     this->setp(start_, start_ + buffer_.capacity()); | ||||
|   } | ||||
| 
 | ||||
|   int_type overflow(int_type ch = traits_type::eof()) { | ||||
|     if (!traits_type::eq_int_type(ch, traits_type::eof())) { | ||||
|       size_t buf_size = size(); | ||||
|       buffer_.resize(buf_size); | ||||
|       buffer_.reserve(buf_size * 2); | ||||
| 
 | ||||
|       start_ = &buffer_[0]; | ||||
|       start_[buf_size] = traits_type::to_char_type(ch); | ||||
|       this->setp(start_+ buf_size + 1, start_ + buf_size * 2); | ||||
| public: | ||||
|     FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0]) | ||||
|     { | ||||
|         this->setp(start_, start_ + buffer_.capacity()); | ||||
|     } | ||||
|     return ch; | ||||
|   } | ||||
| 
 | ||||
|   size_t size() const { | ||||
|     return to_unsigned(this->pptr() - start_); | ||||
|   } | ||||
|     int_type overflow(int_type ch = traits_type::eof()) | ||||
|     { | ||||
|         if (!traits_type::eq_int_type(ch, traits_type::eof())) | ||||
|         { | ||||
|             size_t buf_size = size(); | ||||
|             buffer_.resize(buf_size); | ||||
|             buffer_.reserve(buf_size * 2); | ||||
| 
 | ||||
|             start_ = &buffer_[0]; | ||||
|             start_[buf_size] = traits_type::to_char_type(ch); | ||||
|             this->setp(start_+ buf_size + 1, start_ + buf_size * 2); | ||||
|         } | ||||
|         return ch; | ||||
|     } | ||||
| 
 | ||||
|     size_t size() const | ||||
|     { | ||||
|         return to_unsigned(this->pptr() - start_); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| Yes &convert(std::ostream &); | ||||
| 
 | ||||
| struct DummyStream : std::ostream { | ||||
|   DummyStream();  // Suppress a bogus warning in MSVC.
 | ||||
|   // Hide all operator<< overloads from std::ostream.
 | ||||
|   void operator<<(Null<>); | ||||
| struct DummyStream : std::ostream | ||||
| { | ||||
|     DummyStream();  // Suppress a bogus warning in MSVC.
 | ||||
|     // Hide all operator<< overloads from std::ostream.
 | ||||
|     void operator<<(Null<>); | ||||
| }; | ||||
| 
 | ||||
| No &operator<<(std::ostream &, int); | ||||
| 
 | ||||
| template<typename T> | ||||
| struct ConvertToIntImpl<T, true> { | ||||
|   // Convert to int only if T doesn't have an overloaded operator<<.
 | ||||
|   enum { | ||||
|     value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No) | ||||
|   }; | ||||
| struct ConvertToIntImpl<T, true> | ||||
| { | ||||
|     // Convert to int only if T doesn't have an overloaded operator<<.
 | ||||
|     enum | ||||
|     { | ||||
|         value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No) | ||||
|     }; | ||||
| }; | ||||
| }  // namespace internal
 | ||||
| 
 | ||||
| // Formats a value.
 | ||||
| template <typename Char, typename ArgFormatter, typename T> | ||||
| void format(BasicFormatter<Char, ArgFormatter> &f, | ||||
|             const Char *&format_str, const T &value) { | ||||
|   internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer; | ||||
|             const Char *&format_str, const T &value) | ||||
| { | ||||
|     internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer; | ||||
| 
 | ||||
|   internal::FormatBuf<Char> format_buf(buffer); | ||||
|   std::basic_ostream<Char> output(&format_buf); | ||||
|   output << value; | ||||
|     internal::FormatBuf<Char> format_buf(buffer); | ||||
|     std::basic_ostream<Char> output(&format_buf); | ||||
|     output << value; | ||||
| 
 | ||||
|   BasicStringRef<Char> str(&buffer[0], format_buf.size()); | ||||
|   typedef internal::MakeArg< BasicFormatter<Char> > MakeArg; | ||||
|   format_str = f.format(format_str, MakeArg(str)); | ||||
|     BasicStringRef<Char> str(&buffer[0], format_buf.size()); | ||||
|     typedef internal::MakeArg< BasicFormatter<Char> > MakeArg; | ||||
|     format_str = f.format(format_str, MakeArg(str)); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | ||||
| @ -83,112 +83,134 @@ | ||||
| 
 | ||||
| #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) | ||||
| 
 | ||||
| namespace fmt { | ||||
| namespace fmt | ||||
| { | ||||
| 
 | ||||
| // An error code.
 | ||||
| class ErrorCode { | ||||
|  private: | ||||
|   int value_; | ||||
| class ErrorCode | ||||
| { | ||||
| private: | ||||
|     int value_; | ||||
| 
 | ||||
|  public: | ||||
|   explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {} | ||||
| public: | ||||
| explicit ErrorCode(int value = 0) FMT_NOEXCEPT : | ||||
|     value_(value) {} | ||||
| 
 | ||||
|   int get() const FMT_NOEXCEPT { return value_; } | ||||
|     int get() const FMT_NOEXCEPT | ||||
|     { | ||||
|         return value_; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| // A buffered file.
 | ||||
| class BufferedFile { | ||||
|  private: | ||||
|   FILE *file_; | ||||
| class BufferedFile | ||||
| { | ||||
| private: | ||||
|     FILE *file_; | ||||
| 
 | ||||
|   friend class File; | ||||
|     friend class File; | ||||
| 
 | ||||
|   explicit BufferedFile(FILE *f) : file_(f) {} | ||||
| 
 | ||||
|  public: | ||||
|   // Constructs a BufferedFile object which doesn't represent any file.
 | ||||
|   BufferedFile() FMT_NOEXCEPT : file_(0) {} | ||||
| 
 | ||||
|   // Destroys the object closing the file it represents if any.
 | ||||
|   ~BufferedFile() FMT_NOEXCEPT; | ||||
| 
 | ||||
| #if !FMT_USE_RVALUE_REFERENCES | ||||
|   // Emulate a move constructor and a move assignment operator if rvalue
 | ||||
|   // references are not supported.
 | ||||
| 
 | ||||
|  private: | ||||
|   // A proxy object to emulate a move constructor.
 | ||||
|   // It is private to make it impossible call operator Proxy directly.
 | ||||
|   struct Proxy { | ||||
|     FILE *file; | ||||
|   }; | ||||
|     explicit BufferedFile(FILE *f) : file_(f) {} | ||||
| 
 | ||||
| public: | ||||
|   // A "move constructor" for moving from a temporary.
 | ||||
|   BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {} | ||||
|     // Constructs a BufferedFile object which doesn't represent any file.
 | ||||
| BufferedFile() FMT_NOEXCEPT : | ||||
|     file_(0) {} | ||||
| 
 | ||||
|   // A "move constructor" for moving from an lvalue.
 | ||||
|   BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) { | ||||
|     f.file_ = 0; | ||||
|   } | ||||
|     // Destroys the object closing the file it represents if any.
 | ||||
|     ~BufferedFile() FMT_NOEXCEPT; | ||||
| 
 | ||||
|   // A "move assignment operator" for moving from a temporary.
 | ||||
|   BufferedFile &operator=(Proxy p) { | ||||
|     close(); | ||||
|     file_ = p.file; | ||||
|     return *this; | ||||
|   } | ||||
| #if !FMT_USE_RVALUE_REFERENCES | ||||
|     // Emulate a move constructor and a move assignment operator if rvalue
 | ||||
|     // references are not supported.
 | ||||
| 
 | ||||
|   // A "move assignment operator" for moving from an lvalue.
 | ||||
|   BufferedFile &operator=(BufferedFile &other) { | ||||
|     close(); | ||||
|     file_ = other.file_; | ||||
|     other.file_ = 0; | ||||
|     return *this; | ||||
|   } | ||||
| private: | ||||
|     // A proxy object to emulate a move constructor.
 | ||||
|     // It is private to make it impossible call operator Proxy directly.
 | ||||
|     struct Proxy | ||||
|     { | ||||
|         FILE *file; | ||||
|     }; | ||||
| 
 | ||||
|   // Returns a proxy object for moving from a temporary:
 | ||||
|   //   BufferedFile file = BufferedFile(...);
 | ||||
|   operator Proxy() FMT_NOEXCEPT { | ||||
|     Proxy p = {file_}; | ||||
|     file_ = 0; | ||||
|     return p; | ||||
|   } | ||||
| public: | ||||
|     // A "move constructor" for moving from a temporary.
 | ||||
| BufferedFile(Proxy p) FMT_NOEXCEPT : | ||||
|     file_(p.file) {} | ||||
| 
 | ||||
|     // A "move constructor" for moving from an lvalue.
 | ||||
| BufferedFile(BufferedFile &f) FMT_NOEXCEPT : | ||||
|     file_(f.file_) | ||||
|     { | ||||
|         f.file_ = 0; | ||||
|     } | ||||
| 
 | ||||
|     // A "move assignment operator" for moving from a temporary.
 | ||||
|     BufferedFile &operator=(Proxy p) | ||||
|     { | ||||
|         close(); | ||||
|         file_ = p.file; | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     // A "move assignment operator" for moving from an lvalue.
 | ||||
|     BufferedFile &operator=(BufferedFile &other) | ||||
|     { | ||||
|         close(); | ||||
|         file_ = other.file_; | ||||
|         other.file_ = 0; | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     // Returns a proxy object for moving from a temporary:
 | ||||
|     //   BufferedFile file = BufferedFile(...);
 | ||||
|     operator Proxy() FMT_NOEXCEPT | ||||
|     { | ||||
|         Proxy p = {file_}; | ||||
|         file_ = 0; | ||||
|         return p; | ||||
|     } | ||||
| 
 | ||||
| #else | ||||
|  private: | ||||
|   FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile); | ||||
| private: | ||||
|     FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile); | ||||
| 
 | ||||
|  public: | ||||
|   BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) { | ||||
|     other.file_ = 0; | ||||
|   } | ||||
| public: | ||||
| BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : | ||||
|     file_(other.file_) | ||||
|     { | ||||
|         other.file_ = 0; | ||||
|     } | ||||
| 
 | ||||
|   BufferedFile& operator=(BufferedFile &&other) { | ||||
|     close(); | ||||
|     file_ = other.file_; | ||||
|     other.file_ = 0; | ||||
|     return *this; | ||||
|   } | ||||
|     BufferedFile& operator=(BufferedFile &&other) | ||||
|     { | ||||
|         close(); | ||||
|         file_ = other.file_; | ||||
|         other.file_ = 0; | ||||
|         return *this; | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
|   // Opens a file.
 | ||||
|   BufferedFile(CStringRef filename, CStringRef mode); | ||||
|     // Opens a file.
 | ||||
|     BufferedFile(CStringRef filename, CStringRef mode); | ||||
| 
 | ||||
|   // Closes the file.
 | ||||
|   void close(); | ||||
|     // Closes the file.
 | ||||
|     void close(); | ||||
| 
 | ||||
|   // Returns the pointer to a FILE object representing this file.
 | ||||
|   FILE *get() const FMT_NOEXCEPT { return file_; } | ||||
|     // Returns the pointer to a FILE object representing this file.
 | ||||
|     FILE *get() const FMT_NOEXCEPT | ||||
|     { | ||||
|         return file_; | ||||
|     } | ||||
| 
 | ||||
|   // We place parentheses around fileno to workaround a bug in some versions
 | ||||
|   // of MinGW that define fileno as a macro.
 | ||||
|   int (fileno)() const; | ||||
|     // We place parentheses around fileno to workaround a bug in some versions
 | ||||
|     // of MinGW that define fileno as a macro.
 | ||||
|     int (fileno)() const; | ||||
| 
 | ||||
|   void print(CStringRef format_str, const ArgList &args) { | ||||
|     fmt::print(file_, format_str, args); | ||||
|   } | ||||
|   FMT_VARIADIC(void, print, CStringRef) | ||||
|     void print(CStringRef format_str, const ArgList &args) | ||||
|     { | ||||
|         fmt::print(file_, format_str, args); | ||||
|     } | ||||
|     FMT_VARIADIC(void, print, CStringRef) | ||||
| }; | ||||
| 
 | ||||
| // A file. Closed file is represented by a File object with descriptor -1.
 | ||||
| @ -197,125 +219,141 @@ public: | ||||
| // closing the file multiple times will cause a crash on Windows rather
 | ||||
| // than an exception. You can get standard behavior by overriding the
 | ||||
| // invalid parameter handler with _set_invalid_parameter_handler.
 | ||||
| class File { | ||||
|  private: | ||||
|   int fd_;  // File descriptor.
 | ||||
| class File | ||||
| { | ||||
| private: | ||||
|     int fd_;  // File descriptor.
 | ||||
| 
 | ||||
|   // Constructs a File object with a given descriptor.
 | ||||
|   explicit File(int fd) : fd_(fd) {} | ||||
|     // Constructs a File object with a given descriptor.
 | ||||
|     explicit File(int fd) : fd_(fd) {} | ||||
| 
 | ||||
|  public: | ||||
|   // Possible values for the oflag argument to the constructor.
 | ||||
|   enum { | ||||
|     RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
 | ||||
|     WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
 | ||||
|     RDWR   = FMT_POSIX(O_RDWR)    // Open for reading and writing.
 | ||||
|   }; | ||||
| public: | ||||
|     // Possible values for the oflag argument to the constructor.
 | ||||
|     enum | ||||
|     { | ||||
|         RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
 | ||||
|         WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
 | ||||
|         RDWR   = FMT_POSIX(O_RDWR)    // Open for reading and writing.
 | ||||
|     }; | ||||
| 
 | ||||
|   // Constructs a File object which doesn't represent any file.
 | ||||
|   File() FMT_NOEXCEPT : fd_(-1) {} | ||||
|     // Constructs a File object which doesn't represent any file.
 | ||||
| File() FMT_NOEXCEPT : | ||||
|     fd_(-1) {} | ||||
| 
 | ||||
|   // Opens a file and constructs a File object representing this file.
 | ||||
|   File(CStringRef path, int oflag); | ||||
|     // Opens a file and constructs a File object representing this file.
 | ||||
|     File(CStringRef path, int oflag); | ||||
| 
 | ||||
| #if !FMT_USE_RVALUE_REFERENCES | ||||
|   // Emulate a move constructor and a move assignment operator if rvalue
 | ||||
|   // references are not supported.
 | ||||
|     // Emulate a move constructor and a move assignment operator if rvalue
 | ||||
|     // references are not supported.
 | ||||
| 
 | ||||
|  private: | ||||
|   // A proxy object to emulate a move constructor.
 | ||||
|   // It is private to make it impossible call operator Proxy directly.
 | ||||
|   struct Proxy { | ||||
|     int fd; | ||||
|   }; | ||||
| private: | ||||
|     // A proxy object to emulate a move constructor.
 | ||||
|     // It is private to make it impossible call operator Proxy directly.
 | ||||
|     struct Proxy | ||||
|     { | ||||
|         int fd; | ||||
|     }; | ||||
| 
 | ||||
|  public: | ||||
|   // A "move constructor" for moving from a temporary.
 | ||||
|   File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {} | ||||
| public: | ||||
|     // A "move constructor" for moving from a temporary.
 | ||||
| File(Proxy p) FMT_NOEXCEPT : | ||||
|     fd_(p.fd) {} | ||||
| 
 | ||||
|   // A "move constructor" for moving from an lvalue.
 | ||||
|   File(File &other) FMT_NOEXCEPT : fd_(other.fd_) { | ||||
|     other.fd_ = -1; | ||||
|   } | ||||
|     // A "move constructor" for moving from an lvalue.
 | ||||
| File(File &other) FMT_NOEXCEPT : | ||||
|     fd_(other.fd_) | ||||
|     { | ||||
|         other.fd_ = -1; | ||||
|     } | ||||
| 
 | ||||
|   // A "move assignment operator" for moving from a temporary.
 | ||||
|   File &operator=(Proxy p) { | ||||
|     close(); | ||||
|     fd_ = p.fd; | ||||
|     return *this; | ||||
|   } | ||||
|     // A "move assignment operator" for moving from a temporary.
 | ||||
|     File &operator=(Proxy p) | ||||
|     { | ||||
|         close(); | ||||
|         fd_ = p.fd; | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|   // A "move assignment operator" for moving from an lvalue.
 | ||||
|   File &operator=(File &other) { | ||||
|     close(); | ||||
|     fd_ = other.fd_; | ||||
|     other.fd_ = -1; | ||||
|     return *this; | ||||
|   } | ||||
|     // A "move assignment operator" for moving from an lvalue.
 | ||||
|     File &operator=(File &other) | ||||
|     { | ||||
|         close(); | ||||
|         fd_ = other.fd_; | ||||
|         other.fd_ = -1; | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|   // Returns a proxy object for moving from a temporary:
 | ||||
|   //   File file = File(...);
 | ||||
|   operator Proxy() FMT_NOEXCEPT { | ||||
|     Proxy p = {fd_}; | ||||
|     fd_ = -1; | ||||
|     return p; | ||||
|   } | ||||
|     // Returns a proxy object for moving from a temporary:
 | ||||
|     //   File file = File(...);
 | ||||
|     operator Proxy() FMT_NOEXCEPT | ||||
|     { | ||||
|         Proxy p = {fd_}; | ||||
|         fd_ = -1; | ||||
|         return p; | ||||
|     } | ||||
| 
 | ||||
| #else | ||||
|  private: | ||||
|   FMT_DISALLOW_COPY_AND_ASSIGN(File); | ||||
| private: | ||||
|     FMT_DISALLOW_COPY_AND_ASSIGN(File); | ||||
| 
 | ||||
|  public: | ||||
|   File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) { | ||||
|     other.fd_ = -1; | ||||
|   } | ||||
| public: | ||||
| File(File &&other) FMT_NOEXCEPT : | ||||
|     fd_(other.fd_) | ||||
|     { | ||||
|         other.fd_ = -1; | ||||
|     } | ||||
| 
 | ||||
|   File& operator=(File &&other) { | ||||
|     close(); | ||||
|     fd_ = other.fd_; | ||||
|     other.fd_ = -1; | ||||
|     return *this; | ||||
|   } | ||||
|     File& operator=(File &&other) | ||||
|     { | ||||
|         close(); | ||||
|         fd_ = other.fd_; | ||||
|         other.fd_ = -1; | ||||
|         return *this; | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
|   // Destroys the object closing the file it represents if any.
 | ||||
|   ~File() FMT_NOEXCEPT; | ||||
|     // Destroys the object closing the file it represents if any.
 | ||||
|     ~File() FMT_NOEXCEPT; | ||||
| 
 | ||||
|   // Returns the file descriptor.
 | ||||
|   int descriptor() const FMT_NOEXCEPT { return fd_; } | ||||
|     // Returns the file descriptor.
 | ||||
|     int descriptor() const FMT_NOEXCEPT | ||||
|     { | ||||
|         return fd_; | ||||
|     } | ||||
| 
 | ||||
|   // Closes the file.
 | ||||
|   void close(); | ||||
|     // Closes the file.
 | ||||
|     void close(); | ||||
| 
 | ||||
|   // Returns the file size. The size has signed type for consistency with
 | ||||
|   // stat::st_size.
 | ||||
|   LongLong size() const; | ||||
|     // Returns the file size. The size has signed type for consistency with
 | ||||
|     // stat::st_size.
 | ||||
|     LongLong size() const; | ||||
| 
 | ||||
|   // Attempts to read count bytes from the file into the specified buffer.
 | ||||
|   std::size_t read(void *buffer, std::size_t count); | ||||
|     // Attempts to read count bytes from the file into the specified buffer.
 | ||||
|     std::size_t read(void *buffer, std::size_t count); | ||||
| 
 | ||||
|   // Attempts to write count bytes from the specified buffer to the file.
 | ||||
|   std::size_t write(const void *buffer, std::size_t count); | ||||
|     // Attempts to write count bytes from the specified buffer to the file.
 | ||||
|     std::size_t write(const void *buffer, std::size_t count); | ||||
| 
 | ||||
|   // Duplicates a file descriptor with the dup function and returns
 | ||||
|   // the duplicate as a file object.
 | ||||
|   static File dup(int fd); | ||||
|     // Duplicates a file descriptor with the dup function and returns
 | ||||
|     // the duplicate as a file object.
 | ||||
|     static File dup(int fd); | ||||
| 
 | ||||
|   // Makes fd be the copy of this file descriptor, closing fd first if
 | ||||
|   // necessary.
 | ||||
|   void dup2(int fd); | ||||
|     // Makes fd be the copy of this file descriptor, closing fd first if
 | ||||
|     // necessary.
 | ||||
|     void dup2(int fd); | ||||
| 
 | ||||
|   // Makes fd be the copy of this file descriptor, closing fd first if
 | ||||
|   // necessary.
 | ||||
|   void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT; | ||||
|     // Makes fd be the copy of this file descriptor, closing fd first if
 | ||||
|     // necessary.
 | ||||
|     void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT; | ||||
| 
 | ||||
|   // Creates a pipe setting up read_end and write_end file objects for reading
 | ||||
|   // and writing respectively.
 | ||||
|   static void pipe(File &read_end, File &write_end); | ||||
|     // Creates a pipe setting up read_end and write_end file objects for reading
 | ||||
|     // and writing respectively.
 | ||||
|     static void pipe(File &read_end, File &write_end); | ||||
| 
 | ||||
|   // Creates a BufferedFile object associated with this file and detaches
 | ||||
|   // this File object from the file.
 | ||||
|   BufferedFile fdopen(const char *mode); | ||||
|     // Creates a BufferedFile object associated with this file and detaches
 | ||||
|     // this File object from the file.
 | ||||
|     BufferedFile fdopen(const char *mode); | ||||
| }; | ||||
| 
 | ||||
| // Returns the memory page size.
 | ||||
| @ -328,58 +366,77 @@ long getpagesize(); | ||||
| 
 | ||||
| #ifdef FMT_LOCALE | ||||
| // A "C" numeric locale.
 | ||||
| class Locale { | ||||
|  private: | ||||
| class Locale | ||||
| { | ||||
| private: | ||||
| # ifdef _MSC_VER | ||||
|   typedef _locale_t locale_t; | ||||
|     typedef _locale_t locale_t; | ||||
| 
 | ||||
|   enum { LC_NUMERIC_MASK = LC_NUMERIC }; | ||||
|     enum { LC_NUMERIC_MASK = LC_NUMERIC }; | ||||
| 
 | ||||
|   static locale_t newlocale(int category_mask, const char *locale, locale_t) { | ||||
|     return _create_locale(category_mask, locale); | ||||
|   } | ||||
|     static locale_t newlocale(int category_mask, const char *locale, locale_t) | ||||
|     { | ||||
|         return _create_locale(category_mask, locale); | ||||
|     } | ||||
| 
 | ||||
|   static void freelocale(locale_t locale) { | ||||
|     _free_locale(locale); | ||||
|   } | ||||
|     static void freelocale(locale_t locale) | ||||
|     { | ||||
|         _free_locale(locale); | ||||
|     } | ||||
| 
 | ||||
|   static double strtod_l(const char *nptr, char **endptr, _locale_t locale) { | ||||
|     return _strtod_l(nptr, endptr, locale); | ||||
|   } | ||||
|     static double strtod_l(const char *nptr, char **endptr, _locale_t locale) | ||||
|     { | ||||
|         return _strtod_l(nptr, endptr, locale); | ||||
|     } | ||||
| # endif | ||||
| 
 | ||||
|   locale_t locale_; | ||||
|     locale_t locale_; | ||||
| 
 | ||||
|   FMT_DISALLOW_COPY_AND_ASSIGN(Locale); | ||||
|     FMT_DISALLOW_COPY_AND_ASSIGN(Locale); | ||||
| 
 | ||||
|  public: | ||||
|   typedef locale_t Type; | ||||
| public: | ||||
|     typedef locale_t Type; | ||||
| 
 | ||||
|   Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", NULL)) { | ||||
|     if (!locale_) | ||||
|       FMT_THROW(fmt::SystemError(errno, "cannot create locale")); | ||||
|   } | ||||
|   ~Locale() { freelocale(locale_); } | ||||
|     Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", NULL)) | ||||
|     { | ||||
|         if (!locale_) | ||||
|             FMT_THROW(fmt::SystemError(errno, "cannot create locale")); | ||||
|     } | ||||
|     ~Locale() | ||||
|     { | ||||
|         freelocale(locale_); | ||||
|     } | ||||
| 
 | ||||
|   Type get() const { return locale_; } | ||||
|     Type get() const | ||||
|     { | ||||
|         return locale_; | ||||
|     } | ||||
| 
 | ||||
|   // Converts string to floating-point number and advances str past the end
 | ||||
|   // of the parsed input.
 | ||||
|   double strtod(const char *&str) const { | ||||
|     char *end = 0; | ||||
|     double result = strtod_l(str, &end, locale_); | ||||
|     str = end; | ||||
|     return result; | ||||
|   } | ||||
|     // Converts string to floating-point number and advances str past the end
 | ||||
|     // of the parsed input.
 | ||||
|     double strtod(const char *&str) const | ||||
|     { | ||||
|         char *end = 0; | ||||
|         double result = strtod_l(str, &end, locale_); | ||||
|         str = end; | ||||
|         return result; | ||||
|     } | ||||
| }; | ||||
| #endif  // FMT_LOCALE
 | ||||
| }  // namespace fmt
 | ||||
| 
 | ||||
| #if !FMT_USE_RVALUE_REFERENCES | ||||
| namespace std { | ||||
| namespace std | ||||
| { | ||||
| // For compatibility with C++98.
 | ||||
| inline fmt::BufferedFile &move(fmt::BufferedFile &f) { return f; } | ||||
| inline fmt::File &move(fmt::File &f) { return f; } | ||||
| inline fmt::BufferedFile &move(fmt::BufferedFile &f) | ||||
| { | ||||
|     return f; | ||||
| } | ||||
| inline fmt::File &move(fmt::File &f) | ||||
| { | ||||
|     return f; | ||||
| } | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
|  | ||||
| @ -13,40 +13,45 @@ | ||||
| #include "format.h" | ||||
| #include <ctime> | ||||
| 
 | ||||
| namespace fmt { | ||||
| namespace fmt | ||||
| { | ||||
| template <typename ArgFormatter> | ||||
| void format(BasicFormatter<char, ArgFormatter> &f, | ||||
|             const char *&format_str, const std::tm &tm) { | ||||
|   if (*format_str == ':') | ||||
|     ++format_str; | ||||
|   const char *end = format_str; | ||||
|   while (*end && *end != '}') | ||||
|     ++end; | ||||
|   if (*end != '}') | ||||
|     FMT_THROW(FormatError("missing '}' in format string")); | ||||
|   internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format; | ||||
|   format.append(format_str, end + 1); | ||||
|   format[format.size() - 1] = '\0'; | ||||
|   Buffer<char> &buffer = f.writer().buffer(); | ||||
|   std::size_t start = buffer.size(); | ||||
|   for (;;) { | ||||
|     std::size_t size = buffer.capacity() - start; | ||||
|     std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm); | ||||
|     if (count != 0) { | ||||
|       buffer.resize(start + count); | ||||
|       break; | ||||
|             const char *&format_str, const std::tm &tm) | ||||
| { | ||||
|     if (*format_str == ':') | ||||
|         ++format_str; | ||||
|     const char *end = format_str; | ||||
|     while (*end && *end != '}') | ||||
|         ++end; | ||||
|     if (*end != '}') | ||||
|         FMT_THROW(FormatError("missing '}' in format string")); | ||||
|     internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format; | ||||
|     format.append(format_str, end + 1); | ||||
|     format[format.size() - 1] = '\0'; | ||||
|     Buffer<char> &buffer = f.writer().buffer(); | ||||
|     std::size_t start = buffer.size(); | ||||
|     for (;;) | ||||
|     { | ||||
|         std::size_t size = buffer.capacity() - start; | ||||
|         std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm); | ||||
|         if (count != 0) | ||||
|         { | ||||
|             buffer.resize(start + count); | ||||
|             break; | ||||
|         } | ||||
|         if (size >= format.size() * 256) | ||||
|         { | ||||
|             // If the buffer is 256 times larger than the format string, assume
 | ||||
|             // that `strftime` gives an empty result. There doesn't seem to be a
 | ||||
|             // better way to distinguish the two cases:
 | ||||
|             // https://github.com/fmtlib/fmt/issues/367
 | ||||
|             break; | ||||
|         } | ||||
|         const std::size_t MIN_GROWTH = 10; | ||||
|         buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); | ||||
|     } | ||||
|     if (size >= format.size() * 256) { | ||||
|       // If the buffer is 256 times larger than the format string, assume
 | ||||
|       // that `strftime` gives an empty result. There doesn't seem to be a
 | ||||
|       // better way to distinguish the two cases:
 | ||||
|       // https://github.com/fmtlib/fmt/issues/367
 | ||||
|       break; | ||||
|     } | ||||
|     const std::size_t MIN_GROWTH = 10; | ||||
|     buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); | ||||
|   } | ||||
|   format_str = end + 1; | ||||
|     format_str = end + 1; | ||||
| } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -58,7 +58,7 @@ public: | ||||
|     const std::string& name() const; | ||||
|     void set_pattern(const std::string&); | ||||
|     void set_formatter(formatter_ptr); | ||||
|     | ||||
| 
 | ||||
|     // automatically call flush() if message level >= log_level
 | ||||
|     void flush_on(level::level_enum log_level); | ||||
| 
 | ||||
| @ -66,9 +66,9 @@ public: | ||||
| 
 | ||||
|     const std::vector<sink_ptr>& sinks() const; | ||||
| 
 | ||||
| 	// error handler
 | ||||
| 	virtual void set_error_handler(log_err_handler); | ||||
| 	virtual log_err_handler error_handler(); | ||||
|     // error handler
 | ||||
|     virtual void set_error_handler(log_err_handler); | ||||
|     virtual log_err_handler error_handler(); | ||||
| 
 | ||||
| protected: | ||||
|     virtual void _sink_it(details::log_msg&); | ||||
|  | ||||
							
								
								
									
										140
									
								
								tests/errors.cpp
									
									
									
									
									
								
							
							
						
						
									
										140
									
								
								tests/errors.cpp
									
									
									
									
									
								
							| @ -8,30 +8,30 @@ | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class failing_sink: public spdlog::sinks::sink | ||||
| { | ||||
| 	void log(const spdlog::details::log_msg& msg) override | ||||
| 	{ | ||||
| 		throw std::runtime_error("some error happened during log"); | ||||
| 	} | ||||
| 
 | ||||
| 	void flush() | ||||
| 	{} | ||||
| class failing_sink: public spdlog::sinks::sink | ||||
| { | ||||
|     void log(const spdlog::details::log_msg& msg) override | ||||
|     { | ||||
|         throw std::runtime_error("some error happened during log"); | ||||
|     } | ||||
| 
 | ||||
|     void flush() | ||||
|     {} | ||||
| }; | ||||
| 
 | ||||
| TEST_CASE("default_error_handler", "[errors]]") | ||||
| { | ||||
| 	prepare_logdir(); | ||||
| 	std::string filename = "logs/simple_log.txt"; | ||||
|     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(); | ||||
|     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); | ||||
|     REQUIRE(file_contents(filename) == std::string("Test message 2\n")); | ||||
|     REQUIRE(count_lines(filename) == 1); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @ -41,69 +41,73 @@ 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->flush_on(spdlog::level::info); | ||||
| 	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); | ||||
|     prepare_logdir(); | ||||
|     std::string filename = "logs/simple_log.txt"; | ||||
|     auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true); | ||||
|     logger->flush_on(spdlog::level::info); | ||||
|     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("default_error_handler2", "[errors]]") | ||||
| { | ||||
| 
 | ||||
| 	auto logger = spdlog::create<failing_sink>("failed_logger"); | ||||
| 	logger->set_error_handler([=](const std::string& msg) { | ||||
| 		throw custom_ex(); | ||||
| 	}); | ||||
| 	REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex); | ||||
|     auto logger = spdlog::create<failing_sink>("failed_logger"); | ||||
|     logger->set_error_handler([=](const std::string& msg) | ||||
|     { | ||||
|         throw custom_ex(); | ||||
|     }); | ||||
|     REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
|     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); | ||||
| } | ||||
| 
 | ||||
| // Make sure async error handler is executed
 | ||||
| TEST_CASE("async_error_handler2", "[errors]]") | ||||
| { | ||||
| 	prepare_logdir(); | ||||
| 	std::string err_msg("This is async handler error message"); | ||||
| 	spdlog::set_async_mode(128); | ||||
| 	{ | ||||
| 		auto logger = spdlog::create<failing_sink>("failed_logger"); | ||||
| 		logger->set_error_handler([=](const std::string& msg) { | ||||
| 			std::ofstream ofs("logs/custom_err2.txt"); | ||||
| 			if (!ofs) throw std::runtime_error("Failed open logs/custom_err2.txt"); | ||||
| 			ofs << err_msg; | ||||
| 		}); | ||||
| 		logger->info("Hello failure"); | ||||
| 		spdlog::drop("failed_logger"); //force logger to drain the queue and shutdown
 | ||||
| 		spdlog::set_sync_mode(); | ||||
| 	} | ||||
|     prepare_logdir(); | ||||
|     std::string err_msg("This is async handler error message"); | ||||
|     spdlog::set_async_mode(128); | ||||
|     { | ||||
|         auto logger = spdlog::create<failing_sink>("failed_logger"); | ||||
|         logger->set_error_handler([=](const std::string& msg) | ||||
|         { | ||||
|             std::ofstream ofs("logs/custom_err2.txt"); | ||||
|             if (!ofs) throw std::runtime_error("Failed open logs/custom_err2.txt"); | ||||
|             ofs << err_msg; | ||||
|         }); | ||||
|         logger->info("Hello failure"); | ||||
|         spdlog::drop("failed_logger"); //force logger to drain the queue and shutdown
 | ||||
|         spdlog::set_sync_mode(); | ||||
|     } | ||||
| 
 | ||||
| 	REQUIRE(file_contents("logs/custom_err2.txt") == err_msg); | ||||
|     REQUIRE(file_contents("logs/custom_err2.txt") == err_msg); | ||||
| } | ||||
|  | ||||
| @ -3,15 +3,15 @@ | ||||
| 
 | ||||
| void prepare_logdir() | ||||
| { | ||||
|     spdlog::drop_all();     | ||||
|     spdlog::drop_all(); | ||||
| #ifdef _WIN32 | ||||
| 	system("if not exist logs mkdir logs"); | ||||
|     system("if not exist logs mkdir logs"); | ||||
|     system("del /F /Q logs\\*"); | ||||
| #else | ||||
| 	auto rv = system("mkdir -p logs"); | ||||
|     auto rv = system("mkdir -p logs"); | ||||
|     rv = system("rm -f logs/*"); | ||||
|     (void)rv; | ||||
| #endif     | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user