/******************************************************************************/ /* LOGGING SYSTEM - BUILT-IN LOGGERS - TEXT FILE LOGGER ***********************/ /******************************************************************************/ #include <lw/lib/BuiltinLoggers.hh> #include <lw/lib/SRDParser.hh> using namespace lw; namespace { char const* const V_Name_ = "text-file"; inline T_String Name_( ) { return T_String::Pooled( V_Name_ ); } bool TFLCPath_( T_SRDParserData const& data ) { auto const& ptok( (*data.input)[ 1 ] ); T_VFSPath path( ptok.stringValue( ) ); path = path.normalize( ); if ( path.type( ) != E_VFSPathType::ABSOLUTE ) { data.errors.add( "invalid path" , ptok ); } else { auto lconf( data.targetData->value< RP_LogWriterConfiguration >( ) ); ( dynamic_cast< T_TextFileLogWriterCfg* >( lconf ) )->setPath( std::move( path ) ); } return true; } bool TFLCAppend_( T_SRDParserData const& data ) { auto lconf( data.targetData->value< RP_LogWriterConfiguration >( ) ); ( dynamic_cast< T_TextFileLogWriterCfg* >( lconf ) )->setAppend( true ); return true; } bool TFLCTruncate_( T_SRDParserData const& data ) { auto lconf( data.targetData->value< RP_LogWriterConfiguration >( ) ); ( dynamic_cast< T_TextFileLogWriterCfg* >( lconf ) )->setAppend( false ); return true; } } /*= T_TextFileLogWriterFactory ==================================================*/ T_TextFileLogWriterFactory::T_TextFileLogWriterFactory( T_VFS& vfs ) : A_LogWriterFactory( Name_( ) ) , vfs_( vfs ) { } RP_LogWriterConfiguration T_TextFileLogWriterFactory::createConfiguration( T_String const& name ) const { RP_LogWriterConfiguration p( new T_TextFileLogWriterCfg( ) ); p->setName( name ); return p; } OP_LogWriter T_TextFileLogWriterFactory::createLogWriter( OP_LogWriterConfiguration&& configuration ) const { T_TextFileLogWriter* p( new T_TextFileLogWriter( std::move( configuration ) , vfs_ ) ); return OwnRawPointer( p ); } void T_TextFileLogWriterFactory::initializeSyntax( T_SRDParserDefs& , T_SRDContext& main ) const { using namespace lw::SRD; main << ( Rule( ) << "file" << Text( ) << TFLCPath_ ); main << ( Rule( ) << "append" << TFLCAppend_ ); main << ( Rule( ) << "truncate" << TFLCTruncate_ ); } /*= T_TextFileLogWriterCfg ======================================================*/ T_TextFileLogWriterCfg::T_TextFileLogWriterCfg( ) : T_LogWriterConfiguration( Name_( ) ) { } T_TextFileLogWriterCfg::T_TextFileLogWriterCfg( T_TextFileLogWriterCfg const& source ) : T_LogWriterConfiguration( source ) , path_( source.path_ ) , append_( source.append_ ) { } OP_LogWriterConfiguration T_TextFileLogWriterCfg::clone( ) { T_TextFileLogWriterCfg* ptr( new T_TextFileLogWriterCfg( *this ) ); return OwnRawPointer( ptr ); } void T_TextFileLogWriterCfg::check( T_SRDErrors& errors , T_SRDList const& input ) { T_LogWriterConfiguration::check( errors , input ); if ( path_.type( ) == E_VFSPathType::UNKNOWN || path_.elements( ) == 0 ) { errors.add( "no file selected" , input[ 0 ] ); } } /*= T_TextFileLogWriter =========================================================*/ T_TextFileLogWriter::T_TextFileLogWriter( OP_LogWriterConfiguration&& configuration , T_VFS& vfs ) : A_LogWriter( std::move( configuration ) ) , vfs_( vfs ) { } void T_TextFileLogWriter::log( T_LogTimestamp const& timestamp , E_LogLevel level , T_LogPath const& path , T_LogStringData const& data , uint32_t size ) { using namespace std::chrono; char timeBuffer[ 128 ]; std::time_t tst( T_LogTimestamp::clock::to_time_t( timestamp ) ); std::strftime( timeBuffer , 128 , "%Y-%m-%d %H:%M:%S" , std::gmtime( &tst ) ); const auto ms( ( duration_cast< milliseconds >( timestamp - T_LogTimestamp( ) ) ).count( ) % 1000 ); T_StringBuilder sb; sb << timeBuffer << '.'; if ( ms < 100 ) { sb << '0'; if ( ms < 10 ) { sb << '0'; } } sb << ms << ' ' << path.toString( ) << " - " << level << ": "; sb.append( &( (*data) [ 0 ] ) , size ); sb << '\n'; auto const& cfg( configuration< T_TextFileLogWriterCfg >( ) ); auto const& p( cfg.path( ) ); if ( !file_ ) { vfs_.mkdir( p.parent( ) ); file_ = vfs_.file( p , cfg.append( ) ? E_FileMode::READ_WRITE : E_FileMode::OVERWRITE ); if ( !file_ ) { disable( ); return; } try { file_->open( ); } catch ( X_StreamError const& ) { disable( ); file_.clear( ); return; } } try { file_->position( 0 , true ); file_->write( sb.data( ) , sb.size( ) ); file_->flush( ); } catch ( X_StreamError ) { disable( ); file_.clear( ); return; } }