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