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.hpp
9 * \author Andrey Semashev
10 * \date 09.06.2009
11 *
12 * The header contains implementation of a text file sink backend.
13 */
14
15 #ifndef BOOST_LOG_SINKS_TEXT_FILE_BACKEND_HPP_INCLUDED_
16 #define BOOST_LOG_SINKS_TEXT_FILE_BACKEND_HPP_INCLUDED_
17
18 #include <ios>
19 #include <string>
20 #include <ostream>
21 #include <boost/limits.hpp>
22 #include <boost/cstdint.hpp>
23 #include <boost/smart_ptr/shared_ptr.hpp>
24 #include <boost/date_time/date_defs.hpp>
25 #include <boost/date_time/special_defs.hpp>
26 #include <boost/date_time/gregorian/greg_day.hpp>
27 #include <boost/date_time/posix_time/posix_time_types.hpp>
28 #include <boost/filesystem/path.hpp>
29 #include <boost/log/keywords/max_size.hpp>
30 #include <boost/log/keywords/max_files.hpp>
31 #include <boost/log/keywords/min_free_space.hpp>
32 #include <boost/log/keywords/target.hpp>
33 #include <boost/log/keywords/target_file_name.hpp>
34 #include <boost/log/keywords/file_name.hpp>
35 #include <boost/log/keywords/open_mode.hpp>
36 #include <boost/log/keywords/auto_flush.hpp>
37 #include <boost/log/keywords/rotation_size.hpp>
38 #include <boost/log/keywords/time_based_rotation.hpp>
39 #include <boost/log/keywords/enable_final_rotation.hpp>
40 #include <boost/log/keywords/auto_newline_mode.hpp>
41 #include <boost/log/detail/config.hpp>
42 #include <boost/log/detail/light_function.hpp>
43 #include <boost/log/detail/parameter_tools.hpp>
44 #include <boost/log/sinks/auto_newline_mode.hpp>
45 #include <boost/log/sinks/basic_sink_backend.hpp>
46 #include <boost/log/sinks/frontend_requirements.hpp>
47 #include <boost/log/detail/header.hpp>
48
49 #ifdef BOOST_HAS_PRAGMA_ONCE
50 #pragma once
51 #endif
52
53 namespace boost {
54
55 BOOST_LOG_OPEN_NAMESPACE
56
57 namespace sinks {
58
59 namespace file {
60
61 //! The enumeration of the stored files scan methods
62 enum scan_method
63 {
64 no_scan, //!< Don't scan for stored files
65 scan_matching, //!< Scan for files with names matching the specified mask
66 scan_all //!< Scan for all files in the directory
67 };
68
69 /*!
70 * \brief Base class for file collectors
71 *
72 * All file collectors, supported by file sink backends, should inherit this class.
73 */
74 struct BOOST_LOG_NO_VTABLE collector
75 {
76 /*!
77 * Default constructor
78 */
collectorboost::sinks::file::collector79 BOOST_DEFAULTED_FUNCTION(collector(), {})
80
81 /*!
82 * Virtual destructor
83 */
84 virtual ~collector() {}
85
86 /*!
87 * The function stores the specified file in the storage. May lead to an older file
88 * deletion and a long file moving.
89 *
90 * \param src_path The name of the file to be stored
91 */
92 virtual void store_file(filesystem::path const& src_path) = 0;
93
94 /*!
95 * Scans the target directory for the files that have already been stored. The found
96 * files are added to the collector in order to be tracked and erased, if needed.
97 *
98 * The function may scan the directory in two ways: it will either consider every
99 * file in the directory a log file, or will only consider files with names that
100 * match the specified pattern. The pattern may contain the following placeholders:
101 *
102 * \li %y, %Y, %m, %d - date components, in Boost.DateTime meaning.
103 * \li %H, %M, %S, %f - time components, in Boost.DateTime meaning.
104 * \li %N - numeric file counter. May also contain width specification
105 * in printf-compatible form (e.g. %5N). The resulting number will always be zero-filled.
106 * \li %% - a percent sign
107 *
108 * All other placeholders are not supported.
109 *
110 * \param method The method of scanning. If \c no_scan is specified, the call has no effect.
111 * \param pattern The file name pattern if \a method is \c scan_matching. Otherwise the parameter
112 * is not used.
113 * \param counter If not \c NULL and \a method is \c scan_matching, the method suggests initial value
114 * of a file counter that may be used in the file name pattern. The parameter
115 * is not used otherwise.
116 * \return The number of found files.
117 *
118 * \note In case if \a method is \c scan_matching the effect of this function is highly dependent
119 * on the \a pattern definition. It is recommended to choose patterns with easily
120 * distinguished placeholders (i.e. having delimiters between them). Otherwise
121 * either some files can be mistakenly found or not found, which in turn may lead
122 * to an incorrect file deletion.
123 */
124 virtual uintmax_t scan_for_files(
125 scan_method method, filesystem::path const& pattern = filesystem::path(), unsigned int* counter = 0) = 0;
126
127 BOOST_DELETED_FUNCTION(collector(collector const&))
128 BOOST_DELETED_FUNCTION(collector& operator= (collector const&))
129 };
130
131 namespace aux {
132
133 //! Creates and returns a file collector with the specified parameters
134 BOOST_LOG_API shared_ptr< collector > make_collector(
135 filesystem::path const& target_dir,
136 uintmax_t max_size,
137 uintmax_t min_free_space,
138 uintmax_t max_files = (std::numeric_limits< uintmax_t >::max)()
139 );
140 template< typename ArgsT >
make_collector(ArgsT const & args)141 inline shared_ptr< collector > make_collector(ArgsT const& args)
142 {
143 return aux::make_collector(
144 filesystem::path(args[keywords::target]),
145 args[keywords::max_size | (std::numeric_limits< uintmax_t >::max)()],
146 args[keywords::min_free_space | static_cast< uintmax_t >(0)],
147 args[keywords::max_files | (std::numeric_limits< uintmax_t >::max)()]);
148 }
149
150 } // namespace aux
151
152 #ifndef BOOST_LOG_DOXYGEN_PASS
153
154 template< typename T1 >
make_collector(T1 const & a1)155 inline shared_ptr< collector > make_collector(T1 const& a1)
156 {
157 return aux::make_collector(a1);
158 }
159 template< typename T1, typename T2 >
make_collector(T1 const & a1,T2 const & a2)160 inline shared_ptr< collector > make_collector(T1 const& a1, T2 const& a2)
161 {
162 return aux::make_collector((a1, a2));
163 }
164 template< typename T1, typename T2, typename T3 >
make_collector(T1 const & a1,T2 const & a2,T3 const & a3)165 inline shared_ptr< collector > make_collector(T1 const& a1, T2 const& a2, T3 const& a3)
166 {
167 return aux::make_collector((a1, a2, a3));
168 }
169 template< typename T1, typename T2, typename T3, typename T4 >
make_collector(T1 const & a1,T2 const & a2,T3 const & a3,T4 const & a4)170 inline shared_ptr< collector > make_collector(T1 const& a1, T2 const& a2, T3 const& a3, T4 const& a4)
171 {
172 return aux::make_collector((a1, a2, a3, a4));
173 }
174
175 #else
176
177 /*!
178 * The function creates a file collector for the specified target directory.
179 * Each target directory is managed by a single file collector, so if
180 * this function is called several times for the same directory,
181 * it will return a reference to the same file collector. It is safe
182 * to use the same collector in different sinks, even in a multithreaded
183 * application.
184 *
185 * One can specify certain restrictions for the stored files, such as
186 * maximum total size or minimum free space left in the target directory.
187 * If any of the specified restrictions is not met, the oldest stored file
188 * is deleted. If the same collector is requested more than once with
189 * different restrictions, the collector will act according to the most strict
190 * combination of all specified restrictions.
191 *
192 * The following named parameters are supported:
193 *
194 * \li \c target - Specifies the target directory for the files being stored in. This parameter
195 * is mandatory.
196 * \li \c max_size - Specifies the maximum total size, in bytes, of stored files that the collector
197 * will try not to exceed. If the size exceeds this threshold the oldest file(s) is
198 * deleted to free space. Note that the threshold may be exceeded if the size of
199 * individual files exceed the \c max_size value. The threshold is not maintained,
200 * if not specified.
201 * \li \c min_free_space - Specifies the minimum free space, in bytes, in the target directory that
202 * the collector tries to maintain. If the threshold is exceeded, the oldest
203 * file(s) is deleted to free space. The threshold is not maintained, if not
204 * specified.
205 * \li \c max_files - Specifies the maximum number of log files stored. If the number of files exceeds
206 * this threshold, the oldest file(s) is deleted to free space. The threshhold is
207 * not maintained if not specified.
208 *
209 * \return The file collector.
210 */
211 template< typename... ArgsT >
212 shared_ptr< collector > make_collector(ArgsT... const& args);
213
214 #endif // BOOST_LOG_DOXYGEN_PASS
215
216 /*!
217 * The class represents the time point of log file rotation. One can specify one of three
218 * types of time point based rotation:
219 *
220 * \li rotation takes place every day, at the specified time
221 * \li rotation takes place on the specified day of every week, at the specified time
222 * \li rotation takes place on the specified day of every month, at the specified time
223 *
224 * The time points are considered to be local time.
225 */
226 class rotation_at_time_point
227 {
228 public:
229 typedef bool result_type;
230
231 private:
232 enum day_kind
233 {
234 not_specified,
235 weekday,
236 monthday
237 };
238
239 unsigned char m_Day : 6;
240 unsigned char m_DayKind : 2; // contains day_kind values
241 unsigned char m_Hour, m_Minute, m_Second;
242
243 mutable posix_time::ptime m_Previous;
244
245 public:
246 /*!
247 * Creates a rotation time point of every day at the specified time
248 *
249 * \param hour The rotation hour, should be within 0 and 23
250 * \param minute The rotation minute, should be within 0 and 59
251 * \param second The rotation second, should be within 0 and 59
252 */
253 BOOST_LOG_API explicit rotation_at_time_point(unsigned char hour, unsigned char minute, unsigned char second);
254
255 /*!
256 * Creates a rotation time point of each specified weekday at the specified time
257 *
258 * \param wday The weekday of the rotation
259 * \param hour The rotation hour, should be within 0 and 23
260 * \param minute The rotation minute, should be within 0 and 59
261 * \param second The rotation second, should be within 0 and 59
262 */
263 BOOST_LOG_API explicit rotation_at_time_point(
264 date_time::weekdays wday,
265 unsigned char hour = 0,
266 unsigned char minute = 0,
267 unsigned char second = 0);
268
269 /*!
270 * Creates a rotation time point of each specified day of month at the specified time
271 *
272 * \param mday The monthday of the rotation, should be within 1 and 31
273 * \param hour The rotation hour, should be within 0 and 23
274 * \param minute The rotation minute, should be within 0 and 59
275 * \param second The rotation second, should be within 0 and 59
276 */
277 BOOST_LOG_API explicit rotation_at_time_point(
278 gregorian::greg_day mday,
279 unsigned char hour = 0,
280 unsigned char minute = 0,
281 unsigned char second = 0);
282
283 /*!
284 * Checks if it's time to rotate the file
285 */
286 BOOST_LOG_API bool operator() () const;
287 };
288
289 /*!
290 * The class represents the time interval of log file rotation. The log file will be rotated
291 * after the specified time interval has passed.
292 */
293 class rotation_at_time_interval
294 {
295 public:
296 typedef bool result_type;
297
298 private:
299 posix_time::time_duration m_Interval;
300 mutable posix_time::ptime m_Previous;
301
302 public:
303 /*!
304 * Creates a rotation time interval of the specified duration
305 *
306 * \param interval The interval of the rotation, should be no less than 1 second
307 */
rotation_at_time_interval(posix_time::time_duration const & interval)308 explicit rotation_at_time_interval(posix_time::time_duration const& interval) :
309 m_Interval(interval)
310 {
311 BOOST_ASSERT(!interval.is_special());
312 BOOST_ASSERT(interval.total_seconds() > 0);
313 }
314
315 /*!
316 * Checks if it's time to rotate the file
317 */
318 BOOST_LOG_API bool operator() () const;
319 };
320
321 } // namespace file
322
323
324 /*!
325 * \brief An implementation of a text file logging sink backend
326 *
327 * The sink backend puts formatted log records to a text file.
328 * The sink supports file rotation and advanced file control, such as
329 * size and file count restriction.
330 */
331 class text_file_backend :
332 public basic_formatted_sink_backend<
333 char,
334 combine_requirements< synchronized_feeding, flushing >::type
335 >
336 {
337 //! Base type
338 typedef basic_formatted_sink_backend<
339 char,
340 combine_requirements< synchronized_feeding, flushing >::type
341 > base_type;
342
343 public:
344 //! Character type
345 typedef base_type::char_type char_type;
346 //! String type to be used as a message text holder
347 typedef base_type::string_type string_type;
348 //! Stream type
349 typedef std::basic_ostream< char_type > stream_type;
350
351 //! File open handler
352 typedef boost::log::aux::light_function< void (stream_type&) > open_handler_type;
353 //! File close handler
354 typedef boost::log::aux::light_function< void (stream_type&) > close_handler_type;
355
356 //! Predicate that defines the time-based condition for file rotation
357 typedef boost::log::aux::light_function< bool () > time_based_rotation_predicate;
358
359 private:
360 //! \cond
361
362 struct implementation;
363 implementation* m_pImpl;
364
365 //! \endcond
366
367 public:
368 /*!
369 * Default constructor. The constructed sink backend uses default values of all the parameters.
370 */
371 BOOST_LOG_API text_file_backend();
372
373 /*!
374 * Constructor. Creates a sink backend with the specified named parameters.
375 * The following named parameters are supported:
376 *
377 * \li \c file_name - Specifies the active file name pattern where logs are actually written to. The pattern may
378 * contain directory and file name portions, but only the file name may contain
379 * placeholders. The backend supports Boost.DateTime placeholders for injecting
380 * current time and date into the file name. Also, an additional %N placeholder is
381 * supported, it will be replaced with an integral increasing file counter. The placeholder
382 * may also contain width specification in the printf-compatible form (e.g. %5N). The
383 * printed file counter will always be zero-filled. If \c file_name is not specified,
384 * pattern "%5N.log" will be used.
385 * \li \c target_file_name - Specifies the target file name pattern to use to rename the log file on rotation,
386 * before passing it to the file collector. The pattern may contain the same
387 * placeholders as the \c file_name parameter. By default, no renaming is done,
388 * i.e. the written log file keeps its name according to \c file_name.
389 * \li \c open_mode - File open mode. The mode should be presented in form of mask compatible to
390 * <tt>std::ios_base::openmode</tt>. If not specified, <tt>trunc | out</tt> will be used.
391 * \li \c rotation_size - Specifies the approximate size, in characters written, of the temporary file
392 * upon which the file is passed to the file collector. Note the size does
393 * not count any possible character conversions that may take place during
394 * writing to the file. If not specified, the file won't be rotated upon reaching
395 * any size.
396 * \li \c time_based_rotation - Specifies the predicate for time-based file rotation.
397 * No time-based file rotations will be performed, if not specified.
398 * \li \c enable_final_rotation - Specifies a flag, whether or not perform log file rotation on
399 * sink backend destruction. By default, is \c true.
400 * \li \c auto_flush - Specifies a flag, whether or not to automatically flush the file after each
401 * written log record. By default, is \c false.
402 * \li \c auto_newline_mode - Specifies automatic trailing newline insertion mode. Must be a value of
403 * the \c auto_newline_mode enum. By default, is <tt>auto_newline_mode::insert_if_missing</tt>.
404 *
405 * \note Read the caution note regarding file name pattern in the <tt>sinks::file::collector::scan_for_files</tt>
406 * documentation.
407 */
408 #ifndef BOOST_LOG_DOXYGEN_PASS
409 BOOST_LOG_PARAMETRIZED_CONSTRUCTORS_CALL(text_file_backend, construct)
410 #else
411 template< typename... ArgsT >
412 explicit text_file_backend(ArgsT... const& args);
413 #endif
414
415 /*!
416 * Destructor
417 */
418 BOOST_LOG_API ~text_file_backend();
419
420 /*!
421 * The method sets the active file name wildcard for the files being written. The wildcard supports
422 * date and time injection into the file name.
423 *
424 * \param pattern The name pattern for the file being written.
425 */
426 template< typename PathT >
set_file_name_pattern(PathT const & pattern)427 void set_file_name_pattern(PathT const& pattern)
428 {
429 set_file_name_pattern_internal(filesystem::path(pattern));
430 }
431
432 /*!
433 * The method sets the target file name wildcard for the files being rotated. The wildcard supports
434 * date and time injection into the file name.
435 *
436 * This pattern will be used when the log file is being rotated, to rename the just written
437 * log file (which has the name according to the pattern in the \c file_name constructor parameter or
438 * set by a call to \c set_file_name_pattern), just before passing the file to the file collector.
439 *
440 * \param pattern The name pattern for the file being rotated.
441 */
442 template< typename PathT >
set_target_file_name_pattern(PathT const & pattern)443 void set_target_file_name_pattern(PathT const& pattern)
444 {
445 set_target_file_name_pattern_internal(filesystem::path(pattern));
446 }
447
448 /*!
449 * The method sets the file open mode
450 *
451 * \param mode File open mode
452 */
453 BOOST_LOG_API void set_open_mode(std::ios_base::openmode mode);
454
455 /*!
456 * The method sets the log file collector function. The function is called
457 * on file rotation and is being passed the written file name.
458 *
459 * \param collector The file collector function object
460 */
461 BOOST_LOG_API void set_file_collector(shared_ptr< file::collector > const& collector);
462
463 /*!
464 * The method sets file opening handler. The handler will be called every time
465 * the backend opens a new temporary file. The handler may write a header to the
466 * opened file in order to maintain file validity.
467 *
468 * \param handler The file open handler function object
469 */
470 BOOST_LOG_API void set_open_handler(open_handler_type const& handler);
471
472 /*!
473 * The method sets file closing handler. The handler will be called every time
474 * the backend closes a temporary file. The handler may write a footer to the
475 * opened file in order to maintain file validity.
476 *
477 * \param handler The file close handler function object
478 */
479 BOOST_LOG_API void set_close_handler(close_handler_type const& handler);
480
481 /*!
482 * The method sets maximum file size. When the size is reached, file rotation is performed.
483 *
484 * \note The size does not count any possible character translations that may happen in
485 * the underlying API. This may result in greater actual sizes of the written files.
486 *
487 * \param size The maximum file size, in characters.
488 */
489 BOOST_LOG_API void set_rotation_size(uintmax_t size);
490
491 /*!
492 * The method sets the predicate that defines the time-based condition for file rotation.
493 *
494 * \note The rotation always occurs on writing a log record, so the rotation is
495 * not strictly bound to the specified condition.
496 *
497 * \param predicate The predicate that defines the time-based condition for file rotation.
498 * If empty, no time-based rotation will take place.
499 */
500 BOOST_LOG_API void set_time_based_rotation(time_based_rotation_predicate const& predicate);
501
502 /*!
503 * The method allows to enable or disable log file rotation on sink destruction.
504 *
505 * By default the sink backend will rotate the log file, if it's been written to, on
506 * destruction.
507 *
508 * \param enable The flag indicates whether the final rotation should be performed.
509 */
510 BOOST_LOG_API void enable_final_rotation(bool enable);
511
512 /*!
513 * Sets the flag to automatically flush write buffers of the file being written after each log record.
514 *
515 * \param enable The flag indicates whether the automatic buffer flush should be performed.
516 */
517 BOOST_LOG_API void auto_flush(bool enable = true);
518
519 /*!
520 * Selects whether a trailing newline should be automatically inserted after every log record. See
521 * \c auto_newline_mode description for the possible modes of operation.
522 *
523 * \param mode The trailing newline insertion mode.
524 */
525 BOOST_LOG_API void set_auto_newline_mode(auto_newline_mode mode);
526
527 /*!
528 * \return The name of the currently open log file. If no file is open, returns an empty path.
529 */
530 BOOST_LOG_API filesystem::path get_current_file_name() const;
531
532 /*!
533 * Performs scanning of the target directory for log files that may have been left from
534 * previous runs of the application. The found files are considered by the file collector
535 * as if they were rotated.
536 *
537 * The file scan can be performed in two ways: either all files in the target directory will
538 * be considered as log files, or only those files that satisfy the target file name pattern.
539 * See documentation on <tt>sinks::file::collector::scan_for_files</tt> for more information.
540 *
541 * \pre File collector and the proper file name pattern have already been set.
542 *
543 * \param method File scanning method
544 * \param update_counter If \c true and \a method is \c scan_matching, the method attempts
545 * to update the internal file counter according to the found files. The counter
546 * is unaffected otherwise.
547 * \return The number of files found.
548 *
549 * \note The method essentially delegates to the same-named function of the file collector.
550 */
551 BOOST_LOG_API uintmax_t scan_for_files(
552 file::scan_method method = file::scan_matching, bool update_counter = true);
553
554 /*!
555 * The method writes the message to the sink
556 */
557 BOOST_LOG_API void consume(record_view const& rec, string_type const& formatted_message);
558
559 /*!
560 * The method flushes the currently open log file
561 */
562 BOOST_LOG_API void flush();
563
564 /*!
565 * The method rotates the file
566 */
567 BOOST_LOG_API void rotate_file();
568
569 private:
570 #ifndef BOOST_LOG_DOXYGEN_PASS
571 //! Constructor implementation
572 template< typename ArgsT >
construct(ArgsT const & args)573 void construct(ArgsT const& args)
574 {
575 construct(
576 filesystem::path(args[keywords::file_name | filesystem::path()]),
577 filesystem::path(args[keywords::target_file_name | filesystem::path()]),
578 args[keywords::open_mode | (std::ios_base::trunc | std::ios_base::out)],
579 args[keywords::rotation_size | (std::numeric_limits< uintmax_t >::max)()],
580 args[keywords::time_based_rotation | time_based_rotation_predicate()],
581 args[keywords::auto_newline_mode | insert_if_missing],
582 args[keywords::auto_flush | false],
583 args[keywords::enable_final_rotation | true]);
584 }
585 //! Constructor implementation
586 BOOST_LOG_API void construct(
587 filesystem::path const& pattern,
588 filesystem::path const& target_file_name,
589 std::ios_base::openmode mode,
590 uintmax_t rotation_size,
591 time_based_rotation_predicate const& time_based_rotation,
592 auto_newline_mode auto_newline,
593 bool auto_flush,
594 bool enable_final_rotation);
595
596 //! The method sets file name pattern
597 BOOST_LOG_API void set_file_name_pattern_internal(filesystem::path const& pattern);
598 //! The method sets target file name pattern
599 BOOST_LOG_API void set_target_file_name_pattern_internal(filesystem::path const& pattern);
600
601 //! Closes the currently open file
602 void close_file();
603 #endif // BOOST_LOG_DOXYGEN_PASS
604 };
605
606 } // namespace sinks
607
608 BOOST_LOG_CLOSE_NAMESPACE // namespace log
609
610 } // namespace boost
611
612 #include <boost/log/detail/footer.hpp>
613
614 #endif // BOOST_LOG_SINKS_TEXT_FILE_BACKEND_HPP_INCLUDED_
615