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.
499 lines
12 KiB
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"
|