khanat-opennel-code/code/ryzom/server/src/log_analyser_service/commands.cpp

627 lines
15 KiB
C++

// 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;
}