2014-07-03 05:16:46 +00:00
|
|
|
#include <fc/io/fstream.hpp>
|
2013-06-05 19:19:00 +00:00
|
|
|
#include <fc/log/file_appender.hpp>
|
2014-07-03 05:16:46 +00:00
|
|
|
#include <fc/reflect/variant.hpp>
|
2013-06-05 19:19:00 +00:00
|
|
|
#include <fc/thread/scoped_lock.hpp>
|
|
|
|
|
#include <fc/variant.hpp>
|
2014-07-03 05:16:46 +00:00
|
|
|
#include <boost/thread/mutex.hpp>
|
2013-07-03 17:26:23 +00:00
|
|
|
#include <iomanip>
|
|
|
|
|
#include <sstream>
|
2013-02-05 05:06:16 +00:00
|
|
|
|
|
|
|
|
namespace fc {
|
|
|
|
|
class file_appender::impl : public fc::retainable {
|
|
|
|
|
public:
|
2014-05-17 20:38:32 +00:00
|
|
|
config cfg;
|
|
|
|
|
ofstream out;
|
2013-02-05 05:06:16 +00:00
|
|
|
boost::mutex slock;
|
2014-07-02 21:36:21 +00:00
|
|
|
time_point_sec current_file_start_time;
|
|
|
|
|
|
|
|
|
|
time_point_sec get_file_start_time( const time_point_sec& timestamp, const microseconds& interval )
|
|
|
|
|
{
|
|
|
|
|
const auto interval_seconds = interval.to_seconds();
|
|
|
|
|
const auto file_number = timestamp.sec_since_epoch() / interval_seconds;
|
|
|
|
|
return time_point_sec( file_number * interval_seconds );
|
|
|
|
|
}
|
2014-07-03 05:16:46 +00:00
|
|
|
|
|
|
|
|
void rotate_files( bool initializing = false )
|
|
|
|
|
{
|
|
|
|
|
if( !cfg.rotate ) return;
|
|
|
|
|
const auto now = time_point::now();
|
|
|
|
|
const auto start_time = get_file_start_time( now, cfg.rotation_interval );
|
|
|
|
|
if( !initializing )
|
|
|
|
|
{
|
|
|
|
|
if( start_time <= current_file_start_time ) return;
|
|
|
|
|
out.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto log_filename = cfg.filename.string();
|
|
|
|
|
|
|
|
|
|
/* Delete old log files */
|
|
|
|
|
const auto limit_time = now - cfg.rotation_limit;
|
|
|
|
|
auto itr = directory_iterator( fc::path( log_filename ).parent_path() );
|
|
|
|
|
for( ; itr != directory_iterator(); itr++ )
|
|
|
|
|
{
|
|
|
|
|
const auto current_filename = itr->string();
|
|
|
|
|
const auto current_pos = current_filename.find( log_filename );
|
|
|
|
|
if( current_pos != 0 ) continue;
|
|
|
|
|
const auto current_timestamp = current_filename.substr( log_filename.size() + 1 ); // TODO SKI{P EXTENSION
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if( std::stoi( current_timestamp ) < limit_time.sec_since_epoch() )
|
|
|
|
|
remove_all( current_filename );
|
|
|
|
|
//else compress if not already compressed
|
|
|
|
|
}
|
|
|
|
|
catch( ... )
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Convert to proper timestamp string
|
|
|
|
|
log_filename += "." + std::to_string( start_time.sec_since_epoch() );
|
|
|
|
|
out.open( log_filename.c_str() );
|
|
|
|
|
current_file_start_time = start_time;
|
|
|
|
|
}
|
2013-02-05 05:06:16 +00:00
|
|
|
};
|
|
|
|
|
file_appender::config::config( const fc::path& p )
|
2013-06-05 19:19:00 +00:00
|
|
|
:format( "${timestamp} ${thread_name} ${context} ${file}:${line} ${method} ${level}] ${message}" ),
|
2014-07-03 05:16:46 +00:00
|
|
|
filename(p),flush(true),truncate(true),rotate(false),rotation_compression(false){}
|
2013-02-05 05:06:16 +00:00
|
|
|
|
2013-06-05 19:19:00 +00:00
|
|
|
file_appender::file_appender( const variant& args )
|
2013-02-05 05:06:16 +00:00
|
|
|
:my( new impl() )
|
|
|
|
|
{
|
2014-07-02 21:36:21 +00:00
|
|
|
std::string log_filename;
|
|
|
|
|
try
|
|
|
|
|
{
|
2013-06-05 19:19:00 +00:00
|
|
|
my->cfg = args.as<config>();
|
2014-07-02 21:36:21 +00:00
|
|
|
log_filename = my->cfg.filename.string();
|
|
|
|
|
|
|
|
|
|
fc::create_directories( fc::path( log_filename ).parent_path() );
|
|
|
|
|
|
2014-07-03 05:16:46 +00:00
|
|
|
if( !my->cfg.rotate ) my->out.open( log_filename.c_str() );
|
|
|
|
|
else my->rotate_files( true );
|
2014-07-02 21:36:21 +00:00
|
|
|
}
|
|
|
|
|
catch( ... )
|
|
|
|
|
{
|
2014-07-03 05:16:46 +00:00
|
|
|
std::cerr << "error opening log file: " << log_filename << "\n";
|
2013-06-05 19:19:00 +00:00
|
|
|
//elog( "%s", fc::except_str().c_str() );
|
2013-02-05 05:06:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
file_appender::~file_appender(){}
|
|
|
|
|
|
2013-07-03 17:26:23 +00:00
|
|
|
// MS THREAD METHOD MESSAGE \t\t\t File:Line
|
2013-02-05 05:06:16 +00:00
|
|
|
void file_appender::log( const log_message& m )
|
|
|
|
|
{
|
2013-07-03 17:26:23 +00:00
|
|
|
std::stringstream line;
|
2014-06-24 20:29:59 +00:00
|
|
|
//line << (m.get_context().get_timestamp().time_since_epoch().count() % (1000ll*1000ll*60ll*60))/1000 <<"ms ";
|
|
|
|
|
line << std::string(m.get_context().get_timestamp()) << " ";
|
2013-07-03 17:26:23 +00:00
|
|
|
line << std::setw( 10 ) << m.get_context().get_thread_name().substr(0,9).c_str() <<" ";
|
|
|
|
|
|
|
|
|
|
auto me = m.get_context().get_method();
|
|
|
|
|
// strip all leading scopes...
|
|
|
|
|
if( me.size() )
|
|
|
|
|
{
|
|
|
|
|
uint32_t p = 0;
|
|
|
|
|
for( uint32_t i = 0;i < me.size(); ++i )
|
|
|
|
|
{
|
|
|
|
|
if( me[i] == ':' ) p = i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( me[p] == ':' ) ++p;
|
|
|
|
|
line << std::setw( 20 ) << m.get_context().get_method().substr(p,20).c_str() <<" ";
|
|
|
|
|
}
|
|
|
|
|
line << "] ";
|
2013-06-05 19:19:00 +00:00
|
|
|
fc::string message = fc::format_string( m.get_format(), m.get_data() );
|
2013-07-03 17:26:23 +00:00
|
|
|
line << message.c_str();
|
|
|
|
|
|
|
|
|
|
//fc::variant lmsg(m);
|
2013-02-05 05:06:16 +00:00
|
|
|
|
2014-07-02 21:36:21 +00:00
|
|
|
// fc::string fmt_str = fc::format_string( my->cfg.format, mutable_variant_object(m.get_context())( "message", message) );
|
|
|
|
|
|
|
|
|
|
/* Write to log file (rotating file beforehand if necessary) */
|
2013-02-05 05:06:16 +00:00
|
|
|
{
|
2014-07-02 21:36:21 +00:00
|
|
|
fc::scoped_lock<boost::mutex> lock( my->slock );
|
2014-07-03 05:16:46 +00:00
|
|
|
if( my->cfg.rotate ) my->rotate_files();
|
2013-07-03 17:26:23 +00:00
|
|
|
my->out << line.str() << "\t\t\t" << m.get_context().get_file() <<":"<<m.get_context().get_line_number()<<"\n";
|
2014-07-02 21:36:21 +00:00
|
|
|
if( my->cfg.flush ) my->out.flush();
|
2013-02-05 05:06:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|