2012-05-29 13:31:11 +00:00
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
# include <nel/misc/types_nl.h>
# include <nel/misc/common.h>
# include <nel/misc/variable.h>
# include <nel/misc/command.h>
# include <nel/misc/file.h>
# include <nel/misc/path.h>
# include "game_share/utils.h"
# include "pd_lib/pd_lib.h"
# include "pd_lib/db_description_parser.h"
# include "pd_lib/pd_messages.h"
# include "log_analyser_service.h"
using namespace std ;
using namespace NLMISC ;
using namespace NLNET ;
using namespace RY_PDS ;
/*
* SUMMARY OF COMMANDS :
*
* displayLogContent < description file > < filename >
* Display readable content of a log file
* description file : xml file of database description ( with full path ) ,
* generated by pd_parser , this must be the matching description file
* to the log file to display ( that is the youngest description that
* is older than the log file )
* filename : the file to display content of ( with full path )
*
*/
std : : string decodeDbPathId ( const std : : string & pathid )
{
std : : string shard ( " " ) ;
uint id ;
string : : size_type p = pathid . find ( ' : ' ) ;
if ( p = = std : : string : : npos )
{
NLMISC : : fromString ( pathid , id ) ;
}
else
{
shard = pathid . substr ( 0 , p ) ;
NLMISC : : fromString ( pathid . substr ( p + 1 ) , id ) ;
}
return CPDSLib : : getLogDirectory ( id , shard ) ;
}
class CSimpleFileDisplayer : public IDisplayer
{
public :
CSimpleFileDisplayer ( const CSString & fileName ) : _FileName ( fileName )
{
_File = NULL ;
_TmpFileName = _FileName + " .tmp " ;
// if the file that we've been asked to create already exists then try to delete the existing file
if ( NLMISC : : CFile : : fileExists ( _FileName ) )
NLMISC : : CFile : : deleteFile ( _FileName ) ;
DROP_IF ( NLMISC : : CFile : : fileExists ( _FileName ) , " Failed to delete file: " + _FileName , return ) ;
// if the temp file that we're going to use already exists then try to delete the existing file
if ( NLMISC : : CFile : : fileExists ( _TmpFileName ) )
NLMISC : : CFile : : deleteFile ( _TmpFileName ) ;
DROP_IF ( NLMISC : : CFile : : fileExists ( _TmpFileName ) , " Failed to delete file: " + _TmpFileName , return ) ;
_File = fopen ( _TmpFileName . c_str ( ) , " wb " ) ;
}
~ CSimpleFileDisplayer ( )
{
fclose ( _File ) ;
// rename the created file...
DROP_IF ( ! NLMISC : : CFile : : fileExists ( _TmpFileName ) , " No output file created: " + _TmpFileName , return ) ;
DROP_IF ( NLMISC : : CFile : : fileExists ( _FileName ) , " Cannot rename output file ' " + _TmpFileName + " ' because another file is in the way: " + _FileName , return ) ;
NLMISC : : CFile : : moveFile ( _FileName . c_str ( ) , _TmpFileName . c_str ( ) ) ;
DROP_IF ( ! NLMISC : : CFile : : fileExists ( _FileName ) , " Failed to create final output file: ' " + _FileName + " ' from tmp file: ' " + _TmpFileName + " ' " , return ) ;
}
bool isOK ( ) const
{
return _File ! = NULL ;
}
protected :
virtual void doDisplay ( const CLog : : TDisplayInfo & args , const char * message )
{
if ( isOK ( ) )
{
fwrite ( message , strlen ( message ) , 1 , _File ) ;
}
}
private :
FILE * _File ;
CSString _FileName ;
CSString _TmpFileName ;
} ;
NLMISC_CATEGORISED_COMMAND ( pd_log , executeToFile , " execute a command, spuing output to a file " , " <filename> <commandline> " )
{
// split the raw commandline into a file name and remaining commandline
NLMISC : : CSString s = rawCommandString ;
// skip the wrapper command name
s . strtok ( " \t " ) ;
// get the file name
NLMISC : : CSString fileName = s . strtok ( " \t " , true ) . unquoteIfQuoted ( ) ;
// cleanup the remaining commandline
s = s . strip ( ) ;
DROP_IF ( s . empty ( ) , " Too few elements found in command line " , return false ) ;
// setup a log object with an attached file displayer
CSimpleFileDisplayer * displayer = new CSimpleFileDisplayer ( fileName ) ;
DROP_IF ( ! displayer - > isOK ( ) , " Aborting command execution because failed to setup file displayer " , return false ) ;
CLog theLog ;
theLog . addDisplayer ( displayer , true ) ;
// execute the command...
NLMISC : : ICommand : : execute ( s , theLog ) ;
// remove the displayer and destroy it to ensure files are closed etc
theLog . removeDisplayer ( displayer ) ;
delete displayer ;
// success fo return true
return true ;
}
//
NLMISC_CATEGORISED_COMMAND ( pd_log , displayLogFile , " display pd_log file human readable content " , " <description file> <filename> " )
{
if ( args . size ( ) ! = 2 )
return false ;
string descriptionfile = args [ 0 ] ;
string filename = args [ 1 ] ;
CDBDescriptionParser desc ;
if ( ! desc . loadDescriptionFile ( descriptionfile ) )
{
log . displayNL ( " #! Failed to load database description file '%s' " , descriptionfile . c_str ( ) ) ;
return false ;
}
if ( ! desc . buildColumns ( ) )
{
log . displayNL ( " #! Failed to build database columns for description '%s' " , descriptionfile . c_str ( ) ) ;
return false ;
}
CIFile ifile ;
std : : vector < CUpdateLog > updateLog ;
if ( ! ifile . open ( filename ) )
{
log . displayNL ( " #! Failed to open file '%s' " , filename . c_str ( ) ) ;
return false ;
}
while ( ! ifile . eof ( ) )
{
try
{
ifile . serialCont ( updateLog ) ;
}
catch ( const Exception & e )
{
log . displayNL ( " #! Failed to load file '%s': %s " , filename . c_str ( ) , e . what ( ) ) ;
return false ;
}
uint i ;
for ( i = 0 ; i < updateLog . size ( ) ; + + i )
updateLog [ i ] . display ( desc , log ) ;
}
return true ;
}
//
NLMISC_CATEGORISED_COMMAND ( pd_log , displayLog , " display pd_log file human readable content " , " [shard:]<databaseId> <filename w/o path> " )
{
if ( args . size ( ) ! = 2 )
return false ;
/*
uint databaseId ;
NLMISC : : fromString ( args [ 0 ] , databaseId ) ;
string logpath = CPDSLib : : getLogDirectory ( databaseId ) ;
*/
string logpath = decodeDbPathId ( args [ 0 ] ) ;
string filename = args [ 1 ] ;
std : : string descriptionfile = CUpdateLog : : electDescription ( logpath + filename ) ;
if ( descriptionfile . empty ( ) )
{
log . displayNL ( " #! Failed to elect description file for file '%s' " , filename . c_str ( ) ) ;
return false ;
}
CDBDescriptionParser desc ;
if ( ! desc . loadDescriptionFile ( descriptionfile ) )
{
log . displayNL ( " #! Failed to load database description file '%s' " , descriptionfile . c_str ( ) ) ;
return false ;
}
if ( ! desc . buildColumns ( ) )
{
log . displayNL ( " #! Failed to build database columns for description '%s' " , descriptionfile . c_str ( ) ) ;
return false ;
}
CIFile ifile ;
std : : vector < CUpdateLog > updateLog ;
if ( ! ifile . open ( logpath + filename ) )
{
log . displayNL ( " #! Failed to open file '%s' " , filename . c_str ( ) ) ;
return false ;
}
while ( ! ifile . eof ( ) )
{
try
{
ifile . serialCont ( updateLog ) ;
}
catch ( const Exception & e )
{
log . displayNL ( " #! Failed to load file '%s': %s " , filename . c_str ( ) , e . what ( ) ) ;
return false ;
}
uint i ;
for ( i = 0 ; i < updateLog . size ( ) ; + + i )
updateLog [ i ] . display ( desc , log ) ;
}
return true ;
}
//
NLMISC_CATEGORISED_COMMAND ( pd_log , displayLogs ,
" display logs content between 2 dates (date format is YYYY.MM.DD.hh.mm[.ss], or <-|+>nn<d|h|m|s> to specify relative date) " ,
" [shard:]<databaseId> <startdate> [enddate] " )
{
if ( args . size ( ) < 2 | | args . size ( ) > 3 )
return false ;
/*
uint databaseId ;
NLMISC : : fromString ( args [ 0 ] , databaseId ) ;
string logpath = CPDSLib : : getLogDirectory ( databaseId ) ;
*/
string logpath = decodeDbPathId ( args [ 0 ] ) ;
CTimestamp startdate ;
CTimestamp enddate ;
startdate . setToCurrent ( ) ;
startdate . fromString ( args [ 1 ] . c_str ( ) ) ;
if ( args . size ( ) = = 3 )
{
enddate = startdate ;
enddate . fromString ( args [ 2 ] . c_str ( ) ) ;
}
else
{
enddate . setToCurrent ( ) ;
}
CLogAnalyserService : : CQuery * q = CLogAnalyserService : : getInstance ( ) - > getCurrentQuery ( ) ;
CUpdateLog : : displayLogs ( logpath , startdate , enddate , log , ( q ! = NULL ? ( float * ) & ( q - > Progress ) : NULL ) ) ;
return true ;
}
//
NLMISC_CATEGORISED_COMMAND ( pd_log , searchEId ,
" search for references to an entity id in logs between 2 dates (date format is YYYY:MM:DD:hh:mm[:ss], or <-|+>nn<d|h|m|s> to specify relative date) " ,
" [shard:]<databaseId> <entityId> <startdate> [enddate] " )
{
if ( args . size ( ) < 3 | | args . size ( ) > 4 )
return false ;
/*
uint databaseId ;
NLMISC : : fromString ( args [ 0 ] , databaseId ) ;
string logpath = CPDSLib : : getLogDirectory ( databaseId ) ;
*/
string logpath = decodeDbPathId ( args [ 0 ] ) ;
CEntityId entityId ;
entityId . fromString ( args [ 1 ] . c_str ( ) ) ;
CTimestamp startdate ;
CTimestamp enddate ;
startdate . setToCurrent ( ) ;
startdate . fromString ( args [ 2 ] . c_str ( ) ) ;
if ( args . size ( ) = = 4 )
{
enddate = startdate ;
enddate . fromString ( args [ 3 ] . c_str ( ) ) ;
}
else
{
enddate . setToCurrent ( ) ;
}
CLogAnalyserService : : CQuery * q = CLogAnalyserService : : getInstance ( ) - > getCurrentQuery ( ) ;
CUpdateLog : : displayLogs ( logpath , entityId , startdate , enddate , log , ( q ! = NULL ? ( float * ) & ( q - > Progress ) : NULL ) ) ;
return true ;
}
//
NLMISC_CATEGORISED_COMMAND ( pd_log , searchString ,
" search for references to a string in logs between 2 dates (date format is YYYY:MM:DD:hh:mm[:ss], or <-|+>nn<d|h|m|s> to specify relative date) " ,
" [shard:]<databaseId> <string> <startdate> [enddate] " )
{
if ( args . size ( ) < 3 | | args . size ( ) > 4 )
return false ;
/*
uint databaseId ;
NLMISC : : fromString ( args [ 0 ] , databaseId ) ;
string logpath = CPDSLib : : getLogDirectory ( databaseId ) ;
*/
string logpath = decodeDbPathId ( args [ 0 ] ) ;
std : : string str = args [ 1 ] ;
CTimestamp startdate ;
CTimestamp enddate ;
startdate . setToCurrent ( ) ;
startdate . fromString ( args [ 2 ] . c_str ( ) ) ;
if ( args . size ( ) = = 4 )
{
enddate = startdate ;
enddate . fromString ( args [ 3 ] . c_str ( ) ) ;
}
else
{
enddate . setToCurrent ( ) ;
}
CLogAnalyserService : : CQuery * q = CLogAnalyserService : : getInstance ( ) - > getCurrentQuery ( ) ;
CUpdateLog : : displayLogs ( logpath , str , startdate , enddate , log , ( q ! = NULL ? ( float * ) & ( q - > Progress ) : NULL ) ) ;
return true ;
}
//
NLMISC_CATEGORISED_COMMAND ( pd_log , searchEIds ,
" search for references to multiple entity ids in logs between 2 dates (date format is YYYY:MM:DD:hh:mm[:ss], or <-|+>nn<d|h|m|s> to specify relative date) " ,
" [shard:]<databaseId> [entityId+] - <startdate> [enddate] " )
{
if ( args . size ( ) < 4 )
return false ;
/*
uint databaseId ;
NLMISC : : fromString ( args [ 0 ] , databaseId ) ;
string logpath = CPDSLib : : getLogDirectory ( databaseId ) ;
*/
string logpath = decodeDbPathId ( args [ 0 ] ) ;
std : : vector < NLMISC : : CEntityId > ids ;
uint i = 1 ;
while ( i < args . size ( ) & & args [ i ] ! = " - " )
{
NLMISC : : CEntityId id ;
id . fromString ( args [ i ] . c_str ( ) ) ;
ids . push_back ( id ) ;
+ + i ;
}
+ + i ;
if ( i > = args . size ( ) )
return false ;
CTimestamp startdate ;
CTimestamp enddate ;
startdate . setToCurrent ( ) ;
startdate . fromString ( args [ i ] . c_str ( ) ) ;
+ + i ;
if ( i < args . size ( ) )
{
enddate = startdate ;
enddate . fromString ( args [ i ] . c_str ( ) ) ;
}
else
{
enddate . setToCurrent ( ) ;
}
CLogAnalyserService : : CQuery * q = CLogAnalyserService : : getInstance ( ) - > getCurrentQuery ( ) ;
CUpdateLog : : displayLogs ( logpath , ids , startdate , enddate , log , ( q ! = NULL ? ( float * ) & ( q - > Progress ) : NULL ) ) ;
return true ;
}
//
NLMISC_CATEGORISED_COMMAND ( pd_log , searchValueByEId ,
" search for references to an entity id in logs between 2 dates (date format is YYYY:MM:DD:hh:mm[:ss], or <-|+>nn<d|h|m|s> to specify relative date) " ,
" [shard:]<databaseId> <entityId> <valuePath> <startdate> [enddate] " )
{
if ( args . size ( ) < 4 | | args . size ( ) > 5 )
return false ;
/*
uint databaseId ;
NLMISC : : fromString ( args [ 0 ] , databaseId ) ;
string logpath = CPDSLib : : getLogDirectory ( databaseId ) ;
*/
string logpath = decodeDbPathId ( args [ 0 ] ) ;
CEntityId entityId ;
entityId . fromString ( args [ 1 ] . c_str ( ) ) ;
std : : string valuePath = args [ 2 ] ;
CTimestamp startdate ;
CTimestamp enddate ;
startdate . setToCurrent ( ) ;
startdate . fromString ( args [ 2 ] . c_str ( ) ) ;
if ( args . size ( ) = = 5 )
{
enddate = startdate ;
enddate . fromString ( args [ 3 ] . c_str ( ) ) ;
}
else
{
enddate . setToCurrent ( ) ;
}
CLogAnalyserService : : CQuery * q = CLogAnalyserService : : getInstance ( ) - > getCurrentQuery ( ) ;
CUpdateLog : : displayLogs ( logpath , entityId , valuePath , startdate , enddate , log , ( q ! = NULL ? ( float * ) & ( q - > Progress ) : NULL ) ) ;
return true ;
}
//
NLMISC_CATEGORISED_COMMAND ( pd_log , displayDescription ,
" Display database description. Asks for a specific file if more than one found. " ,
" [shard:]<databaseId> [descriptionfile] " )
{
if ( args . size ( ) < 1 | | args . size ( ) > 2 )
return false ;
/*
uint databaseId ;
NLMISC : : fromString ( args [ 0 ] , databaseId ) ;
string logpath = CPDSLib : : getLogDirectory ( databaseId ) ;
*/
string logpath = decodeDbPathId ( args [ 0 ] ) ;
std : : string useFile ;
if ( args . size ( ) = = 1 )
{
std : : vector < std : : string > files , found ;
NLMISC : : CPath : : getPathContent ( logpath , false , false , true , files ) ;
std : : sort ( files . begin ( ) , files . end ( ) ) ;
uint i ;
for ( i = 0 ; i < files . size ( ) ; + + i )
if ( NLMISC : : CFile : : getExtension ( files [ i ] ) = = " description " )
found . push_back ( files [ i ] ) ;
if ( found . size ( ) = = 0 )
{
log . displayNL ( " ## no description file found in path '%s' " , logpath . c_str ( ) ) ;
return true ;
}
if ( found . size ( ) > 1 )
{
log . displayNL ( " ## found multiple description files in path '%s': " , logpath . c_str ( ) ) ;
for ( i = 0 ; i < found . size ( ) ; + + i )
log . displayNL ( " #- %s " , NLMISC : : CFile : : getFilename ( found [ i ] ) . c_str ( ) ) ;
log . displayNL ( " ## found %d files, please select one " , found . size ( ) ) ;
return true ;
}
useFile = NLMISC : : CFile : : getFilename ( found [ 0 ] ) ;
log . displayNL ( " ## Using file '%s' " , useFile . c_str ( ) ) ;
}
else
{
useFile = args [ 1 ] ;
}
CDBDescriptionParser desc ;
if ( ! desc . loadDescriptionFile ( logpath + useFile ) )
return false ;
desc . buildColumns ( ) ;
desc . display ( log ) ;
return true ;
}
//
NLMISC_CATEGORISED_COMMAND ( pd_log , displayTableDescription ,
" Display table description. Asks for a specific file if more than one found. " ,
" [shard:]<databaseId> <tableName> [descriptionfile] " )
{
if ( args . size ( ) < 2 | | args . size ( ) > 3 )
return false ;
/*
uint databaseId ;
NLMISC : : fromString ( args [ 0 ] , databaseId ) ;
string logpath = CPDSLib : : getLogDirectory ( databaseId ) ;
*/
string logpath = decodeDbPathId ( args [ 0 ] ) ;
std : : string useFile ;
if ( args . size ( ) = = 2 )
{
std : : vector < std : : string > files , found ;
NLMISC : : CPath : : getPathContent ( logpath , false , false , true , files ) ;
std : : sort ( files . begin ( ) , files . end ( ) ) ;
uint i ;
for ( i = 0 ; i < files . size ( ) ; + + i )
if ( NLMISC : : CFile : : getExtension ( files [ i ] ) = = " description " )
found . push_back ( files [ i ] ) ;
if ( found . size ( ) = = 0 )
{
log . displayNL ( " ## no description file found in path '%s' " , logpath . c_str ( ) ) ;
return true ;
}
if ( found . size ( ) > 1 )
{
log . displayNL ( " ## found multiple description files in path '%s': " , logpath . c_str ( ) ) ;
for ( i = 0 ; i < found . size ( ) ; + + i )
log . displayNL ( " #- %s " , NLMISC : : CFile : : getFilename ( found [ i ] ) . c_str ( ) ) ;
log . displayNL ( " ## found %d files, please select one " , found . size ( ) ) ;
return true ;
}
useFile = NLMISC : : CFile : : getFilename ( found [ 0 ] ) ;
log . displayNL ( " ## Using file '%s' " , useFile . c_str ( ) ) ;
}
else
{
useFile = logpath + args [ 2 ] ;
}
std : : string tableName = toLower ( args [ 1 ] ) ;
CDBDescriptionParser desc ;
if ( ! desc . loadDescriptionFile ( logpath + useFile ) )
return false ;
const CDatabaseNode & db = desc . getDatabaseNode ( ) ;
uint i ;
for ( i = 0 ; i < db . Tables . size ( ) ; + + i )
if ( toLower ( db . Tables [ i ] . Name ) = = tableName )
break ;
if ( i = = db . Tables . size ( ) )
return false ;
desc . buildColumns ( ) ;
desc . displayTable ( i , log ) ;
return true ;
}