You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ksystemlog/ksystemlog/src/defaultReader.cpp

499 lines
12 KiB

/***************************************************************************
* 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 <klocale.h>
#include <kmessagebox.h>
#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'...").tqarg(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'.").tqarg(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.").tqarg(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.").tqarg(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()<tmpMaxLines) {
buffers->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()<tmpMaxLines) {
buffers->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()<tmpMaxLines) {
buffers->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()<tmpMaxLines) {
buffers->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"