]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/log/src/text_file_backend.cpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / libs / log / src / text_file_backend.cpp
CommitLineData
7c673cae
FG
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 text_file_backend.cpp
9 * \author Andrey Semashev
10 * \date 09.06.2009
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 <ctime>
18#include <cctype>
19#include <cwctype>
20#include <ctime>
21#include <cstdio>
22#include <cstdlib>
23#include <cstddef>
24#include <list>
25#include <string>
26#include <locale>
27#include <ostream>
28#include <sstream>
29#include <iterator>
30#include <algorithm>
31#include <stdexcept>
32#include <boost/ref.hpp>
33#include <boost/bind.hpp>
34#include <boost/cstdint.hpp>
35#include <boost/smart_ptr/make_shared_object.hpp>
36#include <boost/enable_shared_from_this.hpp>
37#include <boost/throw_exception.hpp>
38#include <boost/mpl/if.hpp>
39#include <boost/type_traits/is_same.hpp>
40#include <boost/system/error_code.hpp>
41#include <boost/system/system_error.hpp>
42#include <boost/filesystem/path.hpp>
43#include <boost/filesystem/fstream.hpp>
44#include <boost/filesystem/operations.hpp>
45#include <boost/filesystem/convenience.hpp>
46#include <boost/intrusive/list.hpp>
47#include <boost/intrusive/list_hook.hpp>
48#include <boost/intrusive/options.hpp>
49#include <boost/date_time/posix_time/posix_time.hpp>
50#include <boost/date_time/gregorian/gregorian_types.hpp>
51#include <boost/spirit/home/qi/numeric/numeric_utils.hpp>
52#include <boost/log/detail/singleton.hpp>
53#include <boost/log/detail/light_function.hpp>
54#include <boost/log/exceptions.hpp>
55#include <boost/log/attributes/time_traits.hpp>
56#include <boost/log/sinks/text_file_backend.hpp>
57#include "unique_ptr.hpp"
58
59#if !defined(BOOST_LOG_NO_THREADS)
60#include <boost/thread/locks.hpp>
61#include <boost/thread/mutex.hpp>
62#endif // !defined(BOOST_LOG_NO_THREADS)
63
64#include <boost/log/detail/header.hpp>
65
66namespace qi = boost::spirit::qi;
67
68namespace boost {
69
70BOOST_LOG_OPEN_NAMESPACE
71
72namespace sinks {
73
74BOOST_LOG_ANONYMOUS_NAMESPACE {
75
76 typedef filesystem::filesystem_error filesystem_error;
77
78 //! A possible Boost.Filesystem extension - renames or moves the file to the target storage
79 inline void move_file(
80 filesystem::path const& from,
81 filesystem::path const& to)
82 {
83#if defined(BOOST_WINDOWS_API)
84 // On Windows MoveFile already does what we need
85 filesystem::rename(from, to);
86#else
87 // On POSIX rename fails if the target points to a different device
88 system::error_code ec;
89 filesystem::rename(from, to, ec);
90 if (ec)
91 {
92 if (ec.value() == system::errc::cross_device_link)
93 {
94 // Attempt to manually move the file instead
95 filesystem::copy_file(from, to);
96 filesystem::remove(from);
97 }
98 else
99 {
100 BOOST_THROW_EXCEPTION(filesystem_error("failed to move file to another location", from, to, ec));
101 }
102 }
103#endif
104 }
105
106 typedef filesystem::path::string_type path_string_type;
107 typedef path_string_type::value_type path_char_type;
108
109 //! An auxiliary traits that contain various constants and functions regarding string and character operations
110 template< typename CharT >
111 struct file_char_traits;
112
113 template< >
114 struct file_char_traits< char >
115 {
116 typedef char char_type;
117
118 static const char_type percent = '%';
119 static const char_type number_placeholder = 'N';
120 static const char_type day_placeholder = 'd';
121 static const char_type month_placeholder = 'm';
122 static const char_type year_placeholder = 'y';
123 static const char_type full_year_placeholder = 'Y';
124 static const char_type frac_sec_placeholder = 'f';
125 static const char_type seconds_placeholder = 'S';
126 static const char_type minutes_placeholder = 'M';
127 static const char_type hours_placeholder = 'H';
128 static const char_type space = ' ';
129 static const char_type plus = '+';
130 static const char_type minus = '-';
131 static const char_type zero = '0';
132 static const char_type dot = '.';
133 static const char_type newline = '\n';
134
135 static bool is_digit(char c)
136 {
137 using namespace std;
138 return (isdigit(c) != 0);
139 }
140 static std::string default_file_name_pattern() { return "%5N.log"; }
141 };
142
143#ifndef BOOST_LOG_BROKEN_STATIC_CONSTANTS_LINKAGE
144 const file_char_traits< char >::char_type file_char_traits< char >::percent;
145 const file_char_traits< char >::char_type file_char_traits< char >::number_placeholder;
146 const file_char_traits< char >::char_type file_char_traits< char >::day_placeholder;
147 const file_char_traits< char >::char_type file_char_traits< char >::month_placeholder;
148 const file_char_traits< char >::char_type file_char_traits< char >::year_placeholder;
149 const file_char_traits< char >::char_type file_char_traits< char >::full_year_placeholder;
150 const file_char_traits< char >::char_type file_char_traits< char >::frac_sec_placeholder;
151 const file_char_traits< char >::char_type file_char_traits< char >::seconds_placeholder;
152 const file_char_traits< char >::char_type file_char_traits< char >::minutes_placeholder;
153 const file_char_traits< char >::char_type file_char_traits< char >::hours_placeholder;
154 const file_char_traits< char >::char_type file_char_traits< char >::space;
155 const file_char_traits< char >::char_type file_char_traits< char >::plus;
156 const file_char_traits< char >::char_type file_char_traits< char >::minus;
157 const file_char_traits< char >::char_type file_char_traits< char >::zero;
158 const file_char_traits< char >::char_type file_char_traits< char >::dot;
159 const file_char_traits< char >::char_type file_char_traits< char >::newline;
160#endif // BOOST_LOG_BROKEN_STATIC_CONSTANTS_LINKAGE
161
162 template< >
163 struct file_char_traits< wchar_t >
164 {
165 typedef wchar_t char_type;
166
167 static const char_type percent = L'%';
168 static const char_type number_placeholder = L'N';
169 static const char_type day_placeholder = L'd';
170 static const char_type month_placeholder = L'm';
171 static const char_type year_placeholder = L'y';
172 static const char_type full_year_placeholder = L'Y';
173 static const char_type frac_sec_placeholder = L'f';
174 static const char_type seconds_placeholder = L'S';
175 static const char_type minutes_placeholder = L'M';
176 static const char_type hours_placeholder = L'H';
177 static const char_type space = L' ';
178 static const char_type plus = L'+';
179 static const char_type minus = L'-';
180 static const char_type zero = L'0';
181 static const char_type dot = L'.';
182 static const char_type newline = L'\n';
183
184 static bool is_digit(wchar_t c)
185 {
186 using namespace std;
187 return (iswdigit(c) != 0);
188 }
189 static std::wstring default_file_name_pattern() { return L"%5N.log"; }
190 };
191
192#ifndef BOOST_LOG_BROKEN_STATIC_CONSTANTS_LINKAGE
193 const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::percent;
194 const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::number_placeholder;
195 const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::day_placeholder;
196 const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::month_placeholder;
197 const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::year_placeholder;
198 const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::full_year_placeholder;
199 const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::frac_sec_placeholder;
200 const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::seconds_placeholder;
201 const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::minutes_placeholder;
202 const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::hours_placeholder;
203 const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::space;
204 const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::plus;
205 const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::minus;
206 const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::zero;
207 const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::dot;
208 const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::newline;
209#endif // BOOST_LOG_BROKEN_STATIC_CONSTANTS_LINKAGE
210
211 //! Date and time formatter
212 class date_and_time_formatter
213 {
214 public:
215 typedef path_string_type result_type;
216
217 private:
218 typedef date_time::time_facet< posix_time::ptime, path_char_type > time_facet_type;
219
220 private:
221 mutable time_facet_type m_Facet;
222 mutable std::basic_ostringstream< path_char_type > m_Stream;
223
224 public:
225 //! Constructor
226 date_and_time_formatter() : m_Facet(1u)
227 {
228 }
229 //! Copy constructor
230 date_and_time_formatter(date_and_time_formatter const& that) : m_Facet(1u)
231 {
232 }
233 //! The method formats the current date and time according to the format string str and writes the result into it
234 path_string_type operator()(path_string_type const& pattern, unsigned int counter) const
235 {
236 m_Facet.format(pattern.c_str());
237 m_Stream.str(path_string_type());
238 // Note: the regular operator<< fails because std::use_facet fails to find the facet in the locale because
239 // the facet type in Boost.DateTime has hidden visibility. See this ticket:
240 // https://svn.boost.org/trac/boost/ticket/11707
241 std::ostreambuf_iterator< path_char_type > sbuf_it(m_Stream);
242 m_Facet.put(sbuf_it, m_Stream, m_Stream.fill(), boost::log::attributes::local_time_traits::get_clock());
243 if (m_Stream.good())
244 {
245 return m_Stream.str();
246 }
247 else
248 {
249 m_Stream.clear();
250 return pattern;
251 }
252 }
253
254 BOOST_DELETED_FUNCTION(date_and_time_formatter& operator= (date_and_time_formatter const&))
255 };
256
257 //! The functor formats the file counter into the file name
258 class file_counter_formatter
259 {
260 public:
261 typedef path_string_type result_type;
262
263 private:
264 //! The position in the pattern where the file counter placeholder is
265 path_string_type::size_type m_FileCounterPosition;
266 //! File counter width
267 std::streamsize m_Width;
268 //! The file counter formatting stream
269 mutable std::basic_ostringstream< path_char_type > m_Stream;
270
271 public:
272 //! Initializing constructor
273 file_counter_formatter(path_string_type::size_type pos, unsigned int width) :
274 m_FileCounterPosition(pos),
275 m_Width(width)
276 {
277 typedef file_char_traits< path_char_type > traits_t;
278 m_Stream.fill(traits_t::zero);
279 }
280 //! Copy constructor
281 file_counter_formatter(file_counter_formatter const& that) :
282 m_FileCounterPosition(that.m_FileCounterPosition),
283 m_Width(that.m_Width)
284 {
285 m_Stream.fill(that.m_Stream.fill());
286 }
287
288 //! The function formats the file counter into the file name
289 path_string_type operator()(path_string_type const& pattern, unsigned int counter) const
290 {
291 path_string_type file_name = pattern;
292
293 m_Stream.str(path_string_type());
294 m_Stream.width(m_Width);
295 m_Stream << counter;
296 file_name.insert(m_FileCounterPosition, m_Stream.str());
297
298 return file_name;
299 }
300
301 BOOST_DELETED_FUNCTION(file_counter_formatter& operator= (file_counter_formatter const&))
302 };
303
304 //! The function returns the pattern as the file name
305 class empty_formatter
306 {
307 public:
308 typedef path_string_type result_type;
309
310 private:
311 path_string_type m_Pattern;
312
313 public:
314 //! Initializing constructor
315 explicit empty_formatter(path_string_type const& pattern) : m_Pattern(pattern)
316 {
317 }
318 //! Copy constructor
319 empty_formatter(empty_formatter const& that) : m_Pattern(that.m_Pattern)
320 {
321 }
322
323 //! The function returns the pattern as the file name
324 path_string_type const& operator() (unsigned int) const
325 {
326 return m_Pattern;
327 }
328
329 BOOST_DELETED_FUNCTION(empty_formatter& operator= (empty_formatter const&))
330 };
331
332 //! The function parses the format placeholder for file counter
333 bool parse_counter_placeholder(path_string_type::const_iterator& it, path_string_type::const_iterator end, unsigned int& width)
334 {
335 typedef qi::extract_uint< unsigned int, 10, 1, -1 > width_extract;
336 typedef file_char_traits< path_char_type > traits_t;
337 if (it == end)
338 return false;
339
340 path_char_type c = *it;
341 if (c == traits_t::zero || c == traits_t::space || c == traits_t::plus || c == traits_t::minus)
342 {
343 // Skip filler and alignment specification
344 ++it;
345 if (it == end)
346 return false;
347 c = *it;
348 }
349
350 if (traits_t::is_digit(c))
351 {
352 // Parse width
353 if (!width_extract::call(it, end, width))
354 return false;
355 if (it == end)
356 return false;
357 c = *it;
358 }
359
360 if (c == traits_t::dot)
361 {
362 // Skip precision
363 ++it;
364 while (it != end && traits_t::is_digit(*it))
365 ++it;
366 if (it == end)
367 return false;
368 c = *it;
369 }
370
371 if (c == traits_t::number_placeholder)
372 {
373 ++it;
374 return true;
375 }
376
377 return false;
378 }
379
380 //! The function matches the file name and the pattern
b32b8144 381 bool match_pattern(path_string_type const& file_name, path_string_type const& pattern, unsigned int& file_counter, bool& file_counter_parsed)
7c673cae
FG
382 {
383 typedef qi::extract_uint< unsigned int, 10, 1, -1 > file_counter_extract;
384 typedef file_char_traits< path_char_type > traits_t;
385
386 struct local
387 {
388 // Verifies that the string contains exactly n digits
389 static bool scan_digits(path_string_type::const_iterator& it, path_string_type::const_iterator end, std::ptrdiff_t n)
390 {
391 for (; n > 0; --n)
392 {
393 path_char_type c = *it++;
394 if (!traits_t::is_digit(c) || it == end)
395 return false;
396 }
397 return true;
398 }
399 };
400
401 path_string_type::const_iterator
402 f_it = file_name.begin(),
403 f_end = file_name.end(),
404 p_it = pattern.begin(),
405 p_end = pattern.end();
406 bool placeholder_expected = false;
407 while (f_it != f_end && p_it != p_end)
408 {
409 path_char_type p_c = *p_it, f_c = *f_it;
410 if (!placeholder_expected)
411 {
412 if (p_c == traits_t::percent)
413 {
414 placeholder_expected = true;
415 ++p_it;
416 }
417 else if (p_c == f_c)
418 {
419 ++p_it;
420 ++f_it;
421 }
422 else
423 return false;
424 }
425 else
426 {
427 switch (p_c)
428 {
429 case traits_t::percent: // An escaped '%'
430 if (p_c == f_c)
431 {
432 ++p_it;
433 ++f_it;
434 break;
435 }
436 else
437 return false;
438
439 case traits_t::seconds_placeholder: // Date/time components with 2-digits width
440 case traits_t::minutes_placeholder:
441 case traits_t::hours_placeholder:
442 case traits_t::day_placeholder:
443 case traits_t::month_placeholder:
444 case traits_t::year_placeholder:
445 if (!local::scan_digits(f_it, f_end, 2))
446 return false;
447 ++p_it;
448 break;
449
450 case traits_t::full_year_placeholder: // Date/time components with 4-digits width
451 if (!local::scan_digits(f_it, f_end, 4))
452 return false;
453 ++p_it;
454 break;
455
456 case traits_t::frac_sec_placeholder: // Fraction seconds width is configuration-dependent
457 typedef posix_time::time_res_traits posix_resolution_traits;
458 if (!local::scan_digits(f_it, f_end, posix_resolution_traits::num_fractional_digits()))
459 {
460 return false;
461 }
462 ++p_it;
463 break;
464
465 default: // This should be the file counter placeholder or some unsupported placeholder
466 {
467 path_string_type::const_iterator p = p_it;
468 unsigned int width = 0;
469 if (!parse_counter_placeholder(p, p_end, width))
470 {
471 BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported placeholder used in pattern for file scanning"));
472 }
473
474 // Find where the file number ends
475 path_string_type::const_iterator f = f_it;
476 if (!local::scan_digits(f, f_end, width))
477 return false;
478 while (f != f_end && traits_t::is_digit(*f))
479 ++f;
480
481 if (!file_counter_extract::call(f_it, f, file_counter))
482 return false;
483
b32b8144 484 file_counter_parsed = true;
7c673cae
FG
485 p_it = p;
486 }
487 break;
488 }
489
490 placeholder_expected = false;
491 }
492 }
493
494 if (p_it == p_end)
495 {
496 if (f_it != f_end)
497 {
498 // The actual file name may end with an additional counter
499 // that is added by the collector in case if file name clash
500 return local::scan_digits(f_it, f_end, std::distance(f_it, f_end));
501 }
502 else
503 return true;
504 }
505 else
506 return false;
507 }
508
509
510 class file_collector_repository;
511
512 //! Type of the hook used for sequencing file collectors
513 typedef intrusive::list_base_hook<
514 intrusive::link_mode< intrusive::safe_link >
515 > file_collector_hook;
516
517 //! Log file collector implementation
518 class file_collector :
519 public file::collector,
520 public file_collector_hook,
521 public enable_shared_from_this< file_collector >
522 {
523 private:
524 //! Information about a single stored file
525 struct file_info
526 {
527 uintmax_t m_Size;
528 std::time_t m_TimeStamp;
529 filesystem::path m_Path;
530 };
531 //! A list of the stored files
532 typedef std::list< file_info > file_list;
533 //! The string type compatible with the universal path type
534 typedef filesystem::path::string_type path_string_type;
535
536 private:
537 //! A reference to the repository this collector belongs to
538 shared_ptr< file_collector_repository > m_pRepository;
539
540#if !defined(BOOST_LOG_NO_THREADS)
541 //! Synchronization mutex
542 mutex m_Mutex;
543#endif // !defined(BOOST_LOG_NO_THREADS)
544
545 //! Total file size upper limit
546 uintmax_t m_MaxSize;
547 //! Free space lower limit
548 uintmax_t m_MinFreeSpace;
549 //! File count upper limit
550 uintmax_t m_MaxFiles;
551
552 //! The current path at the point when the collector is created
553 /*
554 * The special member is required to calculate absolute paths with no
555 * dependency on the current path for the application, which may change
556 */
557 const filesystem::path m_BasePath;
558 //! Target directory to store files to
559 filesystem::path m_StorageDir;
560
561 //! The list of stored files
562 file_list m_Files;
563 //! Total size of the stored files
564 uintmax_t m_TotalSize;
565
566 public:
567 //! Constructor
568 file_collector(
569 shared_ptr< file_collector_repository > const& repo,
570 filesystem::path const& target_dir,
571 uintmax_t max_size,
572 uintmax_t min_free_space,
573 uintmax_t max_files);
574
575 //! Destructor
576 ~file_collector();
577
578 //! The function stores the specified file in the storage
579 void store_file(filesystem::path const& file_name);
580
581 //! Scans the target directory for the files that have already been stored
582 uintmax_t scan_for_files(
583 file::scan_method method, filesystem::path const& pattern, unsigned int* counter);
584
585 //! The function updates storage restrictions
586 void update(uintmax_t max_size, uintmax_t min_free_space, uintmax_t max_files);
587
588 //! The function checks if the directory is governed by this collector
589 bool is_governed(filesystem::path const& dir) const
590 {
591 return filesystem::equivalent(m_StorageDir, dir);
592 }
593
594 private:
595 //! Makes relative path absolute with respect to the base path
596 filesystem::path make_absolute(filesystem::path const& p)
597 {
598 return filesystem::absolute(p, m_BasePath);
599 }
600 //! Acquires file name string from the path
601 static path_string_type filename_string(filesystem::path const& p)
602 {
603 return p.filename().string< path_string_type >();
604 }
605 };
606
607
608 //! The singleton of the list of file collectors
609 class file_collector_repository :
610 public log::aux::lazy_singleton< file_collector_repository, shared_ptr< file_collector_repository > >
611 {
612 private:
613 //! Base type
614 typedef log::aux::lazy_singleton< file_collector_repository, shared_ptr< file_collector_repository > > base_type;
615
616#if !defined(BOOST_LOG_BROKEN_FRIEND_TEMPLATE_SPECIALIZATIONS)
617 friend class log::aux::lazy_singleton< file_collector_repository, shared_ptr< file_collector_repository > >;
618#else
619 friend class base_type;
620#endif
621
622 //! The type of the list of collectors
623 typedef intrusive::list<
624 file_collector,
625 intrusive::base_hook< file_collector_hook >
626 > file_collectors;
627
628 private:
629#if !defined(BOOST_LOG_NO_THREADS)
630 //! Synchronization mutex
631 mutex m_Mutex;
632#endif // !defined(BOOST_LOG_NO_THREADS)
633 //! The list of file collectors
634 file_collectors m_Collectors;
635
636 public:
637 //! Finds or creates a file collector
638 shared_ptr< file::collector > get_collector(
639 filesystem::path const& target_dir, uintmax_t max_size, uintmax_t min_free_space, uintmax_t max_files);
640
641 //! Removes the file collector from the list
642 void remove_collector(file_collector* p);
643
644 private:
645 //! Initializes the singleton instance
646 static void init_instance()
647 {
648 base_type::get_instance() = boost::make_shared< file_collector_repository >();
649 }
650 };
651
652 //! Constructor
653 file_collector::file_collector(
654 shared_ptr< file_collector_repository > const& repo,
655 filesystem::path const& target_dir,
656 uintmax_t max_size,
657 uintmax_t min_free_space,
658 uintmax_t max_files
659 ) :
660 m_pRepository(repo),
661 m_MaxSize(max_size),
662 m_MinFreeSpace(min_free_space),
663 m_MaxFiles(max_files),
664 m_BasePath(filesystem::current_path()),
665 m_TotalSize(0)
666 {
667 m_StorageDir = make_absolute(target_dir);
668 filesystem::create_directories(m_StorageDir);
669 }
670
671 //! Destructor
672 file_collector::~file_collector()
673 {
674 m_pRepository->remove_collector(this);
675 }
676
677 //! The function stores the specified file in the storage
678 void file_collector::store_file(filesystem::path const& src_path)
679 {
680 // NOTE FOR THE FOLLOWING CODE:
681 // Avoid using Boost.Filesystem functions that would call path::codecvt(). store_file() can be called
682 // at process termination, and the global codecvt facet can already be destroyed at this point.
683 // https://svn.boost.org/trac/boost/ticket/8642
684
685 // Let's construct the new file name
686 file_info info;
687 info.m_TimeStamp = filesystem::last_write_time(src_path);
688 info.m_Size = filesystem::file_size(src_path);
689
690 filesystem::path file_name_path = src_path.filename();
691 path_string_type file_name = file_name_path.native();
692 info.m_Path = m_StorageDir / file_name_path;
693
694 // Check if the file is already in the target directory
695 filesystem::path src_dir = src_path.has_parent_path() ?
696 filesystem::system_complete(src_path.parent_path()) :
697 m_BasePath;
698 const bool is_in_target_dir = filesystem::equivalent(src_dir, m_StorageDir);
699 if (!is_in_target_dir)
700 {
701 if (filesystem::exists(info.m_Path))
702 {
703 // If the file already exists, try to mangle the file name
704 // to ensure there's no conflict. I'll need to make this customizable some day.
705 file_counter_formatter formatter(file_name.size(), 5);
706 unsigned int n = 0;
707 do
708 {
709 path_string_type alt_file_name = formatter(file_name, n++);
710 info.m_Path = m_StorageDir / filesystem::path(alt_file_name);
711 }
712 while (filesystem::exists(info.m_Path) && n < (std::numeric_limits< unsigned int >::max)());
713 }
714
715 // The directory should have been created in constructor, but just in case it got deleted since then...
716 filesystem::create_directories(m_StorageDir);
717 }
718
719 BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);)
720
721 // Check if an old file should be erased
722 uintmax_t free_space = m_MinFreeSpace ? filesystem::space(m_StorageDir).available : static_cast< uintmax_t >(0);
723 file_list::iterator it = m_Files.begin(), end = m_Files.end();
724 while (it != end &&
725 (m_TotalSize + info.m_Size > m_MaxSize || (m_MinFreeSpace && m_MinFreeSpace > free_space) || m_MaxFiles <= m_Files.size()))
726 {
727 file_info& old_info = *it;
728 if (filesystem::exists(old_info.m_Path) && filesystem::is_regular_file(old_info.m_Path))
729 {
730 try
731 {
732 filesystem::remove(old_info.m_Path);
733 // Free space has to be queried as it may not increase equally
734 // to the erased file size on compressed filesystems
735 if (m_MinFreeSpace)
736 free_space = filesystem::space(m_StorageDir).available;
737 m_TotalSize -= old_info.m_Size;
738 m_Files.erase(it++);
739 }
740 catch (system::system_error&)
741 {
742 // Can't erase the file. Maybe it's locked? Never mind...
743 ++it;
744 }
745 }
746 else
747 {
748 // If it's not a file or is absent, just remove it from the list
749 m_TotalSize -= old_info.m_Size;
750 m_Files.erase(it++);
751 }
752 }
753
754 if (!is_in_target_dir)
755 {
756 // Move/rename the file to the target storage
757 move_file(src_path, info.m_Path);
758 }
759
760 m_Files.push_back(info);
761 m_TotalSize += info.m_Size;
762 }
763
764 //! Scans the target directory for the files that have already been stored
765 uintmax_t file_collector::scan_for_files(
766 file::scan_method method, filesystem::path const& pattern, unsigned int* counter)
767 {
768 uintmax_t file_count = 0;
769 if (method != file::no_scan)
770 {
771 filesystem::path dir = m_StorageDir;
772 path_string_type mask;
773 if (method == file::scan_matching)
774 {
775 mask = filename_string(pattern);
776 if (pattern.has_parent_path())
777 dir = make_absolute(pattern.parent_path());
778 }
779 else
780 {
781 counter = NULL;
782 }
783
784 if (filesystem::exists(dir) && filesystem::is_directory(dir))
785 {
786 BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);)
787
788 if (counter)
789 *counter = 0;
790
791 file_list files;
792 filesystem::directory_iterator it(dir), end;
793 uintmax_t total_size = 0;
794 for (; it != end; ++it)
795 {
796 file_info info;
797 info.m_Path = *it;
798 if (filesystem::is_regular_file(info.m_Path))
799 {
800 // Check that there are no duplicates in the resulting list
801 struct local
802 {
803 static bool equivalent(filesystem::path const& left, file_info const& right)
804 {
805 return filesystem::equivalent(left, right.m_Path);
806 }
807 };
808 if (std::find_if(m_Files.begin(), m_Files.end(),
809 boost::bind(&local::equivalent, boost::cref(info.m_Path), _1)) == m_Files.end())
810 {
811 // Check that the file name matches the pattern
812 unsigned int file_number = 0;
b32b8144 813 bool file_number_parsed = false;
7c673cae 814 if (method != file::scan_matching ||
b32b8144 815 match_pattern(filename_string(info.m_Path), mask, file_number, file_number_parsed))
7c673cae
FG
816 {
817 info.m_Size = filesystem::file_size(info.m_Path);
818 total_size += info.m_Size;
819 info.m_TimeStamp = filesystem::last_write_time(info.m_Path);
820 files.push_back(info);
821 ++file_count;
822
b32b8144
FG
823 // Test that the file_number >= *counter accounting for the integer overflow
824 if (file_number_parsed && counter != NULL && (file_number - *counter) < ((~0u) ^ ((~0u) >> 1)))
825 *counter = file_number + 1u;
7c673cae
FG
826 }
827 }
828 }
829 }
830
831 // Sort files chronologically
832 m_Files.splice(m_Files.end(), files);
833 m_TotalSize += total_size;
834 m_Files.sort(boost::bind(&file_info::m_TimeStamp, _1) < boost::bind(&file_info::m_TimeStamp, _2));
835 }
836 }
837
838 return file_count;
839 }
840
841 //! The function updates storage restrictions
842 void file_collector::update(uintmax_t max_size, uintmax_t min_free_space, uintmax_t max_files)
843 {
844 BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);)
845
846 m_MaxSize = (std::min)(m_MaxSize, max_size);
847 m_MinFreeSpace = (std::max)(m_MinFreeSpace, min_free_space);
848 m_MaxFiles = (std::min)(m_MaxFiles, max_files);
849 }
850
851
852 //! Finds or creates a file collector
853 shared_ptr< file::collector > file_collector_repository::get_collector(
854 filesystem::path const& target_dir, uintmax_t max_size, uintmax_t min_free_space, uintmax_t max_files)
855 {
856 BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);)
857
858 file_collectors::iterator it = std::find_if(m_Collectors.begin(), m_Collectors.end(),
859 boost::bind(&file_collector::is_governed, _1, boost::cref(target_dir)));
860 shared_ptr< file_collector > p;
861 if (it != m_Collectors.end()) try
862 {
863 // This may throw if the collector is being currently destroyed
864 p = it->shared_from_this();
865 p->update(max_size, min_free_space, max_files);
866 }
867 catch (bad_weak_ptr&)
868 {
869 }
870
871 if (!p)
872 {
873 p = boost::make_shared< file_collector >(
874 file_collector_repository::get(), target_dir, max_size, min_free_space, max_files);
875 m_Collectors.push_back(*p);
876 }
877
878 return p;
879 }
880
881 //! Removes the file collector from the list
882 void file_collector_repository::remove_collector(file_collector* p)
883 {
884 BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);)
885 m_Collectors.erase(m_Collectors.iterator_to(*p));
886 }
887
888 //! Checks if the time point is valid
889 void check_time_point_validity(unsigned char hour, unsigned char minute, unsigned char second)
890 {
891 if (hour >= 24)
892 {
893 std::ostringstream strm;
894 strm << "Time point hours value is out of range: " << static_cast< unsigned int >(hour);
895 BOOST_THROW_EXCEPTION(std::out_of_range(strm.str()));
896 }
897 if (minute >= 60)
898 {
899 std::ostringstream strm;
900 strm << "Time point minutes value is out of range: " << static_cast< unsigned int >(minute);
901 BOOST_THROW_EXCEPTION(std::out_of_range(strm.str()));
902 }
903 if (second >= 60)
904 {
905 std::ostringstream strm;
906 strm << "Time point seconds value is out of range: " << static_cast< unsigned int >(second);
907 BOOST_THROW_EXCEPTION(std::out_of_range(strm.str()));
908 }
909 }
910
911} // namespace
912
913namespace file {
914
915namespace aux {
916
917 //! Creates and returns a file collector with the specified parameters
918 BOOST_LOG_API shared_ptr< collector > make_collector(
919 filesystem::path const& target_dir,
920 uintmax_t max_size,
921 uintmax_t min_free_space,
922 uintmax_t max_files)
923 {
924 return file_collector_repository::get()->get_collector(target_dir, max_size, min_free_space, max_files);
925 }
926
927} // namespace aux
928
929//! Creates a rotation time point of every day at the specified time
930BOOST_LOG_API rotation_at_time_point::rotation_at_time_point(
931 unsigned char hour,
932 unsigned char minute,
933 unsigned char second
934) :
935 m_DayKind(not_specified),
936 m_Day(0),
937 m_Hour(hour),
938 m_Minute(minute),
939 m_Second(second),
940 m_Previous(date_time::not_a_date_time)
941{
942 check_time_point_validity(hour, minute, second);
943}
944
945//! Creates a rotation time point of each specified weekday at the specified time
946BOOST_LOG_API rotation_at_time_point::rotation_at_time_point(
947 date_time::weekdays wday,
948 unsigned char hour,
949 unsigned char minute,
950 unsigned char second
951) :
952 m_DayKind(weekday),
953 m_Day(static_cast< unsigned char >(wday)),
954 m_Hour(hour),
955 m_Minute(minute),
956 m_Second(second),
957 m_Previous(date_time::not_a_date_time)
958{
959 check_time_point_validity(hour, minute, second);
960}
961
962//! Creates a rotation time point of each specified day of month at the specified time
963BOOST_LOG_API rotation_at_time_point::rotation_at_time_point(
964 gregorian::greg_day mday,
965 unsigned char hour,
966 unsigned char minute,
967 unsigned char second
968) :
969 m_DayKind(monthday),
970 m_Day(static_cast< unsigned char >(mday.as_number())),
971 m_Hour(hour),
972 m_Minute(minute),
973 m_Second(second),
974 m_Previous(date_time::not_a_date_time)
975{
976 check_time_point_validity(hour, minute, second);
977}
978
979//! Checks if it's time to rotate the file
980BOOST_LOG_API bool rotation_at_time_point::operator()() const
981{
982 bool result = false;
983 posix_time::time_duration rotation_time(
984 static_cast< posix_time::time_duration::hour_type >(m_Hour),
985 static_cast< posix_time::time_duration::min_type >(m_Minute),
986 static_cast< posix_time::time_duration::sec_type >(m_Second));
987 posix_time::ptime now = posix_time::second_clock::local_time();
988
989 if (m_Previous.is_special())
990 {
991 m_Previous = now;
992 return false;
993 }
994
995 const bool time_of_day_passed = rotation_time.total_seconds() <= m_Previous.time_of_day().total_seconds();
996 switch (m_DayKind)
997 {
998 case not_specified:
999 {
1000 // The rotation takes place every day at the specified time
1001 gregorian::date previous_date = m_Previous.date();
1002 if (time_of_day_passed)
1003 previous_date += gregorian::days(1);
1004 posix_time::ptime next(previous_date, rotation_time);
1005 result = (now >= next);
1006 }
1007 break;
1008
1009 case weekday:
1010 {
1011 // The rotation takes place on the specified week day at the specified time
1012 gregorian::date previous_date = m_Previous.date(), next_date = previous_date;
1013 int weekday = m_Day, previous_weekday = static_cast< int >(previous_date.day_of_week().as_number());
1014 next_date += gregorian::days(weekday - previous_weekday);
1015 if (weekday < previous_weekday || (weekday == previous_weekday && time_of_day_passed))
1016 {
1017 next_date += gregorian::weeks(1);
1018 }
1019
1020 posix_time::ptime next(next_date, rotation_time);
1021 result = (now >= next);
1022 }
1023 break;
1024
1025 case monthday:
1026 {
1027 // The rotation takes place on the specified day of month at the specified time
1028 gregorian::date previous_date = m_Previous.date();
1029 gregorian::date::day_type monthday = static_cast< gregorian::date::day_type >(m_Day),
1030 previous_monthday = previous_date.day();
1031 gregorian::date next_date(previous_date.year(), previous_date.month(), monthday);
1032 if (monthday < previous_monthday || (monthday == previous_monthday && time_of_day_passed))
1033 {
1034 next_date += gregorian::months(1);
1035 }
1036
1037 posix_time::ptime next(next_date, rotation_time);
1038 result = (now >= next);
1039 }
1040 break;
1041
1042 default:
1043 break;
1044 }
1045
1046 if (result)
1047 m_Previous = now;
1048
1049 return result;
1050}
1051
1052//! Checks if it's time to rotate the file
1053BOOST_LOG_API bool rotation_at_time_interval::operator()() const
1054{
1055 bool result = false;
1056 posix_time::ptime now = posix_time::second_clock::universal_time();
1057 if (m_Previous.is_special())
1058 {
1059 m_Previous = now;
1060 return false;
1061 }
1062
1063 result = (now - m_Previous) >= m_Interval;
1064
1065 if (result)
1066 m_Previous = now;
1067
1068 return result;
1069}
1070
1071} // namespace file
1072
1073////////////////////////////////////////////////////////////////////////////////
1074// File sink backend implementation
1075////////////////////////////////////////////////////////////////////////////////
1076//! Sink implementation data
1077struct text_file_backend::implementation
1078{
1079 //! File open mode
1080 std::ios_base::openmode m_FileOpenMode;
1081
1082 //! File name pattern
1083 filesystem::path m_FileNamePattern;
1084 //! Directory to store files in
1085 filesystem::path m_StorageDir;
1086 //! File name generator (according to m_FileNamePattern)
1087 boost::log::aux::light_function< path_string_type (unsigned int) > m_FileNameGenerator;
1088
1089 //! Stored files counter
1090 unsigned int m_FileCounter;
1091
1092 //! Current file name
1093 filesystem::path m_FileName;
1094 //! File stream
1095 filesystem::ofstream m_File;
1096 //! Characters written
1097 uintmax_t m_CharactersWritten;
1098
1099 //! File collector functional object
1100 shared_ptr< file::collector > m_pFileCollector;
1101 //! File open handler
1102 open_handler_type m_OpenHandler;
1103 //! File close handler
1104 close_handler_type m_CloseHandler;
1105
1106 //! The maximum temp file size, in characters written to the stream
1107 uintmax_t m_FileRotationSize;
1108 //! Time-based rotation predicate
1109 time_based_rotation_predicate m_TimeBasedRotation;
1110 //! The flag shows if every written record should be flushed
1111 bool m_AutoFlush;
b32b8144
FG
1112 //! The flag indicates whether the final rotation should be performed
1113 bool m_FinalRotationEnabled;
7c673cae 1114
b32b8144 1115 implementation(uintmax_t rotation_size, bool auto_flush, bool enable_final_rotation) :
7c673cae
FG
1116 m_FileOpenMode(std::ios_base::trunc | std::ios_base::out),
1117 m_FileCounter(0),
1118 m_CharactersWritten(0),
1119 m_FileRotationSize(rotation_size),
b32b8144
FG
1120 m_AutoFlush(auto_flush),
1121 m_FinalRotationEnabled(enable_final_rotation)
7c673cae
FG
1122 {
1123 }
1124};
1125
1126//! Constructor. No streams attached to the constructed backend, auto flush feature disabled.
1127BOOST_LOG_API text_file_backend::text_file_backend()
1128{
1129 construct(log::aux::empty_arg_list());
1130}
1131
1132//! Destructor
1133BOOST_LOG_API text_file_backend::~text_file_backend()
1134{
1135 try
1136 {
1137 // Attempt to put the temporary file into storage
b32b8144 1138 if (m_pImpl->m_FinalRotationEnabled && m_pImpl->m_File.is_open() && m_pImpl->m_CharactersWritten > 0)
7c673cae
FG
1139 rotate_file();
1140 }
1141 catch (...)
1142 {
1143 }
1144
1145 delete m_pImpl;
1146}
1147
1148//! Constructor implementation
1149BOOST_LOG_API void text_file_backend::construct(
1150 filesystem::path const& pattern,
1151 std::ios_base::openmode mode,
1152 uintmax_t rotation_size,
1153 time_based_rotation_predicate const& time_based_rotation,
b32b8144
FG
1154 bool auto_flush,
1155 bool enable_final_rotation)
7c673cae 1156{
b32b8144 1157 m_pImpl = new implementation(rotation_size, auto_flush, enable_final_rotation);
7c673cae
FG
1158 set_file_name_pattern_internal(pattern);
1159 set_time_based_rotation(time_based_rotation);
1160 set_open_mode(mode);
1161}
1162
1163//! The method sets maximum file size.
1164BOOST_LOG_API void text_file_backend::set_rotation_size(uintmax_t size)
1165{
1166 m_pImpl->m_FileRotationSize = size;
1167}
1168
1169//! The method sets the maximum time interval between file rotations.
1170BOOST_LOG_API void text_file_backend::set_time_based_rotation(time_based_rotation_predicate const& predicate)
1171{
1172 m_pImpl->m_TimeBasedRotation = predicate;
1173}
1174
b32b8144
FG
1175//! The method allows to enable or disable log file rotation on sink destruction.
1176BOOST_LOG_API void text_file_backend::enable_final_rotation(bool enable)
7c673cae 1177{
b32b8144
FG
1178 m_pImpl->m_FinalRotationEnabled = enable;
1179}
1180
1181//! Sets the flag to automatically flush write buffers of the file being written after each log record.
1182BOOST_LOG_API void text_file_backend::auto_flush(bool enable)
1183{
1184 m_pImpl->m_AutoFlush = enable;
7c673cae
FG
1185}
1186
1187//! The method writes the message to the sink
1188BOOST_LOG_API void text_file_backend::consume(record_view const& rec, string_type const& formatted_message)
1189{
1190 typedef file_char_traits< string_type::value_type > traits_t;
1191
1192 filesystem::path prev_file_name;
1193 bool use_prev_file_name = false;
1194 if (BOOST_UNLIKELY(!m_pImpl->m_File.good()))
1195 {
1196 // The file stream is not operational. One possible reason is that there is no more free space
1197 // on the file system. In this case it is possible that this log record will fail to be written as well,
1198 // leaving the newly creted file empty. Eventually this results in lots of empty log files.
1199 // We should take precautions to avoid this. https://svn.boost.org/trac/boost/ticket/11016
1200 prev_file_name = m_pImpl->m_FileName;
1201 close_file();
1202
1203 system::error_code ec;
1204 uintmax_t size = filesystem::file_size(prev_file_name, ec);
1205 if (!!ec || size == 0)
1206 {
1207 // To reuse the empty file avoid re-generating the new file name later
1208 use_prev_file_name = true;
1209 }
1210 else if (!!m_pImpl->m_pFileCollector)
1211 {
1212 // Complete file rotation
1213 m_pImpl->m_pFileCollector->store_file(prev_file_name);
1214 }
1215 }
1216 else if
1217 (
1218 m_pImpl->m_File.is_open() &&
1219 (
1220 m_pImpl->m_CharactersWritten + formatted_message.size() >= m_pImpl->m_FileRotationSize ||
1221 (!m_pImpl->m_TimeBasedRotation.empty() && m_pImpl->m_TimeBasedRotation())
1222 )
1223 )
1224 {
1225 rotate_file();
1226 }
1227
1228 if (!m_pImpl->m_File.is_open())
1229 {
1230 filesystem::path new_file_name;
1231 if (!use_prev_file_name)
1232 new_file_name = m_pImpl->m_StorageDir / m_pImpl->m_FileNameGenerator(m_pImpl->m_FileCounter++);
1233 else
1234 prev_file_name.swap(new_file_name);
1235
1236 filesystem::create_directories(new_file_name.parent_path());
1237
1238 m_pImpl->m_File.open(new_file_name, m_pImpl->m_FileOpenMode);
1239 if (BOOST_UNLIKELY(!m_pImpl->m_File.is_open()))
1240 {
1241 BOOST_THROW_EXCEPTION(filesystem_error(
1242 "Failed to open file for writing",
1243 new_file_name,
1244 system::error_code(system::errc::io_error, system::generic_category())));
1245 }
1246 m_pImpl->m_FileName.swap(new_file_name);
1247
1248 if (!m_pImpl->m_OpenHandler.empty())
1249 m_pImpl->m_OpenHandler(m_pImpl->m_File);
1250
1251 m_pImpl->m_CharactersWritten = static_cast< std::streamoff >(m_pImpl->m_File.tellp());
1252 }
1253
1254 m_pImpl->m_File.write(formatted_message.data(), static_cast< std::streamsize >(formatted_message.size()));
1255 m_pImpl->m_File.put(traits_t::newline);
1256
1257 m_pImpl->m_CharactersWritten += formatted_message.size() + 1;
1258
1259 if (m_pImpl->m_AutoFlush)
1260 m_pImpl->m_File.flush();
1261}
1262
1263//! The method flushes the currently open log file
1264BOOST_LOG_API void text_file_backend::flush()
1265{
1266 if (m_pImpl->m_File.is_open())
1267 m_pImpl->m_File.flush();
1268}
1269
1270//! The method sets file name mask
1271BOOST_LOG_API void text_file_backend::set_file_name_pattern_internal(filesystem::path const& pattern)
1272{
1273 // Note: avoid calling Boost.Filesystem functions that involve path::codecvt()
1274 // https://svn.boost.org/trac/boost/ticket/9119
1275
1276 typedef file_char_traits< path_char_type > traits_t;
1277 filesystem::path p = pattern;
1278 if (p.empty())
1279 p = filesystem::path(traits_t::default_file_name_pattern());
1280
1281 m_pImpl->m_FileNamePattern = p.filename();
1282 path_string_type name_pattern = m_pImpl->m_FileNamePattern.native();
1283 m_pImpl->m_StorageDir = filesystem::absolute(p.parent_path());
1284
1285 // Let's try to find the file counter placeholder
1286 unsigned int placeholder_count = 0;
1287 unsigned int width = 0;
1288 bool counter_found = false;
1289 path_string_type::size_type counter_pos = 0;
1290 path_string_type::const_iterator end = name_pattern.end();
1291 path_string_type::const_iterator it = name_pattern.begin();
1292
1293 do
1294 {
1295 it = std::find(it, end, traits_t::percent);
1296 if (it == end)
1297 break;
1298 path_string_type::const_iterator placeholder_begin = it++;
1299 if (it == end)
1300 break;
1301 if (*it == traits_t::percent)
1302 {
1303 // An escaped percent detected
1304 ++it;
1305 continue;
1306 }
1307
1308 ++placeholder_count;
1309
1310 if (!counter_found)
1311 {
1312 path_string_type::const_iterator it2 = it;
1313 if (parse_counter_placeholder(it2, end, width))
1314 {
1315 // We've found the file counter placeholder in the pattern
1316 counter_found = true;
1317 counter_pos = placeholder_begin - name_pattern.begin();
1318 name_pattern.erase(counter_pos, it2 - placeholder_begin);
1319 --placeholder_count;
1320 it = name_pattern.begin() + counter_pos;
1321 end = name_pattern.end();
1322 }
1323 }
1324 }
1325 while (it != end);
1326
1327 // Construct the formatter functor
1328 if (placeholder_count > 0)
1329 {
1330 if (counter_found)
1331 {
1332 // Both counter and date/time placeholder in the pattern
1333 m_pImpl->m_FileNameGenerator = boost::bind(date_and_time_formatter(),
1334 boost::bind(file_counter_formatter(counter_pos, width), name_pattern, _1), _1);
1335 }
1336 else
1337 {
1338 // Only date/time placeholders in the pattern
1339 m_pImpl->m_FileNameGenerator =
1340 boost::bind(date_and_time_formatter(), name_pattern, _1);
1341 }
1342 }
1343 else if (counter_found)
1344 {
1345 // Only counter placeholder in the pattern
1346 m_pImpl->m_FileNameGenerator =
1347 boost::bind(file_counter_formatter(counter_pos, width), name_pattern, _1);
1348 }
1349 else
1350 {
1351 // No placeholders detected
1352 m_pImpl->m_FileNameGenerator = empty_formatter(name_pattern);
1353 }
1354}
1355
1356//! Closes the currently open file
1357void text_file_backend::close_file()
1358{
1359 if (m_pImpl->m_File.is_open())
1360 {
1361 if (!m_pImpl->m_CloseHandler.empty())
1362 {
1363 // Rationale: We should call the close handler even if the stream is !good() because
1364 // writing the footer may not be the only thing the handler does. However, there is
1365 // a chance that the file had become writable since the last failure (e.g. there was
1366 // no space left to write the last record, but it got freed since then), so if the handler
1367 // attempts to write a footer it may succeed now. For this reason we clear the stream state
1368 // and let the handler have a try.
1369 m_pImpl->m_File.clear();
1370 m_pImpl->m_CloseHandler(m_pImpl->m_File);
1371 }
1372
1373 m_pImpl->m_File.close();
1374 }
1375
1376 m_pImpl->m_File.clear();
1377 m_pImpl->m_CharactersWritten = 0;
1378 m_pImpl->m_FileName.clear();
1379}
1380
1381//! The method rotates the file
1382BOOST_LOG_API void text_file_backend::rotate_file()
1383{
1384 filesystem::path prev_file_name = m_pImpl->m_FileName;
1385 close_file();
1386
1387 if (!!m_pImpl->m_pFileCollector)
1388 m_pImpl->m_pFileCollector->store_file(prev_file_name);
1389}
1390
1391//! The method sets the file open mode
1392BOOST_LOG_API void text_file_backend::set_open_mode(std::ios_base::openmode mode)
1393{
1394 mode |= std::ios_base::out;
1395 mode &= ~std::ios_base::in;
1396 if ((mode & (std::ios_base::trunc | std::ios_base::app)) == 0)
1397 mode |= std::ios_base::trunc;
1398 m_pImpl->m_FileOpenMode = mode;
1399}
1400
1401//! The method sets file collector
1402BOOST_LOG_API void text_file_backend::set_file_collector(shared_ptr< file::collector > const& collector)
1403{
1404 m_pImpl->m_pFileCollector = collector;
1405}
1406
1407//! The method sets file open handler
1408BOOST_LOG_API void text_file_backend::set_open_handler(open_handler_type const& handler)
1409{
1410 m_pImpl->m_OpenHandler = handler;
1411}
1412
1413//! The method sets file close handler
1414BOOST_LOG_API void text_file_backend::set_close_handler(close_handler_type const& handler)
1415{
1416 m_pImpl->m_CloseHandler = handler;
1417}
1418
1419//! The method returns name of the currently open log file. If no file is open, returns an empty path.
1420BOOST_LOG_API filesystem::path text_file_backend::get_current_file_name() const
1421{
1422 return m_pImpl->m_FileName;
1423}
1424
1425//! Performs scanning of the target directory for log files
1426BOOST_LOG_API uintmax_t text_file_backend::scan_for_files(file::scan_method method, bool update_counter)
1427{
1428 if (m_pImpl->m_pFileCollector)
1429 {
1430 unsigned int* counter = update_counter ? &m_pImpl->m_FileCounter : static_cast< unsigned int* >(NULL);
1431 return m_pImpl->m_pFileCollector->scan_for_files(method, m_pImpl->m_FileNamePattern, counter);
1432 }
1433 else
1434 {
1435 BOOST_LOG_THROW_DESCR(setup_error, "File collector is not set");
1436 }
1437}
1438
1439} // namespace sinks
1440
1441BOOST_LOG_CLOSE_NAMESPACE // namespace log
1442
1443} // namespace boost
1444
1445#include <boost/log/detail/footer.hpp>