#ifndef __BOOST_LOG_APPENDERS_HPP #define __BOOST_LOG_APPENDERS_HPP #if defined (_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif #include #include #include #include #include #include #include namespace boost { namespace log { namespace fs = boost::filesystem; /// Logfile class encapsulates our file stream, filename prototype, /// filename factory method and the actual generated filename struct logfile { typedef log_types::log_name_string_type (*filename_factory) (const log_types::log_name_string_type& proto); filename_factory filename_generator; log_types::log_name_string_type filename_proto; log_types::log_name_string_type filename; std::ofstream file; logfile (const log_types::log_name_string_type& filename_proto, filename_factory factory = 0) : filename_proto (filename_proto), filename_generator (factory) {} virtual ~logfile () { close (); } void close () { file.close (); } void open (bool append = false) { file.clear (); if (filename_generator) filename = (*filename_generator) (filename_proto); else filename = filename_proto; file.open (filename.c_str (), std::ios::out | (append ? std::ios::app : std::ios::trunc)); } }; /// Basic logging class that appends to a file specified by the user. /// The filename can be a prototype filename which is struct file_appender { boost::shared_ptr log; file_appender (const log_types::log_name_string_type& filename_proto, logfile::filename_factory filename_factory = 0, bool append = true) : log (new logfile (filename_proto, filename_factory)) { log->open (append); } void operator () (const log_types::string&, const log_types::string& msg) { write (msg); } protected: virtual void write (const log_types::string& msg) { log->file.write (msg.data (), msg.size ()); } }; /// File appender that allows user to specify a maximum file size and /// number of files to keep. When logfiles meet or exceed the maximum /// file size, they are renamed to numbered backups in the following /// progression: file -> file.1 -> file.2 -> ... -> file. /// -> removal. Therefore the most recent logfile is always /// and the nexst oldest is file.1 etc. struct rolling_file_appender : public file_appender { int max_size; int num_files; rolling_file_appender (const log_types::log_name_string_type& proto, int max_size, int num_files = 0, logfile::filename_factory filename_factory = 0, bool append = true) : file_appender (proto, filename_factory, append), max_size (max_size), num_files (num_files) {} protected: void write (const log_types::string& msg) { file_appender::write (msg); /// If there is a file size limit and we have met or exceeded /// it, close the log, run the backup policy and open the /// logfile again if (max_size != 0 && log->file.tellp () >= max_size) { log->close (); rollover (log->filename); log->open (false); } } /// roll over the logfile virtual void rollover (const log_types::log_name_string_type& file) const; }; void rolling_file_appender::rollover (const log_types::log_name_string_type& file) const { for (int suffix = num_files - 1; suffix >= 0; --suffix) { std::ostringstream from, to; from << file; if (suffix) from << '.' << suffix; to << file << '.' << (suffix + 1); fs::path from_p (from.str ()), to_p (to.str ()); if (fs::exists (from_p)) { fs::remove (to_p); fs::rename (from_p, to_p); } } } /// filename factory method which passes the prototype through /// "strftime" log_types::log_name_string_type strftime_filename_factory (const log_types::log_name_string_type& proto) { char buf[FILENAME_MAX]; time_t t = time (0); tm tm = *localtime (&t); int len = strftime (buf, sizeof (buf), proto.c_str (), &tm); return (len == 0 ? proto : log_types::log_name_string_type (buf, len)); } } // namespace log } // namespace boost #endif // macro guard