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