basic_sink_frontend.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /*
  2. * Copyright Andrey Semashev 2007 - 2015.
  3. * Distributed under the Boost Software License, Version 1.0.
  4. * (See accompanying file LICENSE_1_0.txt or copy at
  5. * http://www.boost.org/LICENSE_1_0.txt)
  6. */
  7. /*!
  8. * \file basic_sink_frontend.hpp
  9. * \author Andrey Semashev
  10. * \date 14.07.2009
  11. *
  12. * The header contains implementation of a base class for sink frontends.
  13. */
  14. #ifndef BOOST_LOG_SINKS_BASIC_SINK_FRONTEND_HPP_INCLUDED_
  15. #define BOOST_LOG_SINKS_BASIC_SINK_FRONTEND_HPP_INCLUDED_
  16. #include <boost/type_traits/integral_constant.hpp>
  17. #include <boost/log/detail/config.hpp>
  18. #include <boost/log/detail/code_conversion.hpp>
  19. #include <boost/log/detail/attachable_sstream_buf.hpp>
  20. #include <boost/log/detail/fake_mutex.hpp>
  21. #include <boost/log/core/record_view.hpp>
  22. #include <boost/log/sinks/sink.hpp>
  23. #include <boost/log/sinks/frontend_requirements.hpp>
  24. #include <boost/log/expressions/filter.hpp>
  25. #include <boost/log/expressions/formatter.hpp>
  26. #if !defined(BOOST_LOG_NO_THREADS)
  27. #include <boost/memory_order.hpp>
  28. #include <boost/atomic/atomic.hpp>
  29. #include <boost/thread/tss.hpp>
  30. #include <boost/log/detail/locks.hpp>
  31. #include <boost/log/detail/light_rw_mutex.hpp>
  32. #endif // !defined(BOOST_LOG_NO_THREADS)
  33. #include <boost/log/detail/header.hpp>
  34. #ifdef BOOST_HAS_PRAGMA_ONCE
  35. #pragma once
  36. #endif
  37. namespace boost {
  38. BOOST_LOG_OPEN_NAMESPACE
  39. namespace sinks {
  40. //! A base class for a logging sink frontend
  41. class BOOST_LOG_NO_VTABLE basic_sink_frontend :
  42. public sink
  43. {
  44. //! Base type
  45. typedef sink base_type;
  46. public:
  47. //! An exception handler type
  48. typedef base_type::exception_handler_type exception_handler_type;
  49. #if !defined(BOOST_LOG_NO_THREADS)
  50. protected:
  51. //! Mutex type
  52. typedef boost::log::aux::light_rw_mutex mutex_type;
  53. private:
  54. //! Synchronization mutex
  55. mutable mutex_type m_Mutex;
  56. #endif
  57. private:
  58. //! Filter
  59. filter m_Filter;
  60. //! Exception handler
  61. exception_handler_type m_ExceptionHandler;
  62. public:
  63. /*!
  64. * \brief Initializing constructor
  65. *
  66. * \param cross_thread The flag indicates whether the sink passes log records between different threads
  67. */
  68. explicit basic_sink_frontend(bool cross_thread) : sink(cross_thread)
  69. {
  70. }
  71. /*!
  72. * The method sets sink-specific filter functional object
  73. */
  74. template< typename FunT >
  75. void set_filter(FunT const& filter)
  76. {
  77. BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);)
  78. m_Filter = filter;
  79. }
  80. /*!
  81. * The method resets the filter
  82. */
  83. void reset_filter()
  84. {
  85. BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);)
  86. m_Filter.reset();
  87. }
  88. /*!
  89. * The method sets an exception handler function
  90. */
  91. template< typename FunT >
  92. void set_exception_handler(FunT const& handler)
  93. {
  94. BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);)
  95. m_ExceptionHandler = handler;
  96. }
  97. /*!
  98. * The method resets the exception handler function
  99. */
  100. void reset_exception_handler()
  101. {
  102. BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);)
  103. m_ExceptionHandler.clear();
  104. }
  105. /*!
  106. * The method returns \c true if no filter is set or the attribute values pass the filter
  107. *
  108. * \param attrs A set of attribute values of a logging record
  109. */
  110. bool will_consume(attribute_value_set const& attrs) BOOST_OVERRIDE
  111. {
  112. BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(m_Mutex);)
  113. try
  114. {
  115. return m_Filter(attrs);
  116. }
  117. catch (...)
  118. {
  119. if (m_ExceptionHandler.empty())
  120. throw;
  121. m_ExceptionHandler();
  122. return false;
  123. }
  124. }
  125. protected:
  126. #if !defined(BOOST_LOG_NO_THREADS)
  127. //! Returns reference to the frontend mutex
  128. mutex_type& frontend_mutex() const { return m_Mutex; }
  129. #endif
  130. //! Returns reference to the exception handler
  131. exception_handler_type& exception_handler() { return m_ExceptionHandler; }
  132. //! Returns reference to the exception handler
  133. exception_handler_type const& exception_handler() const { return m_ExceptionHandler; }
  134. //! Feeds log record to the backend
  135. template< typename BackendMutexT, typename BackendT >
  136. void feed_record(record_view const& rec, BackendMutexT& backend_mutex, BackendT& backend)
  137. {
  138. try
  139. {
  140. BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< BackendMutexT > lock(backend_mutex);)
  141. backend.consume(rec);
  142. }
  143. catch (...)
  144. {
  145. BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(m_Mutex);)
  146. if (m_ExceptionHandler.empty())
  147. throw;
  148. m_ExceptionHandler();
  149. }
  150. }
  151. //! Attempts to feeds log record to the backend, does not block if \a backend_mutex is locked
  152. template< typename BackendMutexT, typename BackendT >
  153. bool try_feed_record(record_view const& rec, BackendMutexT& backend_mutex, BackendT& backend)
  154. {
  155. #if !defined(BOOST_LOG_NO_THREADS)
  156. try
  157. {
  158. if (!backend_mutex.try_lock())
  159. return false;
  160. }
  161. catch (...)
  162. {
  163. boost::log::aux::shared_lock_guard< mutex_type > frontend_lock(this->frontend_mutex());
  164. if (this->exception_handler().empty())
  165. throw;
  166. this->exception_handler()();
  167. return false;
  168. }
  169. boost::log::aux::exclusive_auto_unlocker< BackendMutexT > unlocker(backend_mutex);
  170. #endif
  171. // No need to lock anything in the feed_record method
  172. boost::log::aux::fake_mutex m;
  173. feed_record(rec, m, backend);
  174. return true;
  175. }
  176. //! Flushes record buffers in the backend, if one supports it
  177. template< typename BackendMutexT, typename BackendT >
  178. void flush_backend(BackendMutexT& backend_mutex, BackendT& backend)
  179. {
  180. typedef typename BackendT::frontend_requirements frontend_requirements;
  181. flush_backend_impl(backend_mutex, backend,
  182. typename has_requirement< frontend_requirements, flushing >::type());
  183. }
  184. private:
  185. //! Flushes record buffers in the backend (the actual implementation)
  186. template< typename BackendMutexT, typename BackendT >
  187. void flush_backend_impl(BackendMutexT& backend_mutex, BackendT& backend, boost::true_type)
  188. {
  189. try
  190. {
  191. BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< BackendMutexT > lock(backend_mutex);)
  192. backend.flush();
  193. }
  194. catch (...)
  195. {
  196. BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(m_Mutex);)
  197. if (m_ExceptionHandler.empty())
  198. throw;
  199. m_ExceptionHandler();
  200. }
  201. }
  202. //! Flushes record buffers in the backend (stub for backends that don't support flushing)
  203. template< typename BackendMutexT, typename BackendT >
  204. void flush_backend_impl(BackendMutexT&, BackendT&, boost::false_type)
  205. {
  206. }
  207. };
  208. //! A base class for a logging sink frontend with formatting support
  209. template< typename CharT >
  210. class BOOST_LOG_NO_VTABLE basic_formatting_sink_frontend :
  211. public basic_sink_frontend
  212. {
  213. typedef basic_sink_frontend base_type;
  214. public:
  215. //! Character type
  216. typedef CharT char_type;
  217. //! Formatted string type
  218. typedef std::basic_string< char_type > string_type;
  219. //! Formatter function object type
  220. typedef basic_formatter< char_type > formatter_type;
  221. //! Output stream type
  222. typedef typename formatter_type::stream_type stream_type;
  223. #if !defined(BOOST_LOG_NO_THREADS)
  224. protected:
  225. //! Mutex type
  226. typedef typename base_type::mutex_type mutex_type;
  227. #endif
  228. private:
  229. struct formatting_context
  230. {
  231. class cleanup_guard
  232. {
  233. private:
  234. formatting_context& m_context;
  235. public:
  236. explicit cleanup_guard(formatting_context& ctx) BOOST_NOEXCEPT : m_context(ctx)
  237. {
  238. }
  239. ~cleanup_guard()
  240. {
  241. m_context.m_FormattedRecord.clear();
  242. m_context.m_FormattingStream.rdbuf()->max_size(m_context.m_FormattedRecord.max_size());
  243. m_context.m_FormattingStream.rdbuf()->storage_overflow(false);
  244. m_context.m_FormattingStream.clear();
  245. }
  246. BOOST_DELETED_FUNCTION(cleanup_guard(cleanup_guard const&))
  247. BOOST_DELETED_FUNCTION(cleanup_guard& operator=(cleanup_guard const&))
  248. };
  249. #if !defined(BOOST_LOG_NO_THREADS)
  250. //! Object version
  251. const unsigned int m_Version;
  252. #endif
  253. //! Formatted log record storage
  254. string_type m_FormattedRecord;
  255. //! Formatting stream
  256. stream_type m_FormattingStream;
  257. //! Formatter functor
  258. formatter_type m_Formatter;
  259. formatting_context() :
  260. #if !defined(BOOST_LOG_NO_THREADS)
  261. m_Version(0u),
  262. #endif
  263. m_FormattingStream(m_FormattedRecord)
  264. {
  265. m_FormattingStream.exceptions(std::ios_base::badbit | std::ios_base::failbit);
  266. }
  267. #if !defined(BOOST_LOG_NO_THREADS)
  268. formatting_context(unsigned int version, std::locale const& loc, formatter_type const& formatter) :
  269. m_Version(version),
  270. m_FormattingStream(m_FormattedRecord),
  271. m_Formatter(formatter)
  272. {
  273. m_FormattingStream.exceptions(std::ios_base::badbit | std::ios_base::failbit);
  274. m_FormattingStream.imbue(loc);
  275. }
  276. #endif
  277. };
  278. private:
  279. #if !defined(BOOST_LOG_NO_THREADS)
  280. //! State version
  281. boost::atomic< unsigned int > m_Version;
  282. //! Formatter functor
  283. formatter_type m_Formatter;
  284. //! Locale to perform formatting
  285. std::locale m_Locale;
  286. //! Formatting state
  287. thread_specific_ptr< formatting_context > m_pContext;
  288. #else
  289. //! Formatting state
  290. formatting_context m_Context;
  291. #endif // !defined(BOOST_LOG_NO_THREADS)
  292. public:
  293. /*!
  294. * \brief Initializing constructor
  295. *
  296. * \param cross_thread The flag indicates whether the sink passes log records between different threads
  297. */
  298. explicit basic_formatting_sink_frontend(bool cross_thread) :
  299. basic_sink_frontend(cross_thread)
  300. #if !defined(BOOST_LOG_NO_THREADS)
  301. , m_Version(0u)
  302. #endif
  303. {
  304. }
  305. /*!
  306. * The method sets sink-specific formatter function object
  307. */
  308. template< typename FunT >
  309. void set_formatter(FunT const& formatter)
  310. {
  311. #if !defined(BOOST_LOG_NO_THREADS)
  312. boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex());
  313. m_Formatter = formatter;
  314. m_Version.opaque_add(1u, boost::memory_order_relaxed);
  315. #else
  316. m_Context.m_Formatter = formatter;
  317. #endif
  318. }
  319. /*!
  320. * The method resets the formatter
  321. */
  322. void reset_formatter()
  323. {
  324. #if !defined(BOOST_LOG_NO_THREADS)
  325. boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex());
  326. m_Formatter.reset();
  327. m_Version.opaque_add(1u, boost::memory_order_relaxed);
  328. #else
  329. m_Context.m_Formatter.reset();
  330. #endif
  331. }
  332. /*!
  333. * The method returns the current locale used for formatting
  334. */
  335. std::locale getloc() const
  336. {
  337. #if !defined(BOOST_LOG_NO_THREADS)
  338. boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex());
  339. return m_Locale;
  340. #else
  341. return m_Context.m_FormattingStream.getloc();
  342. #endif
  343. }
  344. /*!
  345. * The method sets the locale used for formatting
  346. */
  347. void imbue(std::locale const& loc)
  348. {
  349. #if !defined(BOOST_LOG_NO_THREADS)
  350. boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex());
  351. m_Locale = loc;
  352. m_Version.opaque_add(1u, boost::memory_order_relaxed);
  353. #else
  354. m_Context.m_FormattingStream.imbue(loc);
  355. #endif
  356. }
  357. protected:
  358. //! Returns reference to the formatter
  359. formatter_type& formatter()
  360. {
  361. #if !defined(BOOST_LOG_NO_THREADS)
  362. return m_Formatter;
  363. #else
  364. return m_Context.m_Formatter;
  365. #endif
  366. }
  367. //! Feeds log record to the backend
  368. template< typename BackendMutexT, typename BackendT >
  369. void feed_record(record_view const& rec, BackendMutexT& backend_mutex, BackendT& backend)
  370. {
  371. formatting_context* context;
  372. #if !defined(BOOST_LOG_NO_THREADS)
  373. context = m_pContext.get();
  374. if (!context || context->m_Version != m_Version.load(boost::memory_order_relaxed))
  375. {
  376. {
  377. boost::log::aux::shared_lock_guard< mutex_type > lock(this->frontend_mutex());
  378. context = new formatting_context(m_Version.load(boost::memory_order_relaxed), m_Locale, m_Formatter);
  379. }
  380. m_pContext.reset(context);
  381. }
  382. #else
  383. context = &m_Context;
  384. #endif
  385. typename formatting_context::cleanup_guard cleanup(*context);
  386. try
  387. {
  388. // Perform the formatting
  389. context->m_Formatter(rec, context->m_FormattingStream);
  390. context->m_FormattingStream.flush();
  391. // Feed the record
  392. BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< BackendMutexT > lock(backend_mutex);)
  393. backend.consume(rec, context->m_FormattedRecord);
  394. }
  395. catch (...)
  396. {
  397. BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(this->frontend_mutex());)
  398. if (this->exception_handler().empty())
  399. throw;
  400. this->exception_handler()();
  401. }
  402. }
  403. //! Attempts to feeds log record to the backend, does not block if \a backend_mutex is locked
  404. template< typename BackendMutexT, typename BackendT >
  405. bool try_feed_record(record_view const& rec, BackendMutexT& backend_mutex, BackendT& backend)
  406. {
  407. #if !defined(BOOST_LOG_NO_THREADS)
  408. try
  409. {
  410. if (!backend_mutex.try_lock())
  411. return false;
  412. }
  413. catch (...)
  414. {
  415. boost::log::aux::shared_lock_guard< mutex_type > frontend_lock(this->frontend_mutex());
  416. if (this->exception_handler().empty())
  417. throw;
  418. this->exception_handler()();
  419. return false;
  420. }
  421. boost::log::aux::exclusive_auto_unlocker< BackendMutexT > unlocker(backend_mutex);
  422. #endif
  423. // No need to lock anything in the feed_record method
  424. boost::log::aux::fake_mutex m;
  425. feed_record(rec, m, backend);
  426. return true;
  427. }
  428. };
  429. namespace aux {
  430. template<
  431. typename BackendT,
  432. bool RequiresFormattingV = has_requirement<
  433. typename BackendT::frontend_requirements,
  434. formatted_records
  435. >::value
  436. >
  437. struct make_sink_frontend_base
  438. {
  439. typedef basic_sink_frontend type;
  440. };
  441. template< typename BackendT >
  442. struct make_sink_frontend_base< BackendT, true >
  443. {
  444. typedef basic_formatting_sink_frontend< typename BackendT::char_type > type;
  445. };
  446. } // namespace aux
  447. } // namespace sinks
  448. BOOST_LOG_CLOSE_NAMESPACE // namespace log
  449. } // namespace boost
  450. #include <boost/log/detail/footer.hpp>
  451. #endif // BOOST_LOG_SINKS_BASIC_SINK_FRONTEND_HPP_INCLUDED_