Index: boost/log/sinks/text_file_backend.hpp =================================================================== --- boost/log/sinks/text_file_backend.hpp (revision 84158) +++ boost/log/sinks/text_file_backend.hpp (working copy) @@ -140,6 +140,24 @@ args[keywords::min_free_space | static_cast< uintmax_t >(0)]); } + //! Creates and returns a rollover file collector. + BOOST_LOG_API shared_ptr< collector > make_rollover_collector( + filesystem::path const& target_dir, + filesystem::path const& file_name, + uintmax_t max_size, + uintmax_t min_free_space + ); + + template< typename ArgsT > + inline shared_ptr< collector > make_rollover_collector(ArgsT const& args) + { + return aux::make_rollover_collector( + filesystem::path(args[keywords::target]), + filesystem::path(args[keywords::file_name]), + args[keywords::max_size | (std::numeric_limits< uintmax_t >::max)()], + args[keywords::min_free_space | static_cast< uintmax_t >(0)]); + } + } // namespace aux #ifndef BOOST_LOG_DOXYGEN_PASS @@ -160,6 +178,27 @@ return aux::make_collector((a1, a2, a3)); } +template< typename T1 > +inline shared_ptr< collector > make_rollover_collector(T1 const& a1) +{ + return aux::make_rollover_collector(a1); +} +template< typename T1, typename T2 > +inline shared_ptr< collector > make_rollover_collector(T1 const& a1, T2 const& a2) +{ + return aux::make_rollover_collector((a1, a2)); +} +template< typename T1, typename T2, typename T3 > +inline shared_ptr< collector > make_rollover_collector(T1 const& a1, T2 const& a2, T3 const& a3) +{ + return aux::make_rollover_collector((a1, a2, a3)); +} +template< typename T1, typename T2, typename T3, typename T4 > +inline shared_ptr< collector > make_rollover_collector(T1 const& a1, T2 const& a2, T3 const& a3, T4 const& a4) +{ + return aux::make_rollover_collector((a1, a2, a3, a4)); +} + #else /*! @@ -196,6 +235,32 @@ template< typename... ArgsT > shared_ptr< collector > make_collector(ArgsT... const& args); +/*! + * Creates and returns a rollover file collector. + * Collector will perform files rotation in "sliding window" manner: + * file.log (latest), file1.log (previous), ..., fileN.log (earliest). + * + * \param target_dir - Directory where log files to be collected and rotated. + * This parameter is mandatory. + * \param file_name - File name or file mask. + * Has the same meaning as in text_file_backend context. + * Only counter placeholder is valid for this parameter. + * When \a file_name does not contain placeholder, placeholder ".%N" will be appended. + * \param max_size - Specifies the maximum total size, in bytes, of stored files that the collector + * will try not to exceed. If the size exceeds this threshold the oldest file(s) is + * deleted to free space. Note that the threshold may be exceeded if the size of + * individual files exceed the \c max_size value. The threshold is not maintained, + * if not specified. + * \param min_free_space - Specifies the minimum free space, in bytes, in the target directory that + * the collector tries to maintain. If the threshold is exceeded, the oldest + * file(s) is deleted to free space. The threshold is not maintained, if not + * specified. + * + * \return The rollover file collector + */ +template< typename... ArgsT > +shared_ptr< collector > make_rollover_collector(ArgsT... const& args); + #endif // BOOST_LOG_DOXYGEN_PASS /*! Index: libs/log/src/text_file_backend.cpp =================================================================== --- libs/log/src/text_file_backend.cpp (revision 84158) +++ libs/log/src/text_file_backend.cpp (working copy) @@ -468,37 +468,152 @@ return false; } + class file_name_generator + { + public: + //generator types + enum formatter + { + empty = 0, + counter = 1, + date_time = 1<<1, + date_time_counter = date_time | counter + }; - class file_collector_repository; + typedef boost::log::aux::light_function< path_string_type (unsigned int) > type; - //! Type of the hook used for sequencing file collectors - typedef intrusive::list_base_hook< - intrusive::link_mode< intrusive::safe_link > - > file_collector_hook; + struct cookie + { + path_string_type pattern; + path_string_type::size_type counter_pos; + unsigned int placeholder_count; + unsigned int width; + bool counter_found; + enum formatter formatter; - //! Log file collector implementation - class file_collector : - public file::collector, - public file_collector_hook, - public enable_shared_from_this< file_collector > + explicit cookie(path_string_type const& name_pattern) + : pattern(name_pattern), + counter_pos(0), + placeholder_count(0), + width(0), + counter_found(false), + formatter(empty) + {} + + explicit cookie(filesystem::path const& file_name) + : pattern(file_name.filename().string< path_string_type >()), + counter_pos(0), + placeholder_count(0), + width(0), + counter_found(false), + formatter(empty) + {} + + void parse() + { + typedef file_char_traits< path_char_type > traits_t; + + path_string_type::const_iterator end = pattern.end(); + path_string_type::const_iterator it = pattern.begin(); + + do + { + it = std::find(it, end, traits_t::percent); + if (it == end) + break; + path_string_type::const_iterator placeholder_begin = it++; + if (it == end) + break; + if (*it == traits_t::percent) + { + // An escaped percent detected + ++it; + continue; + } + + ++placeholder_count; + + if (!counter_found && parse_counter_placeholder(it, end, width)) + { + // We've found the file counter placeholder in the pattern + counter_found = true; + counter_pos = placeholder_begin - pattern.begin(); + pattern.erase(counter_pos, it - placeholder_begin); + --placeholder_count; + it = pattern.begin() + counter_pos; + end = pattern.end(); + } + } + while (it != end); + + unsigned int choice = (static_cast< unsigned int >(placeholder_count > 0) << 1) | + static_cast< unsigned int >(counter_found); + + formatter = choice == 0 || choice > date_time_counter ? + empty : static_cast(choice) + ; + } + }; + + static type create(path_string_type const& name_pattern) + { + typedef file_char_traits< path_char_type > traits_t; + + cookie cook(name_pattern); + cook.parse(); + + return create(cook); + } + + static type create(const cookie& c) + { + switch (c.formatter) + { + case counter: // Only counter placeholder in the pattern + return + boost::bind(file_counter_formatter(c.counter_pos, c.width), c.pattern, _1); + case date_time: // Only date/time placeholders in the pattern + return + boost::bind(date_and_time_formatter(), c.pattern, _1); + case date_time_counter: // Counter and date/time placeholder in the pattern + return boost::bind(date_and_time_formatter(), + boost::bind(file_counter_formatter(c.counter_pos, c.width), c.pattern, _1), _1); + default: // No placeholders detected + return empty_formatter(c.pattern); + } + } + }; + + + //! Base implementation class for log file collectors + class file_collector_base : public file::collector { - private: + protected: //! Information about a single stored file struct file_info { uintmax_t m_Size; std::time_t m_TimeStamp; filesystem::path m_Path; + + file_info() + : m_Size(0), + m_TimeStamp(0) + {} + + explicit file_info(filesystem::path const& path) + : m_Size(filesystem::file_size(path)), + m_TimeStamp(filesystem::last_write_time(path)) + {} }; //! A list of the stored files typedef std::list< file_info > file_list; + typedef file_list::size_type size_type; + typedef file_list::iterator file_iterator; + //! The string type compatible with the universal path type typedef filesystem::path::string_type path_string_type; - private: - //! A reference to the repository this collector belongs to - shared_ptr< file_collector_repository > m_pRepository; - #if !defined(BOOST_LOG_NO_THREADS) //! Synchronization mutex mutex m_Mutex; @@ -521,21 +636,61 @@ file_list m_Files; //! Total size of the stored files uintmax_t m_TotalSize; + protected: + filesystem::path storage_dir() const + { + return m_StorageDir; + } + file_iterator first_file() + { + return m_Files.begin(); + } + + file_iterator last_file() + { + return m_Files.end(); + } + + size_type files_size() const + { + return m_Files.size(); + } + + //! Generate log file name to be stored + file_info stored_file_name(filesystem::path const& src_path); + + /*! + * Performs file storage cleanup according to collector settings + * + * \param size_increment - files' size that expected to be added to storage + */ + void cleanup_storage(uintmax_t size_increment); + + //! Append log file to storage + void append_file(filesystem::path const& src_path, const file_info& info); + + //! Shortcut function that stores file + void store_file_impl(filesystem::path const& src_path) + { + file_info info(stored_file_name(src_path)); + cleanup_storage(info.m_Size); + append_file(src_path, info); + } public: - //! Constructor - file_collector( - shared_ptr< file_collector_repository > const& repo, + file_collector_base( filesystem::path const& target_dir, uintmax_t max_size, - uintmax_t min_free_space); + uintmax_t min_free_space + ) : m_MaxSize(max_size), + m_MinFreeSpace(min_free_space), + m_BasePath(filesystem::current_path()), + m_TotalSize(0) + { + m_StorageDir = make_absolute(target_dir); + filesystem::create_directories(m_StorageDir); + } - //! Destructor - ~file_collector(); - - //! The function stores the specified file in the storage - void store_file(filesystem::path const& file_name); - //! Scans the target directory for the files that have already been stored uintmax_t scan_for_files( file::scan_method method, filesystem::path const& pattern, unsigned int* counter); @@ -562,82 +717,11 @@ } }; - - //! The singleton of the list of file collectors - class file_collector_repository : - public log::aux::lazy_singleton< file_collector_repository, shared_ptr< file_collector_repository > > + file_collector_base::file_info + file_collector_base::stored_file_name(filesystem::path const& src_path) { - private: - //! Base type - typedef log::aux::lazy_singleton< file_collector_repository, shared_ptr< file_collector_repository > > base_type; + file_info info(src_path); -#if !defined(BOOST_LOG_BROKEN_FRIEND_TEMPLATE_INSTANTIATIONS) - friend class log::aux::lazy_singleton< file_collector_repository, shared_ptr< file_collector_repository > >; -#else - friend class base_type; -#endif - - //! The type of the list of collectors - typedef intrusive::list< - file_collector, - intrusive::base_hook< file_collector_hook > - > file_collectors; - - private: -#if !defined(BOOST_LOG_NO_THREADS) - //! Synchronization mutex - mutex m_Mutex; -#endif // !defined(BOOST_LOG_NO_THREADS) - //! The list of file collectors - file_collectors m_Collectors; - - public: - //! Finds or creates a file collector - shared_ptr< file::collector > get_collector( - filesystem::path const& target_dir, uintmax_t max_size, uintmax_t min_free_space); - - //! Removes the file collector from the list - void remove_collector(file_collector* p); - - private: - //! Initializes the singleton instance - static void init_instance() - { - base_type::get_instance() = boost::make_shared< file_collector_repository >(); - } - }; - - //! Constructor - file_collector::file_collector( - shared_ptr< file_collector_repository > const& repo, - filesystem::path const& target_dir, - uintmax_t max_size, - uintmax_t min_free_space - ) : - m_pRepository(repo), - m_MaxSize(max_size), - m_MinFreeSpace(min_free_space), - m_BasePath(filesystem::current_path()), - m_TotalSize(0) - { - m_StorageDir = make_absolute(target_dir); - filesystem::create_directories(m_StorageDir); - } - - //! Destructor - file_collector::~file_collector() - { - m_pRepository->remove_collector(this); - } - - //! The function stores the specified file in the storage - void file_collector::store_file(filesystem::path const& src_path) - { - // Let's construct the new file name - file_info info; - info.m_TimeStamp = filesystem::last_write_time(src_path); - info.m_Size = filesystem::file_size(src_path); - path_string_type file_name = filename_string(src_path); info.m_Path = m_StorageDir / file_name; @@ -645,34 +729,36 @@ filesystem::path src_dir = src_path.has_parent_path() ? filesystem::system_complete(src_path.parent_path()) : m_BasePath; - const bool is_in_target_dir = filesystem::equivalent(src_dir, m_StorageDir); - if (!is_in_target_dir) + + if (filesystem::equivalent(src_dir, m_StorageDir)) return info; + + //special handling for the case when the file is from outside directory + if (filesystem::exists(info.m_Path)) { - if (filesystem::exists(info.m_Path)) + // If the file already exists, try to mangle the file name + // to ensure there's no conflict. I'll need to make this customizable some day. + file_counter_formatter formatter(file_name.size(), 5); + unsigned int n = 0; + do { - // If the file already exists, try to mangle the file name - // to ensure there's no conflict. I'll need to make this customizable some day. - file_counter_formatter formatter(file_name.size(), 5); - unsigned int n = 0; - do - { - path_string_type alt_file_name = formatter(file_name, n++); - info.m_Path = m_StorageDir / alt_file_name; - } - while (filesystem::exists(info.m_Path) && n < (std::numeric_limits< unsigned int >::max)()); + path_string_type alt_file_name = formatter(file_name, n++); + info.m_Path = m_StorageDir / alt_file_name; } - - // The directory should have been created in constructor, but just in case it got deleted since then... - filesystem::create_directories(m_StorageDir); + while (filesystem::exists(info.m_Path) && n < (std::numeric_limits< unsigned int >::max)()); } + return info; + } + + void file_collector_base::cleanup_storage(uintmax_t size_increment) + { BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);) // Check if an old file should be erased uintmax_t free_space = m_MinFreeSpace ? filesystem::space(m_StorageDir).available : static_cast< uintmax_t >(0); file_list::iterator it = m_Files.begin(), end = m_Files.end(); while (it != end && - (m_TotalSize + info.m_Size > m_MaxSize || (m_MinFreeSpace && m_MinFreeSpace > free_space))) + (m_TotalSize + size_increment > m_MaxSize || (m_MinFreeSpace && m_MinFreeSpace > free_space))) { file_info& old_info = *it; if (filesystem::exists(old_info.m_Path) && filesystem::is_regular_file(old_info.m_Path)) @@ -700,19 +786,23 @@ m_Files.erase(it++); } } - - if (!is_in_target_dir) + } + + void file_collector_base::append_file(filesystem::path const& src_path, const file_info& info) + { + if (!filesystem::exists(info.m_Path)) { - // Move/rename the file to the target storage + // The directory should have been created in constructor, but just in case it got deleted since then... + filesystem::create_directories(m_StorageDir); move_file(src_path, info.m_Path); } - + m_Files.push_back(info); m_TotalSize += info.m_Size; } //! Scans the target directory for the files that have already been stored - uintmax_t file_collector::scan_for_files( + uintmax_t file_collector_base::scan_for_files( file::scan_method method, filesystem::path const& pattern, unsigned int* counter) { uintmax_t file_count = 0; @@ -779,6 +869,7 @@ // Sort files chronologically m_Files.splice(m_Files.end(), files); m_TotalSize += total_size; + //TODO: this sorting could fail when several files were generated within 1 second m_Files.sort(boost::bind(&file_info::m_TimeStamp, _1) < boost::bind(&file_info::m_TimeStamp, _2)); } } @@ -787,7 +878,7 @@ } //! The function updates storage restrictions - void file_collector::update(uintmax_t max_size, uintmax_t min_free_space) + void file_collector_base::update(uintmax_t max_size, uintmax_t min_free_space) { BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);) @@ -796,6 +887,186 @@ } + class file_collector_repository; + + //! Type of the hook used for sequencing file collectors + typedef intrusive::list_base_hook< + intrusive::link_mode< intrusive::safe_link > + > file_collector_hook; + + + //! Log file collector implementation + class file_collector : + public file_collector_base, + public file_collector_hook, + public enable_shared_from_this< file_collector > + { + //! A reference to the repository this collector belongs to + shared_ptr< file_collector_repository > m_pRepository; + public: + //! Constructor + file_collector( + shared_ptr< file_collector_repository > const& repo, + filesystem::path const& target_dir, + uintmax_t max_size, + uintmax_t min_free_space); + + //! Destructor + ~file_collector(); + + void store_file(filesystem::path const& src_path); + }; + + + //! Rotating log file collector, collects only files that are base on 1 file pattern + class rollover_file_collector : public file_collector_base + { + struct rename + { + filesystem::path& path; + + explicit rename(filesystem::path& p) + : path(p) + {} + + void operator () (file_info& fi) const + { + filesystem::rename(fi.m_Path, path); + using std::swap; + swap(fi.m_Path, path); + } + }; + + //! File name pattern + filesystem::path m_FileNamePattern; + //! File name generator + file_name_generator::type m_FileNameGenerator; + + void init(filesystem::path const& file_name) + { + file_name_generator::cookie cookie(file_name); + cookie.parse(); + + if (cookie.formatter == file_name_generator::empty) + { + init(file_name.string< path_string_type >() + ".%N");//TODO: use file_char_traits + return ; + } + + if (cookie.formatter >= file_name_generator::date_time) + { + BOOST_LOG_THROW_DESCR(setup_error, "rollover collector supports only counter formatter"); + } + + m_FileNamePattern = file_name; + m_FileNameGenerator = file_name_generator::create(cookie); + } + public: + rollover_file_collector( + filesystem::path const& target_dir, + filesystem::path const& file_name, + uintmax_t max_size, + uintmax_t min_free_space + ) : file_collector_base(target_dir, max_size, min_free_space) + { + init(file_name); + } + + uintmax_t scan_for_files( + file::scan_method method, filesystem::path const& pattern, unsigned int* counter) + { + //ensure that pattern does not have any placeholders - + //in such case rotating collector will conflict with file sink + file_name_generator::cookie cookie(pattern); + cookie.parse(); + if (cookie.formatter != file_name_generator::empty) + { + BOOST_LOG_THROW_DESCR(setup_error, "File sink name pattern should not contain placeholders"); + } + + return file_collector_base::scan_for_files(method, m_FileNamePattern, counter); + } + + void store_file(filesystem::path const& src_path) + { + file_collector_base::store_file_impl(src_path); + + if (files_size() == 0) return ; + + filesystem::path last = storage_dir() / m_FileNameGenerator(files_size()); + + std::for_each(first_file(), last_file(), rename(last)); + } + }; + + + //! The singleton of the list of file collectors + class file_collector_repository : + public log::aux::lazy_singleton< file_collector_repository, shared_ptr< file_collector_repository > > + { + private: + //! Base type + typedef log::aux::lazy_singleton< file_collector_repository, shared_ptr< file_collector_repository > > base_type; + +#if !defined(BOOST_LOG_BROKEN_FRIEND_TEMPLATE_INSTANTIATIONS) + friend class log::aux::lazy_singleton< file_collector_repository, shared_ptr< file_collector_repository > >; +#else + friend class base_type; +#endif + + //! The type of the list of collectors + typedef intrusive::list< + file_collector, + intrusive::base_hook< file_collector_hook > + > file_collectors; + + private: +#if !defined(BOOST_LOG_NO_THREADS) + //! Synchronization mutex + mutex m_Mutex; +#endif // !defined(BOOST_LOG_NO_THREADS) + //! The list of file collectors + file_collectors m_Collectors; + + public: + //! Finds or creates a file collector + shared_ptr< file::collector > get_collector( + filesystem::path const& target_dir, uintmax_t max_size, uintmax_t min_free_space); + + //! Removes the file collector from the list + void remove_collector(file_collector* p); + + private: + //! Initializes the singleton instance + static void init_instance() + { + base_type::get_instance() = boost::make_shared< file_collector_repository >(); + } + }; + + //! Constructor + file_collector::file_collector( + shared_ptr< file_collector_repository > const& repo, + filesystem::path const& target_dir, + uintmax_t max_size, + uintmax_t min_free_space + ) : + file_collector_base(target_dir, max_size, min_free_space), + m_pRepository(repo) + {} + + //! Destructor + file_collector::~file_collector() + { + m_pRepository->remove_collector(this); + } + + void file_collector::store_file(filesystem::path const& src_path) + { + file_collector_base::store_file_impl(src_path); + } + + //! Finds or creates a file collector shared_ptr< file::collector > file_collector_repository::get_collector( filesystem::path const& target_dir, uintmax_t max_size, uintmax_t min_free_space) @@ -870,6 +1141,16 @@ return file_collector_repository::get()->get_collector(target_dir, max_size, min_free_space); } + BOOST_LOG_API shared_ptr< collector > make_rollover_collector( + filesystem::path const& target_dir, + filesystem::path const& file_name, + uintmax_t max_size, + uintmax_t min_free_space) + { + return shared_ptr< collector >( + new rollover_file_collector(target_dir, file_name, max_size, min_free_space) + ); + } } // namespace aux //! Creates a rotation time point of every day at the specified time @@ -1030,7 +1311,7 @@ //! Directory to store files in filesystem::path m_StorageDir; //! File name generator (according to m_FileNamePattern) - boost::log::aux::light_function< path_string_type (unsigned int) > m_FileNameGenerator; + file_name_generator::type m_FileNameGenerator; //! Stored files counter unsigned int m_FileCounter; @@ -1188,65 +1469,7 @@ m_pImpl->m_FileNamePattern = name_pattern; m_pImpl->m_StorageDir = filesystem::absolute(p.parent_path()); - // Let's try to find the file counter placeholder - unsigned int placeholder_count = 0; - unsigned int width = 0; - bool counter_found = false; - path_string_type::size_type counter_pos = 0; - path_string_type::const_iterator end = name_pattern.end(); - path_string_type::const_iterator it = name_pattern.begin(); - - do - { - it = std::find(it, end, traits_t::percent); - if (it == end) - break; - path_string_type::const_iterator placeholder_begin = it++; - if (it == end) - break; - if (*it == traits_t::percent) - { - // An escaped percent detected - ++it; - continue; - } - - ++placeholder_count; - - if (!counter_found && parse_counter_placeholder(it, end, width)) - { - // We've found the file counter placeholder in the pattern - counter_found = true; - counter_pos = placeholder_begin - name_pattern.begin(); - name_pattern.erase(counter_pos, it - placeholder_begin); - --placeholder_count; - it = name_pattern.begin() + counter_pos; - end = name_pattern.end(); - } - } - while (it != end); - - // Construct the formatter functor - unsigned int choice = (static_cast< unsigned int >(placeholder_count > 0) << 1) | - static_cast< unsigned int >(counter_found); - switch (choice) - { - case 1: // Only counter placeholder in the pattern - m_pImpl->m_FileNameGenerator = - boost::bind(file_counter_formatter(counter_pos, width), name_pattern, _1); - break; - case 2: // Only date/time placeholders in the pattern - m_pImpl->m_FileNameGenerator = - boost::bind(date_and_time_formatter(), name_pattern, _1); - break; - case 3: // Counter and date/time placeholder in the pattern - m_pImpl->m_FileNameGenerator = boost::bind(date_and_time_formatter(), - boost::bind(file_counter_formatter(counter_pos, width), name_pattern, _1), _1); - break; - default: // No placeholders detected - m_pImpl->m_FileNameGenerator = empty_formatter(name_pattern); - break; - } + m_pImpl->m_FileNameGenerator = file_name_generator::create(name_pattern); } //! The method rotates the file Index: libs/log/example/rotating_file/main.cpp =================================================================== --- libs/log/example/rotating_file/main.cpp (revision 84158) +++ libs/log/example/rotating_file/main.cpp (working copy) @@ -41,24 +41,47 @@ enum { LOG_RECORDS_TO_WRITE = 10000 }; -int main(int argc, char* argv[]) + +class sink_builder { - try + static const uintmax_t m_MaxSize; + static const uintmax_t m_MinFreeSpace; +public: + typedef sinks::synchronous_sink< sinks::text_file_backend > file_sink; + typedef shared_ptr< file_sink > file_sink_ptr; + + typedef shared_ptr< sinks::file::collector > collector_ptr; + + file_sink_ptr make(std::string const& file_name_pattern) const { - // Create a text file sink - typedef sinks::synchronous_sink< sinks::text_file_backend > file_sink; - shared_ptr< file_sink > sink(new file_sink( - keywords::file_name = "%Y%m%d_%H%M%S_%5N.log", // file name pattern - keywords::rotation_size = 16384 // rotation size, in characters - )); + return file_sink_ptr(new file_sink( + keywords::file_name = file_name_pattern, + keywords::rotation_size = 16384 + ) + ); + } - // Set up where the rotated files will be stored - sink->locked_backend()->set_file_collector(sinks::file::make_collector( - keywords::target = "logs", // where to store rotated files - keywords::max_size = 16 * 1024 * 1024, // maximum total size of the stored files, in bytes - keywords::min_free_space = 100 * 1024 * 1024 // minimum free space on the drive, in bytes - )); + collector_ptr make_collector(std::string const& target_dir) const + { + return sinks::file::make_collector( + keywords::target = target_dir, + keywords::max_size = m_MaxSize, + keywords::min_free_space = m_MinFreeSpace + ); + } + collector_ptr make_rollover_collector(std::string const& target_dir, std::string const& file_name_pattern) const + { + return sinks::file::make_rollover_collector( + keywords::target = target_dir, + keywords::file_name = file_name_pattern, + keywords::max_size = m_MaxSize, + keywords::min_free_space = m_MinFreeSpace + ); + } + + void setup(file_sink_ptr const& sink) const + { // Upon restart, scan the target directory for files matching the file_name pattern sink->locked_backend()->scan_for_files(); @@ -69,9 +92,36 @@ % expr::attr< boost::posix_time::ptime >("TimeStamp") % expr::smessage ); + } +}; - // Add it to the core +const uintmax_t sink_builder::m_MaxSize = 16 * 1024 * 1024; +const uintmax_t sink_builder::m_MinFreeSpace = 100 * 1024 * 1024; + +int main(int argc, char* argv[]) +{ + try + { + sink_builder builder; + + // Let's setup 2 sinks: + // 1st will be rotating sink with regular file collector, + // 2nd - regular sink with rotating file collector + + sink_builder::file_sink_ptr sink = builder.make("%Y%m%d_%H%M%S_%5N.log"); + sink->locked_backend()->set_file_collector(builder.make_collector("logs/chrono")); + + sink_builder::file_sink_ptr rsink = builder.make("logs/app.log"); + rsink->locked_backend()->set_file_collector( + builder.make_rollover_collector("logs", "app.log") + ); + + builder.setup(sink); + builder.setup(rsink); + + // Add them to the core logging::core::get()->add_sink(sink); + logging::core::get()->add_sink(rsink); // Add some attributes too logging::core::get()->add_global_attribute("TimeStamp", attrs::local_clock());