basic_sink_frontend.hpp 15 KB

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