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