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