Fix includes
parent
553a51488b
commit
6169126391
@ -1,581 +0,0 @@
|
||||
// NeLNS - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// 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 "log_report.h"
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include "nel/misc/common.h"
|
||||
#include "nel/misc/displayer.h"
|
||||
#include "nel/misc/file.h"
|
||||
#include "nel/misc/path.h"
|
||||
#include "nel/misc/variable.h"
|
||||
|
||||
using namespace NLMISC;
|
||||
using namespace std;
|
||||
|
||||
|
||||
CVariable<string> LogPath( "LogReport","LogPath", "Path of the log files", ".", 0, true );
|
||||
|
||||
const uint MAX_LOG_LINE_SIZE = 1024;
|
||||
//nlctassert(MAX_LOG_LINE_SIZE>0);
|
||||
|
||||
enum TLogLineHeader { LHDate, LHTime, LHType, LHThread, LHService, LHCodeFile, LHCodeLine, LHSeparator, LH_NB_FIELDS };
|
||||
|
||||
|
||||
///
|
||||
bool isLogFile( const std::string& filename )
|
||||
{
|
||||
uint len = (uint)filename.size();
|
||||
return (len >= 4 ) && (filename.substr( len-4 ) == ".log");
|
||||
}
|
||||
|
||||
///
|
||||
inline bool isNumberChar( char c )
|
||||
{
|
||||
return (c >= '0') && (c <= '9');
|
||||
}
|
||||
|
||||
///
|
||||
void sortLogFiles( vector<std::string>& filenames )
|
||||
{
|
||||
uint i;
|
||||
for ( i=0; i!=filenames.size(); ++i )
|
||||
{
|
||||
// Ensure that a log file without number comes *after* the ones with a number
|
||||
string name = string(filenames[i]);
|
||||
string::size_type dotpos = name.find_last_of('.');
|
||||
if ( (dotpos!=string::npos) && (dotpos > 2) )
|
||||
{
|
||||
if ( ! (isNumberChar(name[dotpos-1]) && isNumberChar(name[dotpos-2]) && isNumberChar(name[dotpos-3])) )
|
||||
{
|
||||
name = name.substr( 0, dotpos ) + "ZZZ" + name.substr( dotpos );
|
||||
filenames[i] = name.c_str();
|
||||
}
|
||||
}
|
||||
}
|
||||
sort( filenames.begin(), filenames.end() );
|
||||
for ( i=0; i!=filenames.size(); ++i )
|
||||
{
|
||||
// Set the original names back
|
||||
string name = filenames[i];
|
||||
string::size_type tokenpos = name.find( "ZZZ." );
|
||||
if ( tokenpos != string::npos )
|
||||
{
|
||||
name = name.substr( 0, tokenpos ) + name.substr( tokenpos + 3 );
|
||||
filenames[i] = name.c_str();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CMakeLogTask::setLogPath(const std::string & logPath)
|
||||
{
|
||||
_LogPaths.resize( 1 );
|
||||
_LogPaths[0] = logPath;
|
||||
}
|
||||
|
||||
void CMakeLogTask::setLogPaths(const std::vector<std::string>& logPaths)
|
||||
{
|
||||
_LogPaths = logPaths;
|
||||
}
|
||||
|
||||
void CMakeLogTask::setLogPathToDefault()
|
||||
{
|
||||
setLogPath( LogPath.get() );
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
CMakeLogTask::~CMakeLogTask()
|
||||
{
|
||||
if ( _Thread ) // implies && _OutputLogReport
|
||||
{
|
||||
if ( ! _Complete )
|
||||
{
|
||||
pleaseStop();
|
||||
_Thread->wait();
|
||||
}
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void CMakeLogTask::start()
|
||||
{
|
||||
if ( _Thread )
|
||||
{
|
||||
if ( _Complete )
|
||||
clear();
|
||||
else
|
||||
return;
|
||||
}
|
||||
_Stopping = false;
|
||||
_Complete = false;
|
||||
_Thread = NLMISC::IThread::create( this );
|
||||
_OutputLogReport = new CLogReport();
|
||||
_Thread->start();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void CMakeLogTask::clear()
|
||||
{
|
||||
if (_Thread)
|
||||
{
|
||||
delete _Thread;
|
||||
_Thread = NULL;
|
||||
}
|
||||
if (_OutputLogReport)
|
||||
{
|
||||
delete _OutputLogReport;
|
||||
_OutputLogReport = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void CMakeLogTask::terminateTask()
|
||||
{
|
||||
if (!_Thread) // _Thread _implies _OutputLogReport
|
||||
return;
|
||||
|
||||
pleaseStop();
|
||||
_Thread->wait();
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
//
|
||||
bool isOfLogDotLogFamily( const std::string& filename )
|
||||
{
|
||||
return ((filename == "log.log") ||
|
||||
((filename.size() == 10) &&
|
||||
(filename.substr( 0, 3 ) == "log") &&
|
||||
isNumberChar(filename[3]) && isNumberChar(filename[4]) && isNumberChar(filename[5]) &&
|
||||
(filename.substr( 6, 4 ) == ".log")) );
|
||||
}
|
||||
|
||||
|
||||
enum TVersionTargetMode { TTMAll, TTMMatchAllV, TTMMatchExactV, TTMMatchGreaterV, TTMMatchLowerV } targetMode;
|
||||
const uint CurrentVersion = std::numeric_limits<uint>::max();
|
||||
|
||||
// Return true and logVersion, or false if not a log with version
|
||||
bool getLogVersion( const std::string& filename, uint& logVersion )
|
||||
{
|
||||
uint len = (uint)filename.size();
|
||||
if ( (len > 4) && (filename.substr( len-4 ) == ".log") )
|
||||
{
|
||||
if ( filename.substr(0, 3) == "log" )
|
||||
{
|
||||
if ( (len == 7) ||
|
||||
((len == 10) && (isNumberChar(filename[3]) && isNumberChar(filename[4]) && isNumberChar(filename[5]))) )
|
||||
{
|
||||
logVersion = CurrentVersion;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if ( filename[0] == 'v' )
|
||||
{
|
||||
string::size_type p = filename.find( "_", 1 );
|
||||
if ( p != string::npos )
|
||||
{
|
||||
if ( (len == p + 8) ||
|
||||
((len == p + 11) && (isNumberChar(filename[p+4]) && isNumberChar(filename[p+5]) && isNumberChar(filename[p+6]))) )
|
||||
{
|
||||
NLMISC::fromString( filename.substr( 1, p-1 ), logVersion );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Assumes filename is .log file
|
||||
bool matchLogTarget( const std::string& filename, TVersionTargetMode targetMode, uint targetVersion )
|
||||
{
|
||||
if ( targetMode == TTMAll )
|
||||
return true;
|
||||
|
||||
uint version;
|
||||
|
||||
// Get version or exclude non-standard log files
|
||||
if ( ! getLogVersion( filename, version ) )
|
||||
return false;
|
||||
|
||||
// Exclude non-matching version
|
||||
switch ( targetMode )
|
||||
{
|
||||
case TTMMatchExactV:
|
||||
return (version == targetVersion); // break;
|
||||
case TTMMatchGreaterV:
|
||||
return (version >= targetVersion); // break;
|
||||
case TTMMatchLowerV:
|
||||
return (version <= targetVersion); // break;
|
||||
default: // TTMMatchAllV
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void CMakeLogTask::run()
|
||||
{
|
||||
// Parse log target
|
||||
uint targetVersion = CurrentVersion;
|
||||
uint lts = (uint)_LogTarget.size();
|
||||
if ( _LogTarget.empty() || (_LogTarget == "v") )
|
||||
{
|
||||
targetMode = TTMMatchExactV;
|
||||
}
|
||||
else if ( _LogTarget == "v*" )
|
||||
{
|
||||
targetMode = TTMMatchAllV;
|
||||
}
|
||||
else if ( _LogTarget == "*" )
|
||||
{
|
||||
targetMode = TTMAll;
|
||||
}
|
||||
else if ( (lts > 1) && (_LogTarget[0] == 'v') )
|
||||
{
|
||||
uint additionalChars = 1;
|
||||
if ( _LogTarget[lts-1] == '+' )
|
||||
targetMode = TTMMatchGreaterV;
|
||||
else if ( _LogTarget[lts-1] == '-' )
|
||||
targetMode = TTMMatchLowerV;
|
||||
else
|
||||
{
|
||||
targetMode = TTMMatchExactV;
|
||||
additionalChars = 0;
|
||||
}
|
||||
|
||||
NLMISC::fromString( _LogTarget.substr( 1, lts-additionalChars-1 ), targetVersion );
|
||||
}
|
||||
else
|
||||
{
|
||||
nlwarning( "Invalid log target argument: %s", _LogTarget.c_str() );
|
||||
_Complete = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get log files and sort them
|
||||
vector<string> filenames;
|
||||
vector<string> filenamesOfPath;
|
||||
for ( vector<string>::const_iterator ilf=_LogPaths.begin(); ilf!=_LogPaths.end(); ++ilf )
|
||||
{
|
||||
string path = (*ilf);
|
||||
if ( (! path.empty()) && (path[path.size()-1]!='/') )
|
||||
path += "/";
|
||||
filenamesOfPath.clear();
|
||||
CPath::getPathContent( path, false, false, true, filenamesOfPath, NULL, true );
|
||||
vector<string>::iterator ilf2 = partition( filenamesOfPath.begin(), filenamesOfPath.end(), isLogFile );
|
||||
filenamesOfPath.erase( ilf2, filenamesOfPath.end() );
|
||||
sortLogFiles( filenamesOfPath );
|
||||
filenames.insert( filenames.end(), filenamesOfPath.begin(), filenamesOfPath.end() );
|
||||
}
|
||||
|
||||
// Analyse log files
|
||||
_OutputLogReport->reset();
|
||||
uint nbLines = 0;
|
||||
char line [MAX_LOG_LINE_SIZE];
|
||||
|
||||
uint nbSkippedFiles = 0;
|
||||
for ( vector<string>::const_iterator ilf=filenames.begin(); ilf!=filenames.end(); ++ilf )
|
||||
{
|
||||
string shortname = CFile::getFilename( *ilf );
|
||||
|
||||
// Filter log files based on filename before opening them
|
||||
if ( ! matchLogTarget( shortname, targetMode, targetVersion ) )
|
||||
{
|
||||
++nbSkippedFiles;
|
||||
continue;
|
||||
}
|
||||
|
||||
nlinfo( "Processing %s (%u/%u)", (*ilf).c_str(), ilf-filenames.begin(), filenames.size() );
|
||||
CIFile logfile;
|
||||
if ( logfile.open( *ilf, true ) )
|
||||
{
|
||||
_OutputLogReport->setProgress( (uint)(ilf-filenames.begin()), (uint)filenames.size() );
|
||||
while ( ! logfile.eof() )
|
||||
{
|
||||
logfile.getline( line, MAX_LOG_LINE_SIZE );
|
||||
line[MAX_LOG_LINE_SIZE-1] = '\0'; // force valid end of line
|
||||
_OutputLogReport->pushLine( line );
|
||||
++nbLines;
|
||||
|
||||
if ( isStopping() )
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
nlinfo( "%u lines processed", nbLines );
|
||||
if ( nbSkippedFiles != 0 )
|
||||
nlinfo( "%u log files skipped, not matching target %s", nbSkippedFiles, _LogTarget.c_str() );
|
||||
_Complete = true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add a log line to the report tree
|
||||
*/
|
||||
void CLogReport::pushLine( const std::string& line, NLMISC::CLog::TLogType onlyType, bool countOtherTypes )
|
||||
{
|
||||
// Ignore session title
|
||||
if ( (line.size() > 14) && (line.substr( 0, 14 ) == "Log Starting [") )
|
||||
return;
|
||||
|
||||
// Decode standard log line
|
||||
vector<string> lineTokens;
|
||||
explode( line, string(" "), lineTokens );
|
||||
|
||||
if ( lineTokens.size() < LH_NB_FIELDS )
|
||||
return;
|
||||
|
||||
// Filter log type
|
||||
if ( onlyType != CLog::LOG_UNKNOWN )
|
||||
{
|
||||
if ( lineTokens[LHType] != IDisplayer::logTypeToString( onlyType ) )
|
||||
{
|
||||
if ( countOtherTypes )
|
||||
storeLine( lineTokens, true );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Store
|
||||
storeLine( lineTokens, false );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void CLogReportNode::storeLine( const std::vector<std::string>& lineTokens, bool mainCountOnly )
|
||||
{
|
||||
// Get service name from "[machine/]serviceName-serviceId"
|
||||
string service = lineTokens[LHService];
|
||||
string::size_type p = service.find( '/' );
|
||||
if ( p != string::npos )
|
||||
service = service.substr( p+1 );
|
||||
p = service.find( '-' );
|
||||
if ( p != string::npos )
|
||||
service = service.substr( 0, p );
|
||||
|
||||
// Store to appropriate child
|
||||
CLogReportLeaf *child = getChild( service );
|
||||
if ( ! child )
|
||||
child = addChild( service );
|
||||
child->storeLine( lineTokens, mainCountOnly );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void CLogReportLeaf::storeLine( const std::vector<std::string>& lineTokens, bool mainCountOnly )
|
||||
{
|
||||
if ( ! mainCountOnly )
|
||||
{
|
||||
// Build key from "codeFile codeLine"
|
||||
string key = lineTokens[LHCodeFile] + ":" + lineTokens[LHCodeLine];
|
||||
_LogLineInfo[key].addAnOccurence( lineTokens );
|
||||
}
|
||||
++_Counts[lineTokens[LHType]];
|
||||
++_TotalLines;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void CLogLineInfo::addAnOccurence( const std::vector<std::string>& lineTokens )
|
||||
{
|
||||
if ( NbOccurences == 0 )
|
||||
{
|
||||
for ( uint i=LH_NB_FIELDS; i<lineTokens.size(); ++i )
|
||||
{
|
||||
if ( i != LH_NB_FIELDS )
|
||||
SampleLogText += " ";
|
||||
SampleLogText += lineTokens[i];
|
||||
}
|
||||
}
|
||||
++NbOccurences;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
uint CLogReportLeaf::getNbTotalLines( NLMISC::CLog::TLogType logType )
|
||||
{
|
||||
return (logType==NLMISC::CLog::LOG_UNKNOWN) ? _TotalLines : _Counts[NLMISC::IDisplayer::logTypeToString( logType )];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get results for a service
|
||||
*/
|
||||
void CLogReport::reportByService( const std::string& service, NLMISC::CLog *targetLog )
|
||||
{
|
||||
ILogReport *child = getChild( service );
|
||||
if ( child )
|
||||
{
|
||||
child->report( targetLog, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
targetLog->displayNL( "Nothing found for service %s", service.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get results for a service (all distinct lines, sorted by occurence)
|
||||
*/
|
||||
void CLogReportLeaf::report( NLMISC::CLog *targetLog, bool )
|
||||
{
|
||||
// Sort it
|
||||
typedef multimap< uint, pair< string, const CLogLineInfo * >, std::greater<uint> > CSortedByOccurenceLogLineInfoMap;
|
||||
CSortedByOccurenceLogLineInfoMap sortedByOccurence;
|
||||
for ( CLogLineInfoMap::const_iterator it=_LogLineInfo.begin(); it!=_LogLineInfo.end(); ++it )
|
||||
{
|
||||
const string &key = (*it).first;
|
||||
const CLogLineInfo& info = (*it).second;
|
||||
sortedByOccurence.insert( make_pair( info.NbOccurences, make_pair( key, &info ) ) );
|
||||
}
|
||||
|
||||
// Display it
|
||||
for ( CSortedByOccurenceLogLineInfoMap::const_iterator iso=sortedByOccurence.begin(); iso!=sortedByOccurence.end(); ++iso )
|
||||
{
|
||||
const string &key = (*iso).second.first;
|
||||
const CLogLineInfo& info = *((*iso).second.second);
|
||||
targetLog->displayRawNL( "%s %6u %s : %s", _Service.c_str(), info.NbOccurences, key.c_str(), info.SampleLogText.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the number of lines displayed
|
||||
*/
|
||||
uint CLogReportLeaf::reportPart( uint beginIndex, uint maxNbLines, NLMISC::CLog *targetLog )
|
||||
{
|
||||
uint i = 0;
|
||||
CLogLineInfoMap::const_iterator it;
|
||||
for ( it=_LogLineInfo.begin(); it!=_LogLineInfo.end(); ++it )
|
||||
{
|
||||
if ( i >= beginIndex )
|
||||
{
|
||||
if ( i >= maxNbLines )
|
||||
return i - beginIndex;
|
||||
|
||||
const string &key = (*it).first;
|
||||
const CLogLineInfo& info = (*it).second;
|
||||
targetLog->displayRawNL( "%s %6u %s : %s", _Service.c_str(), info.NbOccurences, key.c_str(), info.SampleLogText.c_str() );
|
||||
}
|
||||
++i;
|
||||
}
|
||||
return i - beginIndex;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get summary of results
|
||||
*/
|
||||
void CLogReportNode::report( NLMISC::CLog *targetLog, bool displayDetailsPerService )
|
||||
{
|
||||
uint nb1Sum=0, nb2Sum=0;
|
||||
for ( std::vector<CLogReportLeaf*>::const_iterator it=_Children.begin(); it!=_Children.end(); ++it )
|
||||
{
|
||||
CLogReportLeaf *pt = (*it);
|
||||
|
||||
// Get distinct warnings
|
||||
uint nb1 = pt->getNbDistinctLines();
|
||||
nb1Sum += nb1;
|
||||
|
||||
// Get total warnings, info... but filter out lines with no header
|
||||
uint sumTotalLinesNotUnknown = 0;
|
||||
uint nbTotalLines [CLog::LOG_UNKNOWN];
|
||||
for ( uint i=CLog::LOG_ERROR; i!=CLog::LOG_UNKNOWN; ++i )
|
||||
{
|
||||
nbTotalLines[i] = pt->getNbTotalLines( (CLog::TLogType)i );
|
||||
sumTotalLinesNotUnknown += nbTotalLines[i];
|
||||
}
|
||||
if ( sumTotalLinesNotUnknown != 0 )
|
||||
{
|
||||
targetLog->displayRawNL( "%s: \t%u distinct WRN, %u total WRN, %u INF, %u DBG, %u STT, %u AST, %u ERR, %u TOTAL",
|
||||
pt->service().c_str(), nb1, nbTotalLines[CLog::LOG_WARNING],
|
||||
nbTotalLines[CLog::LOG_INFO], nbTotalLines[CLog::LOG_DEBUG],
|
||||
nbTotalLines[CLog::LOG_STAT], nbTotalLines[CLog::LOG_ASSERT],
|
||||
nbTotalLines[CLog::LOG_ERROR], pt->getNbTotalLines( CLog::LOG_UNKNOWN ) );
|
||||
nb2Sum += nbTotalLines[CLog::LOG_WARNING];
|
||||
}
|
||||
}
|
||||
targetLog->displayRawNL( "=> %u distinct, %u total WRN (%u pages)", nb1Sum, nb2Sum, nb1Sum / NB_LINES_PER_PAGE + 1 );
|
||||
|
||||
if ( displayDetailsPerService )
|
||||
{
|
||||
for ( std::vector<CLogReportLeaf*>::const_iterator it=_Children.begin(); it!=_Children.end(); ++it )
|
||||
{
|
||||
(*it)->report( targetLog, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get partial results (pageNum>=1)
|
||||
*/
|
||||
void CLogReportNode::reportPage( uint pageNum, NLMISC::CLog *targetLog )
|
||||
{
|
||||
if ( _Children.empty() )
|
||||
{
|
||||
targetLog->displayRawNL( "[END OF LOG]" );
|
||||
return;
|
||||
}
|
||||
|
||||
uint beginIndex = pageNum * NB_LINES_PER_PAGE;
|
||||
uint lineCounter = 0, prevLineCounter;
|
||||
for ( std::vector<CLogReportLeaf*>::const_iterator it=_Children.begin(); it!=_Children.end(); ++it )
|
||||
{
|
||||
CLogReportLeaf *pt = (*it);
|
||||
prevLineCounter = lineCounter;
|
||||
lineCounter += pt->getNbDistinctLines();
|
||||
if ( lineCounter >= beginIndex )
|
||||
{
|
||||
uint remainingLines = pageNum - (lineCounter - beginIndex );
|
||||
pt->reportPart( beginIndex - prevLineCounter, remainingLines, targetLog ); //
|
||||
while ( remainingLines != 0 )
|
||||
{
|
||||
++it;
|
||||
if ( it == _Children.end() )
|
||||
{
|
||||
targetLog->displayRawNL( "[END OF LOG]" );
|
||||
return;
|
||||
}
|
||||
pt = (*it);
|
||||
remainingLines -= pt->reportPart( 0, remainingLines, targetLog );
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,291 +0,0 @@
|
||||
// NeLNS - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// 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/>.
|
||||
|
||||
#ifndef NL_LOG_REPORT_H
|
||||
#define NL_LOG_REPORT_H
|
||||
|
||||
#include "nel/misc/types_nl.h"
|
||||
#include "nel/misc/log.h"
|
||||
#include "nel/misc/thread.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
|
||||
class CLogReport;
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
class CMakeLogTask : public NLMISC::IRunnable
|
||||
{
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
CMakeLogTask() : _Stopping(false), _Complete(false), _Thread(NULL), _OutputLogReport(NULL) {}
|
||||
|
||||
/// Destructor
|
||||
~CMakeLogTask();
|
||||
|
||||
/// Start (called in main thread)
|
||||
void start();
|
||||
|
||||
/// Ask for stop and wait until terminated (called in main thread)
|
||||
void terminateTask();
|
||||
|
||||
///
|
||||
bool isRunning() const { return (_Thread != NULL) && (!_Complete); }
|
||||
|
||||
///
|
||||
bool isStopping() const { return _Stopping; }
|
||||
|
||||
///
|
||||
bool isComplete() const { return _Complete; }
|
||||
|
||||
virtual void run();
|
||||
|
||||
/// Set the path of logfile directory.
|
||||
void setLogPath(const std::string & logPath);
|
||||
|
||||
/// Set one or more paths of logfile directory. They will be processed when running the background thread.
|
||||
void setLogPaths(const std::vector<std::string>& logPaths);
|
||||
|
||||
/// Set the path of logfile directory to default value
|
||||
void setLogPathToDefault();
|
||||
|
||||
/// Return the log paths
|
||||
const std::vector<std::string> getLogPaths() const { return _LogPaths; }
|
||||
|
||||
/** Set which files are to be browsed:
|
||||
* v --> log*.log (default if setLogTarget() not called or called with an empty string)
|
||||
* v* --> log*.log + v*_log*.log
|
||||
* v<n> --> v<n>_log*.log
|
||||
* v<n>+ --> v<n>_log*.log + all matching greater <n> + log*.log
|
||||
* v<n>- --> v<n>_log*.log + all matching lower <n>
|
||||
* * --> *.log
|
||||
*/
|
||||
void setLogTarget(const std::string & logTarget) { _LogTarget = logTarget; }
|
||||
|
||||
/// Return the log report (NULL if task not started yet)
|
||||
CLogReport* getLogReport() { return _OutputLogReport; }
|
||||
|
||||
private:
|
||||
|
||||
void pleaseStop() { _Stopping = true; }
|
||||
void clear();
|
||||
|
||||
volatile bool _Stopping;
|
||||
volatile bool _Complete;
|
||||
std::string _LogTarget;
|
||||
std::vector<std::string> _LogPaths;
|
||||
|
||||
NLMISC::IThread *_Thread;
|
||||
|
||||
CLogReport *_OutputLogReport;
|
||||
};
|
||||
|
||||
|
||||
const uint NB_LINES_PER_PAGE = 100;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class CLogLineInfo
|
||||
{
|
||||
public:
|
||||
|
||||
CLogLineInfo() : NbOccurences(0) {}
|
||||
|
||||
void addAnOccurence( const std::vector<std::string>& lineTokens );
|
||||
|
||||
uint NbOccurences;
|
||||
|
||||
std::string SampleLogText;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class ILogReport
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void storeLine( const std::vector<std::string>& lineTokens, bool mainCountOnly ) = 0;
|
||||
|
||||
virtual void report( NLMISC::CLog *targetLog, bool detailed ) = 0;
|
||||
|
||||
virtual uint getNbDistinctLines() const = 0;
|
||||
|
||||
virtual uint getNbTotalLines( NLMISC::CLog::TLogType logType ) = 0;
|
||||
|
||||
virtual ~ILogReport() {}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* For one service (service name, not service instance), store info about log lines
|
||||
*/
|
||||
class CLogReportLeaf : public ILogReport
|
||||
{
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
CLogReportLeaf( const std::string& service ) : _Service(service), _TotalLines(0) {}
|
||||
|
||||
std::string service() { return _Service; }
|
||||
|
||||
virtual uint getNbDistinctLines() const { return (uint)_LogLineInfo.size(); }
|
||||
|
||||
virtual uint getNbTotalLines( NLMISC::CLog::TLogType logType );
|
||||
|
||||
virtual void storeLine( const std::vector<std::string>& lineTokens, bool mainCountOnly );
|
||||
|
||||
virtual void report( NLMISC::CLog *targetLog, bool detailed );
|
||||
|
||||
uint reportPart( uint beginIndex, uint maxNbLines, NLMISC::CLog *targetLog );
|
||||
|
||||
protected:
|
||||
|
||||
typedef std::map< std::string, CLogLineInfo > CLogLineInfoMap;
|
||||
|
||||
std::string _Service;
|
||||
|
||||
CLogLineInfoMap _LogLineInfo;
|
||||
|
||||
std::map< std::string, uint > _Counts; // indexed by log type string
|
||||
|
||||
uint _TotalLines;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Store info about log lines for several services
|
||||
*/
|
||||
class CLogReportNode : public ILogReport
|
||||
{
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
CLogReportNode() {}
|
||||
|
||||
/// Destructor
|
||||
~CLogReportNode() { reset(); }
|
||||
|
||||
/// Clear all
|
||||
void reset()
|
||||
{
|
||||
for ( std::vector<CLogReportLeaf*>::iterator it=_Children.begin(); it!=_Children.end(); ++it )
|
||||
delete (*it);
|
||||
_Children.clear();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
virtual void storeLine( const std::vector<std::string>& lineTokens, bool mainCountOnly=false );
|
||||
|
||||
CLogReportLeaf* getChild( const std::string& service )
|
||||
{
|
||||
for ( std::vector<CLogReportLeaf*>::iterator it=_Children.begin(); it!=_Children.end(); ++it )
|
||||
{
|
||||
if ( (*it)->service() == service )
|
||||
return (*it);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CLogReportLeaf* addChild( const std::string& service )
|
||||
{
|
||||
CLogReportLeaf *child = new CLogReportLeaf( service );
|
||||
_Children.push_back( child );
|
||||
return child;
|
||||
}
|
||||
|
||||
virtual void report( NLMISC::CLog *targetLog, bool displayDetailsPerService );
|
||||
|
||||
virtual uint getNbDistinctLines() const
|
||||
{
|
||||
uint n = 0;
|
||||
for ( std::vector<CLogReportLeaf*>::const_iterator it=_Children.begin(); it!=_Children.end(); ++it )
|
||||
n += (*it)->getNbDistinctLines();
|
||||
return n;
|
||||
}
|
||||
|
||||
virtual uint getNbTotalLines( NLMISC::CLog::TLogType logType=NLMISC::CLog::LOG_UNKNOWN )
|
||||
{
|
||||
uint n = 0;
|
||||
for ( std::vector<CLogReportLeaf*>::const_iterator it=_Children.begin(); it!=_Children.end(); ++it )
|
||||
n += (*it)->getNbTotalLines( logType );
|
||||
return n;
|
||||
}
|
||||
|
||||
void reportPage( uint pageNum, NLMISC::CLog *targetLog );
|
||||
|
||||
private:
|
||||
|
||||
std::vector<CLogReportLeaf*> _Children;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* <Class description>
|
||||
* \author Olivier Cado
|
||||
* \author Nevrax France
|
||||
* \date 2004
|
||||
*/
|
||||
class CLogReport : public CLogReportNode
|
||||
{
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
CLogReport() : _CurrentFile(~0), _TotalFiles(~0) {}
|
||||
|
||||
/// Clear all
|
||||
void reset() { CLogReportNode::reset(); }
|
||||
|
||||
/**
|
||||
* Add a log line to the report tree.
|
||||
* \param onlyType Type of log to study. If LOG_UNKNOWN, study all.
|
||||
* \param countOtherTypes If true and onlyType not LOG_UNKNOWN, count the number of lines of each other type found.
|
||||
*/
|
||||
void pushLine( const std::string& line, NLMISC::CLog::TLogType onlyType=NLMISC::CLog::LOG_WARNING, bool countOtherTypes=true );
|
||||
|
||||
/// Set the current progress
|
||||
void setProgress( uint currentFile, uint totalFiles ) { _CurrentFile = currentFile; _TotalFiles = totalFiles; }
|
||||
|
||||
/// Get the current progress
|
||||
void getProgress( uint& currentFile, uint& totalFiles ) { currentFile = _CurrentFile; totalFiles = _TotalFiles; }
|
||||
|
||||
/// Get results for a service
|
||||
void reportByService( const std::string& service, NLMISC::CLog *targetLog );
|
||||
|
||||
/// Get partial results (pageNum>=1)
|
||||
void reportPage( uint pageNum, NLMISC::CLog *targetLog ) { CLogReportNode::reportPage( pageNum, targetLog ); }
|
||||
|
||||
/// Get summary of results
|
||||
virtual void report( NLMISC::CLog *targetLog, bool displayDetailsPerService=false ) { CLogReportNode::report( targetLog, displayDetailsPerService ); }
|
||||
|
||||
private:
|
||||
|
||||
uint _CurrentFile;
|
||||
uint _TotalFiles;
|
||||
};
|
||||
|
||||
|
||||
#endif // NL_LOG_REPORT_H
|
||||
|
||||
/* End of log_report.h */
|
Loading…
Reference in New Issue