basic_sink_frontend.hpp 16 KB

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