]>
Commit | Line | Data |
---|---|---|
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 core.cpp | |
9 | * \author Andrey Semashev | |
10 | * \date 19.04.2007 | |
11 | * | |
12 | * \brief This header is the Boost.Log library implementation, see the library documentation | |
13 | * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. | |
14 | */ | |
15 | ||
16 | #include <boost/log/detail/config.hpp> | |
17 | #include <cstddef> | |
18 | #include <new> | |
19 | #include <vector> | |
20 | #include <algorithm> | |
21 | #include <boost/cstdint.hpp> | |
22 | #include <boost/assert.hpp> | |
23 | #include <boost/core/swap.hpp> | |
24 | #include <boost/filesystem/path.hpp> | |
25 | #include <boost/smart_ptr/weak_ptr.hpp> | |
26 | #include <boost/smart_ptr/shared_ptr.hpp> | |
27 | #include <boost/smart_ptr/make_shared_object.hpp> | |
28 | #include <boost/range/iterator_range_core.hpp> | |
29 | #include <boost/date_time/posix_time/posix_time_types.hpp> | |
30 | #include <boost/random/taus88.hpp> | |
31 | #include <boost/move/core.hpp> | |
32 | #include <boost/move/utility_core.hpp> | |
33 | #include <boost/log/core/core.hpp> | |
34 | #include <boost/log/core/record.hpp> | |
35 | #include <boost/log/core/record_view.hpp> | |
36 | #include <boost/log/sinks/sink.hpp> | |
37 | #include <boost/log/attributes/attribute_value_set.hpp> | |
38 | #include <boost/log/detail/singleton.hpp> | |
39 | #if !defined(BOOST_LOG_NO_THREADS) | |
40 | #include <boost/thread/tss.hpp> | |
41 | #include <boost/thread/exceptions.hpp> | |
42 | #include <boost/log/detail/locks.hpp> | |
43 | #include <boost/log/detail/light_rw_mutex.hpp> | |
44 | #include <boost/log/detail/thread_id.hpp> | |
45 | #endif | |
46 | #include "unique_ptr.hpp" | |
47 | #include "default_sink.hpp" | |
48 | #include "stateless_allocator.hpp" | |
49 | #include "alignment_gap_between.hpp" | |
50 | #include <boost/log/detail/header.hpp> | |
51 | ||
52 | namespace boost { | |
53 | ||
54 | BOOST_LOG_OPEN_NAMESPACE | |
55 | ||
56 | namespace aux { | |
57 | ||
58 | BOOST_LOG_ANONYMOUS_NAMESPACE { | |
59 | ||
60 | //! Sequence shuffling algorithm. Very similar to std::random_shuffle, used for forward portability with compilers that removed it from the standard library (C++17). | |
61 | template< typename Iterator, typename RandomNumberGenerator > | |
62 | void random_shuffle(Iterator begin, Iterator end, RandomNumberGenerator& rng) | |
63 | { | |
64 | Iterator it = begin; | |
65 | ++it; | |
66 | while (it != end) | |
67 | { | |
68 | Iterator where = begin + rng() % (it - begin + 1u); | |
69 | if (where != it) | |
70 | boost::swap(*where, *it); | |
71 | ++it; | |
72 | } | |
73 | } | |
74 | ||
75 | } // namespace | |
76 | ||
77 | } // namespace aux | |
78 | ||
79 | //! Private record data information, with core-specific structures | |
80 | struct record_view::private_data : | |
81 | public public_data | |
82 | { | |
83 | //! Underlying memory allocator | |
84 | typedef boost::log::aux::stateless_allocator< char > stateless_allocator; | |
85 | //! Sink pointer type | |
86 | typedef weak_ptr< sinks::sink > sink_ptr; | |
87 | //! Iterator range with pointers to the accepting sinks | |
88 | typedef iterator_range< sink_ptr* > sink_list; | |
89 | ||
90 | private: | |
91 | //! Number of sinks accepting the record | |
92 | uint32_t m_accepting_sink_count; | |
93 | //! Maximum number of sinks accepting the record | |
94 | const uint32_t m_accepting_sink_capacity; | |
95 | //! The flag indicates that the record has to be detached from the current thread | |
96 | bool m_detach_from_thread_needed; | |
97 | ||
98 | private: | |
99 | //! Initializing constructor | |
100 | private_data(BOOST_RV_REF(attribute_value_set) values, uint32_t capacity) BOOST_NOEXCEPT : | |
101 | public_data(boost::move(values)), | |
102 | m_accepting_sink_count(0), | |
103 | m_accepting_sink_capacity(capacity), | |
104 | m_detach_from_thread_needed(false) | |
105 | { | |
106 | } | |
107 | ||
108 | public: | |
109 | //! Creates the object with the specified capacity | |
110 | static private_data* create(BOOST_RV_REF(attribute_value_set) values, uint32_t capacity) | |
111 | { | |
112 | private_data* p = reinterpret_cast< private_data* >(stateless_allocator().allocate | |
113 | ( | |
114 | sizeof(private_data) + | |
115 | boost::log::aux::alignment_gap_between< private_data, sink_ptr >::value + | |
116 | capacity * sizeof(sink_ptr) | |
117 | )); | |
118 | new (p) private_data(boost::move(values), capacity); | |
119 | return p; | |
120 | } | |
121 | ||
122 | //! Destroys the object and frees the underlying storage | |
123 | void destroy() BOOST_NOEXCEPT | |
124 | { | |
125 | sink_ptr* psink = begin(); | |
126 | for (uint32_t i = 0u, n = m_accepting_sink_count; i < n; ++i) | |
127 | { | |
128 | psink[i].~sink_ptr(); | |
129 | } | |
130 | ||
131 | const uint32_t capacity = m_accepting_sink_capacity; | |
132 | this->~private_data(); | |
133 | ||
134 | stateless_allocator().deallocate | |
135 | ( | |
136 | reinterpret_cast< stateless_allocator::pointer >(this), | |
137 | sizeof(private_data) + | |
138 | boost::log::aux::alignment_gap_between< private_data, sink_ptr >::value + | |
139 | capacity * sizeof(sink_ptr) | |
140 | ); | |
141 | } | |
142 | ||
143 | //! Returns iterator range with the pointers to the accepting sinks | |
144 | sink_list get_accepting_sinks() BOOST_NOEXCEPT | |
145 | { | |
146 | sink_ptr* p = begin(); | |
147 | return sink_list(p, p + m_accepting_sink_count); | |
148 | } | |
149 | ||
150 | //! Adds an accepting sink | |
151 | void push_back_accepting_sink(shared_ptr< sinks::sink > const& sink) | |
152 | { | |
153 | BOOST_ASSERT(m_accepting_sink_count < m_accepting_sink_capacity); | |
154 | sink_ptr* p = begin() + m_accepting_sink_count; | |
155 | new (p) sink_ptr(sink); | |
156 | ++m_accepting_sink_count; | |
157 | m_detach_from_thread_needed |= sink->is_cross_thread(); | |
158 | } | |
159 | ||
160 | //! Returns the number of accepting sinks | |
161 | uint32_t accepting_sink_count() const BOOST_NOEXCEPT { return m_accepting_sink_count; } | |
162 | ||
163 | //! Returns the flag indicating whether it is needed to detach the record from the current thread | |
164 | bool is_detach_from_thread_needed() const BOOST_NOEXCEPT { return m_detach_from_thread_needed; } | |
165 | ||
166 | BOOST_DELETED_FUNCTION(private_data(private_data const&)) | |
167 | BOOST_DELETED_FUNCTION(private_data& operator= (private_data const&)) | |
168 | ||
169 | private: | |
170 | //! Returns a pointer to the first accepting sink | |
171 | sink_ptr* begin() BOOST_NOEXCEPT | |
172 | { | |
173 | return reinterpret_cast< sink_ptr* > | |
174 | ( | |
175 | reinterpret_cast< char* >(this) + | |
176 | sizeof(private_data) + | |
177 | boost::log::aux::alignment_gap_between< private_data, sink_ptr >::value | |
178 | ); | |
179 | } | |
180 | }; | |
181 | ||
182 | //! Destructor | |
183 | BOOST_LOG_API void record_view::public_data::destroy(const public_data* p) BOOST_NOEXCEPT | |
184 | { | |
185 | const_cast< private_data* >(static_cast< const private_data* >(p))->destroy(); | |
186 | } | |
187 | ||
188 | //! The function ensures that the log record does not depend on any thread-specific data. | |
189 | BOOST_LOG_API record_view record::lock() | |
190 | { | |
191 | BOOST_ASSERT(m_impl != NULL); | |
192 | ||
193 | record_view::private_data* const impl = static_cast< record_view::private_data* >(m_impl); | |
194 | if (impl->is_detach_from_thread_needed()) | |
195 | { | |
196 | attribute_value_set::const_iterator | |
197 | it = impl->m_attribute_values.begin(), | |
198 | end = impl->m_attribute_values.end(); | |
199 | for (; it != end; ++it) | |
200 | { | |
201 | // Yep, a bit hackish. I'll need a better backdoor to do it gracefully. | |
202 | const_cast< attribute_value_set::mapped_type& >(it->second).detach_from_thread(); | |
203 | } | |
204 | } | |
205 | ||
206 | // Move the implementation to the view | |
207 | m_impl = NULL; | |
208 | return record_view(impl); | |
209 | } | |
210 | ||
211 | //! Logging system implementation | |
212 | struct core::implementation : | |
213 | public log::aux::lazy_singleton< | |
214 | implementation, | |
215 | core_ptr | |
216 | > | |
217 | { | |
218 | public: | |
219 | //! Base type of singleton holder | |
220 | typedef log::aux::lazy_singleton< | |
221 | implementation, | |
222 | core_ptr | |
223 | > base_type; | |
224 | ||
225 | #if !defined(BOOST_LOG_NO_THREADS) | |
226 | //! Read lock type | |
227 | typedef log::aux::shared_lock_guard< log::aux::light_rw_mutex > scoped_read_lock; | |
228 | //! Write lock type | |
229 | typedef log::aux::exclusive_lock_guard< log::aux::light_rw_mutex > scoped_write_lock; | |
230 | #endif | |
231 | ||
232 | //! Sinks container type | |
233 | typedef std::vector< shared_ptr< sinks::sink > > sink_list; | |
234 | ||
235 | //! Thread-specific data | |
236 | struct thread_data | |
237 | { | |
238 | //! Thread-specific attribute set | |
239 | attribute_set m_thread_attributes; | |
240 | //! Random number generator for shuffling | |
241 | random::taus88 m_rng; | |
242 | ||
243 | thread_data() : m_rng(get_random_seed()) | |
244 | { | |
245 | } | |
246 | ||
247 | private: | |
248 | //! Creates a seed for RNG | |
249 | static uint32_t get_random_seed() | |
250 | { | |
251 | uint32_t seed = static_cast< uint32_t >(posix_time::microsec_clock::universal_time().time_of_day().ticks()); | |
252 | #if !defined(BOOST_LOG_NO_THREADS) | |
253 | seed += static_cast< uint32_t >(log::aux::this_thread::get_id().native_id()); | |
254 | #endif | |
255 | return seed; | |
256 | } | |
257 | }; | |
258 | ||
259 | public: | |
260 | #if !defined(BOOST_LOG_NO_THREADS) | |
261 | //! Synchronization mutex | |
262 | log::aux::light_rw_mutex m_mutex; | |
263 | #endif | |
264 | ||
265 | //! List of sinks involved into output | |
266 | sink_list m_sinks; | |
267 | //! Default sink | |
268 | const shared_ptr< sinks::sink > m_default_sink; | |
269 | ||
270 | //! Global attribute set | |
271 | attribute_set m_global_attributes; | |
272 | #if !defined(BOOST_LOG_NO_THREADS) | |
273 | //! Thread-specific data | |
274 | thread_specific_ptr< thread_data > m_thread_data; | |
275 | ||
276 | #if defined(BOOST_LOG_USE_COMPILER_TLS) | |
277 | //! Cached pointer to the thread-specific data | |
278 | static BOOST_LOG_TLS thread_data* m_thread_data_cache; | |
279 | #endif | |
280 | ||
281 | #else | |
282 | //! Thread-specific data | |
283 | log::aux::unique_ptr< thread_data > m_thread_data; | |
284 | #endif | |
285 | ||
286 | //! The global state of logging | |
287 | volatile bool m_enabled; | |
288 | //! Global filter | |
289 | filter m_filter; | |
290 | ||
291 | //! Exception handler | |
292 | exception_handler_type m_exception_handler; | |
293 | ||
294 | public: | |
295 | //! Constructor | |
296 | implementation() : | |
297 | m_default_sink(boost::make_shared< sinks::aux::default_sink >()), | |
298 | m_enabled(true) | |
299 | { | |
300 | } | |
301 | ||
302 | //! Opens a record | |
303 | template< typename SourceAttributesT > | |
304 | BOOST_FORCEINLINE record open_record(BOOST_FWD_REF(SourceAttributesT) source_attributes) | |
305 | { | |
306 | record_view::private_data* rec_impl = NULL; | |
307 | bool invoke_exception_handler = true; | |
308 | ||
309 | // Try a quick win first | |
310 | if (m_enabled) try | |
311 | { | |
312 | thread_data* tsd = get_thread_data(); | |
313 | ||
314 | // Lock the core to be safe against any attribute or sink set modifications | |
315 | BOOST_LOG_EXPR_IF_MT(scoped_read_lock lock(m_mutex);) | |
316 | ||
317 | if (m_enabled) | |
318 | { | |
319 | // Compose a view of attribute values (unfrozen, yet) | |
320 | attribute_value_set attr_values(boost::forward< SourceAttributesT >(source_attributes), tsd->m_thread_attributes, m_global_attributes); | |
321 | if (m_filter(attr_values)) | |
322 | { | |
323 | // The global filter passed, trying the sinks | |
324 | attribute_value_set* values = &attr_values; | |
325 | ||
326 | // apply_sink_filter will invoke the exception handler if it has to | |
327 | invoke_exception_handler = false; | |
328 | ||
329 | if (!m_sinks.empty()) | |
330 | { | |
331 | uint32_t remaining_capacity = static_cast< uint32_t >(m_sinks.size()); | |
332 | sink_list::iterator it = m_sinks.begin(), end = m_sinks.end(); | |
333 | for (; it != end; ++it, --remaining_capacity) | |
334 | { | |
335 | apply_sink_filter(*it, rec_impl, values, remaining_capacity); | |
336 | } | |
337 | } | |
338 | else | |
339 | { | |
340 | // Use the default sink | |
341 | apply_sink_filter(m_default_sink, rec_impl, values, 1); | |
342 | } | |
343 | ||
344 | invoke_exception_handler = true; | |
345 | ||
346 | if (rec_impl && rec_impl->accepting_sink_count() == 0) | |
347 | { | |
348 | // No sinks accepted the record | |
349 | rec_impl->destroy(); | |
350 | rec_impl = NULL; | |
351 | goto done; | |
352 | } | |
353 | ||
354 | // Some sinks have accepted the record | |
355 | values->freeze(); | |
356 | } | |
357 | } | |
358 | } | |
359 | #if !defined(BOOST_LOG_NO_THREADS) | |
360 | catch (thread_interrupted&) | |
361 | { | |
362 | if (rec_impl) | |
363 | rec_impl->destroy(); | |
364 | throw; | |
365 | } | |
366 | #endif // !defined(BOOST_LOG_NO_THREADS) | |
367 | catch (...) | |
368 | { | |
369 | if (rec_impl) | |
370 | { | |
371 | rec_impl->destroy(); | |
372 | rec_impl = NULL; | |
373 | } | |
374 | ||
375 | if (invoke_exception_handler) | |
376 | { | |
377 | // Lock the core to be safe against any attribute or sink set modifications | |
378 | BOOST_LOG_EXPR_IF_MT(scoped_read_lock lock(m_mutex);) | |
379 | if (m_exception_handler.empty()) | |
380 | throw; | |
381 | ||
382 | m_exception_handler(); | |
383 | } | |
384 | else | |
385 | throw; | |
386 | } | |
387 | ||
388 | done: | |
389 | return record(rec_impl); | |
390 | } | |
391 | ||
392 | //! The method returns the current thread-specific data | |
393 | thread_data* get_thread_data() | |
394 | { | |
395 | #if defined(BOOST_LOG_USE_COMPILER_TLS) | |
396 | thread_data* p = m_thread_data_cache; | |
397 | #else | |
398 | thread_data* p = m_thread_data.get(); | |
399 | #endif | |
400 | if (!p) | |
401 | { | |
402 | init_thread_data(); | |
403 | #if defined(BOOST_LOG_USE_COMPILER_TLS) | |
404 | p = m_thread_data_cache; | |
405 | #else | |
406 | p = m_thread_data.get(); | |
407 | #endif | |
408 | } | |
409 | return p; | |
410 | } | |
411 | ||
412 | //! The function initializes the logging system | |
413 | static void init_instance() | |
414 | { | |
415 | base_type::get_instance().reset(new core()); | |
416 | } | |
417 | ||
418 | private: | |
419 | //! The method initializes thread-specific data | |
420 | void init_thread_data() | |
421 | { | |
422 | BOOST_LOG_EXPR_IF_MT(scoped_write_lock lock(m_mutex);) | |
423 | if (!m_thread_data.get()) | |
424 | { | |
425 | log::aux::unique_ptr< thread_data > p(new thread_data()); | |
426 | m_thread_data.reset(p.get()); | |
427 | #if defined(BOOST_LOG_USE_COMPILER_TLS) | |
428 | m_thread_data_cache = p.release(); | |
429 | #else | |
430 | p.release(); | |
431 | #endif | |
432 | } | |
433 | } | |
434 | ||
435 | //! Invokes sink-specific filter and adds the sink to the record if the filter passes the log record | |
436 | void apply_sink_filter(shared_ptr< sinks::sink > const& sink, record_view::private_data*& rec_impl, attribute_value_set*& attr_values, uint32_t remaining_capacity) | |
437 | { | |
438 | try | |
439 | { | |
440 | if (sink->will_consume(*attr_values)) | |
441 | { | |
442 | // If at least one sink accepts the record, it's time to create it | |
443 | record_view::private_data* impl = rec_impl; | |
444 | if (!impl) | |
445 | { | |
446 | rec_impl = impl = record_view::private_data::create(boost::move(*attr_values), remaining_capacity); | |
447 | attr_values = &impl->m_attribute_values; | |
448 | } | |
449 | ||
450 | impl->push_back_accepting_sink(sink); | |
451 | } | |
452 | } | |
453 | #if !defined(BOOST_LOG_NO_THREADS) | |
454 | catch (thread_interrupted&) | |
455 | { | |
456 | throw; | |
457 | } | |
458 | #endif // !defined(BOOST_LOG_NO_THREADS) | |
459 | catch (...) | |
460 | { | |
461 | if (m_exception_handler.empty()) | |
462 | throw; | |
463 | m_exception_handler(); | |
464 | } | |
465 | } | |
466 | }; | |
467 | ||
468 | #if defined(BOOST_LOG_USE_COMPILER_TLS) | |
469 | //! Cached pointer to the thread-specific data | |
470 | BOOST_LOG_TLS core::implementation::thread_data* core::implementation::m_thread_data_cache = NULL; | |
471 | #endif // defined(BOOST_LOG_USE_COMPILER_TLS) | |
472 | ||
473 | //! Logging system constructor | |
474 | core::core() : | |
475 | m_impl(new implementation()) | |
476 | { | |
477 | } | |
478 | ||
479 | //! Logging system destructor | |
480 | core::~core() | |
481 | { | |
482 | delete m_impl; | |
483 | m_impl = NULL; | |
484 | } | |
485 | ||
486 | //! The method returns a pointer to the logging system instance | |
487 | BOOST_LOG_API core_ptr core::get() | |
488 | { | |
489 | return implementation::get(); | |
490 | } | |
491 | ||
492 | //! The method enables or disables logging and returns the previous state of logging flag | |
493 | BOOST_LOG_API bool core::set_logging_enabled(bool enabled) | |
494 | { | |
495 | BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) | |
496 | const bool old_value = m_impl->m_enabled; | |
497 | m_impl->m_enabled = enabled; | |
498 | return old_value; | |
499 | } | |
500 | ||
501 | //! The method allows to detect if logging is enabled | |
502 | BOOST_LOG_API bool core::get_logging_enabled() const | |
503 | { | |
504 | // Should have a read barrier here, but for performance reasons it is omitted. | |
505 | // The function should be used as a quick check and doesn't need to be reliable. | |
506 | return m_impl->m_enabled; | |
507 | } | |
508 | ||
509 | //! The method adds a new sink | |
510 | BOOST_LOG_API void core::add_sink(shared_ptr< sinks::sink > const& s) | |
511 | { | |
512 | BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) | |
513 | implementation::sink_list::iterator it = | |
514 | std::find(m_impl->m_sinks.begin(), m_impl->m_sinks.end(), s); | |
515 | if (it == m_impl->m_sinks.end()) | |
516 | m_impl->m_sinks.push_back(s); | |
517 | } | |
518 | ||
519 | //! The method removes the sink from the output | |
520 | BOOST_LOG_API void core::remove_sink(shared_ptr< sinks::sink > const& s) | |
521 | { | |
522 | BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) | |
523 | implementation::sink_list::iterator it = | |
524 | std::find(m_impl->m_sinks.begin(), m_impl->m_sinks.end(), s); | |
525 | if (it != m_impl->m_sinks.end()) | |
526 | m_impl->m_sinks.erase(it); | |
527 | } | |
528 | ||
529 | //! The method removes all registered sinks from the output | |
530 | BOOST_LOG_API void core::remove_all_sinks() | |
531 | { | |
532 | BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) | |
533 | m_impl->m_sinks.clear(); | |
534 | } | |
535 | ||
536 | ||
537 | //! The method adds an attribute to the global attribute set | |
538 | BOOST_LOG_API std::pair< attribute_set::iterator, bool > | |
539 | core::add_global_attribute(attribute_name const& name, attribute const& attr) | |
540 | { | |
541 | BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) | |
542 | return m_impl->m_global_attributes.insert(name, attr); | |
543 | } | |
544 | ||
545 | //! The method removes an attribute from the global attribute set | |
546 | BOOST_LOG_API void core::remove_global_attribute(attribute_set::iterator it) | |
547 | { | |
548 | BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) | |
549 | m_impl->m_global_attributes.erase(it); | |
550 | } | |
551 | ||
552 | //! The method returns the complete set of currently registered global attributes | |
553 | BOOST_LOG_API attribute_set core::get_global_attributes() const | |
554 | { | |
555 | BOOST_LOG_EXPR_IF_MT(implementation::scoped_read_lock lock(m_impl->m_mutex);) | |
556 | return m_impl->m_global_attributes; | |
557 | } | |
558 | ||
559 | //! The method replaces the complete set of currently registered global attributes with the provided set | |
560 | BOOST_LOG_API void core::set_global_attributes(attribute_set const& attrs) | |
561 | { | |
562 | BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) | |
563 | m_impl->m_global_attributes = attrs; | |
564 | } | |
565 | ||
566 | //! The method adds an attribute to the thread-specific attribute set | |
567 | BOOST_LOG_API std::pair< attribute_set::iterator, bool > | |
568 | core::add_thread_attribute(attribute_name const& name, attribute const& attr) | |
569 | { | |
570 | implementation::thread_data* p = m_impl->get_thread_data(); | |
571 | return p->m_thread_attributes.insert(name, attr); | |
572 | } | |
573 | ||
574 | //! The method removes an attribute from the thread-specific attribute set | |
575 | BOOST_LOG_API void core::remove_thread_attribute(attribute_set::iterator it) | |
576 | { | |
577 | implementation::thread_data* p = m_impl->get_thread_data(); | |
578 | p->m_thread_attributes.erase(it); | |
579 | } | |
580 | ||
581 | //! The method returns the complete set of currently registered thread-specific attributes | |
582 | BOOST_LOG_API attribute_set core::get_thread_attributes() const | |
583 | { | |
584 | implementation::thread_data* p = m_impl->get_thread_data(); | |
585 | return p->m_thread_attributes; | |
586 | } | |
587 | //! The method replaces the complete set of currently registered thread-specific attributes with the provided set | |
588 | BOOST_LOG_API void core::set_thread_attributes(attribute_set const& attrs) | |
589 | { | |
590 | implementation::thread_data* p = m_impl->get_thread_data(); | |
591 | p->m_thread_attributes = attrs; | |
592 | } | |
593 | ||
594 | //! An internal method to set the global filter | |
595 | BOOST_LOG_API void core::set_filter(filter const& filter) | |
596 | { | |
597 | BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) | |
598 | m_impl->m_filter = filter; | |
599 | } | |
600 | ||
601 | //! The method removes the global logging filter | |
602 | BOOST_LOG_API void core::reset_filter() | |
603 | { | |
604 | BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) | |
605 | m_impl->m_filter.reset(); | |
606 | } | |
607 | ||
608 | //! The method sets exception handler function | |
609 | BOOST_LOG_API void core::set_exception_handler(exception_handler_type const& handler) | |
610 | { | |
611 | BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) | |
612 | m_impl->m_exception_handler = handler; | |
613 | } | |
614 | ||
615 | //! The method performs flush on all registered sinks. | |
616 | BOOST_LOG_API void core::flush() | |
617 | { | |
618 | // Acquire exclusive lock to prevent any logging attempts while flushing | |
619 | BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) | |
620 | implementation::sink_list::iterator it = m_impl->m_sinks.begin(), end = m_impl->m_sinks.end(); | |
621 | for (; it != end; ++it) | |
622 | { | |
623 | try | |
624 | { | |
625 | it->get()->flush(); | |
626 | } | |
627 | #if !defined(BOOST_LOG_NO_THREADS) | |
628 | catch (thread_interrupted&) | |
629 | { | |
630 | throw; | |
631 | } | |
632 | #endif // !defined(BOOST_LOG_NO_THREADS) | |
633 | catch (...) | |
634 | { | |
635 | if (m_impl->m_exception_handler.empty()) | |
636 | throw; | |
637 | m_impl->m_exception_handler(); | |
638 | } | |
639 | } | |
640 | } | |
641 | ||
642 | //! The method attempts to open a new record to be written | |
643 | BOOST_LOG_API record core::open_record(attribute_set const& source_attributes) | |
644 | { | |
645 | return m_impl->open_record(source_attributes); | |
646 | } | |
647 | ||
648 | //! The method attempts to open a new record to be written | |
649 | BOOST_LOG_API record core::open_record(attribute_value_set const& source_attributes) | |
650 | { | |
651 | return m_impl->open_record(source_attributes); | |
652 | } | |
653 | ||
654 | //! The method attempts to open a new record to be written. | |
655 | BOOST_LOG_API record core::open_record_move(attribute_value_set& source_attributes) | |
656 | { | |
657 | return m_impl->open_record(boost::move(source_attributes)); | |
658 | } | |
659 | ||
660 | //! The method pushes the record | |
661 | BOOST_LOG_API void core::push_record_move(record& rec) | |
662 | { | |
663 | try | |
664 | { | |
665 | record_view rec_view(rec.lock()); | |
666 | record_view::private_data* data = static_cast< record_view::private_data* >(rec_view.m_impl.get()); | |
667 | ||
668 | typedef std::vector< shared_ptr< sinks::sink > > accepting_sinks_t; | |
669 | accepting_sinks_t accepting_sinks(data->accepting_sink_count()); | |
670 | shared_ptr< sinks::sink >* const begin = &*accepting_sinks.begin(); | |
671 | shared_ptr< sinks::sink >* end = begin; | |
672 | ||
673 | // Lock sinks that are willing to consume the record | |
674 | record_view::private_data::sink_list weak_sinks = data->get_accepting_sinks(); | |
675 | record_view::private_data::sink_list::iterator | |
676 | weak_it = weak_sinks.begin(), | |
677 | weak_end = weak_sinks.end(); | |
678 | for (; weak_it != weak_end; ++weak_it) | |
679 | { | |
680 | shared_ptr< sinks::sink >& last = *end; | |
681 | weak_it->lock().swap(last); | |
682 | if (last.get()) | |
683 | ++end; | |
684 | } | |
685 | ||
686 | bool shuffled = (end - begin) <= 1; | |
687 | shared_ptr< sinks::sink >* it = begin; | |
688 | while (true) try | |
689 | { | |
690 | // First try to distribute load between different sinks | |
691 | bool all_locked = true; | |
692 | while (it != end) | |
693 | { | |
694 | if (it->get()->try_consume(rec_view)) | |
695 | { | |
696 | --end; | |
697 | end->swap(*it); | |
698 | all_locked = false; | |
699 | } | |
700 | else | |
701 | ++it; | |
702 | } | |
703 | ||
704 | it = begin; | |
705 | if (begin != end) | |
706 | { | |
707 | if (all_locked) | |
708 | { | |
709 | // If all sinks are busy then block on any | |
710 | if (!shuffled) | |
711 | { | |
712 | implementation::thread_data* tsd = m_impl->get_thread_data(); | |
713 | log::aux::random_shuffle(begin, end, tsd->m_rng); | |
714 | shuffled = true; | |
715 | } | |
716 | ||
717 | it->get()->consume(rec_view); | |
718 | --end; | |
719 | end->swap(*it); | |
720 | } | |
721 | } | |
722 | else | |
723 | break; | |
724 | } | |
725 | #if !defined(BOOST_LOG_NO_THREADS) | |
726 | catch (thread_interrupted&) | |
727 | { | |
728 | throw; | |
729 | } | |
730 | #endif // !defined(BOOST_LOG_NO_THREADS) | |
731 | catch (...) | |
732 | { | |
733 | // Lock the core to be safe against any attribute or sink set modifications | |
734 | BOOST_LOG_EXPR_IF_MT(implementation::scoped_read_lock lock(m_impl->m_mutex);) | |
735 | if (m_impl->m_exception_handler.empty()) | |
736 | throw; | |
737 | ||
738 | m_impl->m_exception_handler(); | |
739 | ||
740 | // Skip the sink that failed to consume the record | |
741 | --end; | |
742 | end->swap(*it); | |
743 | } | |
744 | } | |
745 | #if !defined(BOOST_LOG_NO_THREADS) | |
746 | catch (thread_interrupted&) | |
747 | { | |
748 | throw; | |
749 | } | |
750 | #endif // !defined(BOOST_LOG_NO_THREADS) | |
751 | catch (...) | |
752 | { | |
753 | // Lock the core to be safe against any attribute or sink set modifications | |
754 | BOOST_LOG_EXPR_IF_MT(implementation::scoped_read_lock lock(m_impl->m_mutex);) | |
755 | if (m_impl->m_exception_handler.empty()) | |
756 | throw; | |
757 | ||
758 | m_impl->m_exception_handler(); | |
759 | } | |
760 | } | |
761 | ||
762 | BOOST_LOG_CLOSE_NAMESPACE // namespace log | |
763 | ||
764 | } // namespace boost | |
765 | ||
766 | #include <boost/log/detail/footer.hpp> |