// Ryzom - MMORPG Framework // 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 . #include #include #include #include #include #include #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 * 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, _TmpFileName); 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 ", " ") { // 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", " ") { 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 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 ") { 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 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; inn to specify relative date)", "[shard:] [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 to specify relative date)", "[shard:] [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 to specify relative date)", "[shard:] [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 to specify relative date)", "[shard:] [entityId+] - [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 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 to specify relative date)", "[shard:] [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:] [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 files, found; NLMISC::CPath::getPathContent(logpath, false, false, true, files); std::sort(files.begin(), files.end()); uint i; for (i=0; i 1) { log.displayNL("## found multiple description files in path '%s':", logpath.c_str()); for (i=0; i [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 files, found; NLMISC::CPath::getPathContent(logpath, false, false, true, files); std::sort(files.begin(), files.end()); uint i; for (i=0; i 1) { log.displayNL("## found multiple description files in path '%s':", logpath.c_str()); for (i=0; i