/*************************************************************************** * Copyright (C) 2005 by Nicolas Ternisien * * nicolas.ternisien@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include #include #include "logListItem.h" #include "ksystemlogConfig.h" #include "logLine.h" #include "view.h" #include "parsingHelper.h" #include "defaultReader.h" DefaultReader::DefaultReader(TQObject *parent, const char *name) : Reader(parent, name), buffers(NULL) { } DefaultReader::~DefaultReader() { delete buffers; } void DefaultReader::readLog() { //Each file in the Log file list are watched with the following method watchLogFiles(); if (buffers!=NULL) delete buffers; if (logManager->getGroupBy()==NO_GROUP_BY) buffers=new LogLineList(); else { buffers=new LogLineTree(logManager->getColumns(), logManager->getGroupBy(), logManager->getGroupByColumn()); } LogFiles& files=logManager->getLogFiles(); //Inform connected TQObject that the reading has begun emit readingBegin(); //Read each file of the list, and place their lines in the buffer int i=files.count()-1; LogFile* logFile; while(i>=0) { logFile=files[i]; kdDebug() << "Reading file " << logFile->url.path() << endl; this->readFile(logFile); i--; } //Synchronize the buffer once we have fill it logManager->synchronize(buffers); //Says to the buffer that we have read all files, and that //the next adding will come from logChanged method if (!buffers->isEmpty()) buffers->setFirstReadPerformed(true); //Inform connected TQObject that the reading is now finished emit readingEnd(); //Emit a signal which informs connected slots that there are new lines emit logUpdated(logManager->getView()->getLogList()->childCount()); } void DefaultReader::readFile(LogFile* logFile) { TQString message=i18n("Opening file '%1'...").arg(logFile->url.path()); emit statusbarChanged(message); //Inform connected TQObject that we are now reading the "index" file emit readingFile(logManager->getLogFiles().count() - logManager->getLogFiles().findIndex(logFile)); //We initialize these values from configuration to be used by the insert methods tmpDeleteDuplicate=KSystemLogConfig::deleteDuplicatedLines(); tmpDeleteProcessId=KSystemLogConfig::deleteProcessIdentifier(); tmpMaxLines=KSystemLogConfig::maxLines(); tmpMaxCharacters=KSystemLogConfig::maxReadCharacters(); //Open the file TQFile* file=this->openFile(logFile->url.path()); //If an error occurs, end this method if (file==NULL) { return; } TQString buffer; TQString filePath=logFile->url.path(); //Insert the content of the file in a string list TQStringList* rawBuffer=getRawBuffer(file); //If there is no line if (rawBuffer->size()==0) { TQString message=i18n("No log line in '%1'.").arg(logFile->url.path()); emit statusbarChanged(message); delete rawBuffer; return; } kdDebug() << "Testing each line..." << endl; TQStringList::iterator it; it=rawBuffer->end(); it--; //Calculate how many lines we will read int size=rawBuffer->size(); int stop; if (size>tmpMaxLines) stop=size-tmpMaxLines; else stop=0; //Test each line of the raw buffer int i=size-1; int progress=0; int progressTotal=size-1 - stop; int each=progressTotal / 100; if (each==0) each=progressTotal; while(i>=stop) { buffer=*it; if (insertOrReplaceLine(buffer, logFile)==false) break; it--; i--; if (i>=0 && i%each==0) { emit openingProgressed(progress++); } } delete rawBuffer; logFile->lastFileSize=file->size(); // Close the file this->closeFile(file); message=i18n("Log file '%1' loaded successfully.").arg(logFile->url.path()); emit statusbarChanged(message); } void DefaultReader::logChanged(LogFile* logFile) { kdDebug() << "SortedReader : File has been modified !" << endl; //We initialize these values from configuration to be used by the insert methods tmpDeleteDuplicate=KSystemLogConfig::deleteDuplicatedLines(); tmpDeleteProcessId=KSystemLogConfig::deleteProcessIdentifier(); tmpMaxLines=KSystemLogConfig::maxLines(); tmpMaxCharacters=KSystemLogConfig::maxReadCharacters(); TQString buffer; TQString filePath=logFile->url.path(); TQFile* file=this->openFile(filePath); if (file==NULL) return; //If there are new lines in the file, insert only them if (logFile->lastFileSize <= (int) file->size()) { //Place the cursor to the last line opened file->at(logFile->lastFileSize); kdDebug() << "Retrieving a part of the file..." << endl; //Get the maximum number of line read from LogManager int maxLines=KSystemLogConfig::maxLines(); TQString buffer; TQStringList* rawBuffer=getRawBuffer(file); //If there is no line if (rawBuffer->size()==0) { delete rawBuffer; return; } kdDebug() << "Testing each line..." << endl; TQStringList::iterator it; it=rawBuffer->end(); it--; //Calculate how many lines we will read int size=rawBuffer->size(); int stop=0; if (size>maxLines) stop=size-maxLines; //Test each line of the raw buffer int i=size-1; while(i>=stop) { buffer=*it; if (insertOrReplaceLine(buffer, logFile)==false) break; it--; i--; } kdDebug() << "Total read lines : " << (size-1-i) << endl; delete rawBuffer; } //Else reread all lines, clear log list else { buffers->clear(); file->close(); this->readFile(logFile); } int newLineCount=buffers->getNewLineCount(); logManager->synchronize(buffers); //Get the size file for the next calculation logFile->lastFileSize=file->size(); // Close the file this->closeFile(file); TQString message; message=i18n("Log file '%1' has changed.").arg(logFile->url.path()); emit statusbarChanged(message); //Emit a signal which informs connected slots that there are new lines emit logUpdated(newLineCount); } bool DefaultReader::insertLine(TQString& buffer, LogFile* originalFile) { LogLine* line=this->parseMessage(buffer, originalFile); //Do not insert the line if it NULL //TODO Maybe return true (because we don't want to stop the reading for one dirty line) if (line==NULL) return(false); //If the Delete Duplicated Line is checked, we first test that the line is really new if (tmpDeleteDuplicate==true) { if (buffers->lineAlreadyExists(line)==true) return(true); } //If the line is newer, it can be inserted if (buffers->isNewer(line)==true) { if (buffers->getItemCount()insert(line); } else { return(false); } } //If the line is older, then inserts it only if there is still space in the buffer else if (buffers->getItemCount()insert(line); } return(true); } bool DefaultReader::insertOrReplaceLine(TQString& buffer, LogFile* originalFile) { LogLine* line=this->parseMessage(buffer, originalFile); //Do not insert the line if it NULL //TODO Maybe return true (because we don't want to stop the reading for one dirty line) if (line==NULL) return(false); //If the Delete Duplicated Line is checked, we first test that the line is really new if (tmpDeleteDuplicate==true) { if (buffers->lineAlreadyExists(line)==true) { return(true); } } //If the line is newer, it can be inserted if (buffers->isNewer(line)==true) { if (buffers->getItemCount()insert(line); return(true); } else { buffers->removeOldestLine(); buffers->insert(line); return(true); } } //If the line is older, then inserts it only if there is still space in the buffer else if (buffers->getItemCount()insert(line); return(true); } return(false); } /** * TODO Improve speed of this method (with KRegExp class for example) */ LogLine* DefaultReader::parseMessage(TQString& logLine, LogFile* originalFile) { //kdDebug() << "Please don't use parseMessage() from SortedReader class" << endl; int year=TQDate::currentDate().year(); //Month number TQString month; month=logLine.left(3); logLine=logLine.remove(0, 4); int monthNum=ParsingHelper::parseMonthNumber(month); //Day number TQString day; day=logLine.left(2); int dayNum=day.toInt(); logLine=logLine.remove(0, 3); TQDate date(year, monthNum, dayNum); if (!date.isValid()) { kdDebug() << "Malformed date" << endl; date=TQDate::currentDate(); } //Time TQString stringTime; stringTime=logLine.left(8); int h=stringTime.left(2).toInt(); stringTime.remove(0, 3); int m=stringTime.left(2).toInt(); stringTime.remove(0, 3); int s=stringTime.left(2).toInt(); stringTime.remove(0, 3); TQTime time(h, m, s); if (!time.isValid()) { kdDebug() << "Malformed time" << endl; time=TQTime::currentTime(); } //TQStringList logLine=logLine.remove(0, 9); int nextSpace; nextSpace=logLine.find(' '); //Host name TQString hostname; hostname=logLine.left(nextSpace); logLine=logLine.remove(0, nextSpace+1); TQString process; TQString message; //Process name nextSpace=logLine.find(':'); if (nextSpace!=-1) { process=logLine.left(nextSpace); //If the delete process identifier option is enabled if (tmpDeleteProcessId==true) { int squareBracket=process.find('['); //If we find a bracket, we remove the useless part if (squareBracket!=-1) { process=process.left(squareBracket); } } logLine=logLine.remove(0, nextSpace+1); message=logLine.remove(0, 1); } //If we can't find any ':' character, it means that this line is a //internal message of syslogd else { process=i18n("none"); message=logLine; } TQStringList list; list.append(hostname); list.append(process); list.append(message); /* *TODO Try to avoid that 2 columns with the same timestamps (but not the *same appearance order are not sorted correctly */ /* LogLine* lastLine=buffers->lastLineInserted(); if (lastLine!=NULL) { if (lastLine->getTime().date()==date && lastLine->getTime().time()==time) time=time.addMSecs(1); } */ TQString filePath=originalFile->url.path(); LogLine* logLineObject=new LogLine(date, time, list, filePath, originalFile->level, Globals::noMode->id); return(logLineObject); } TQStringList* DefaultReader::getRawBuffer(TQFile* file) { kdDebug() << "Retrieving the raw buffer..." << endl; TQString buffer; TQString tmpBuffer; TQStringList* rawBuffer=new TQStringList(); int res; //Insert the content of the file in a string list while(!file->atEnd()) { //Read the first MaxCharactersRead characters res=file->readLine(buffer, tmpMaxCharacters); //Ignore the rest of the line while(res==tmpMaxCharacters) file->readLine(tmpBuffer, tmpMaxCharacters); //Push the new buffer to the list rawBuffer->push_back(buffer); } kdDebug() << "Raw buffer retrieved." << endl; return(rawBuffer); } #include "defaultReader.moc"