[SOLVED] Logging class operator ()
-
Hi,
I am currently trying to implement a simple QTextStream logging class using the singleton approach for multiple threads. Here's the code I have so far (just for QVariant and QString, a template class might be used in the future). This is in a dll so multiple applications can use it.
LoggerStream.h
@
#ifndef LOGGERSTREAM_H
#define LOGGERSTREAM_H#include "dll_logger_global.h"
#include <QObject>
#include <QTextStream>
#include <QFile>
#include <QMutex>// enum for log levels
enum LogLevels {
LOG_INFO = 0,
LOG_DEBUG1,
LOG_DEBUG2,
LOG_DEBUG3,
LOG_WARNING,
LOG_CRITICAL
};// logger stream class
class LOGGERSHARED_EXPORT LoggerStream : public QObject
{
Q_OBJECTpublic:
static LoggerStream *instance();
LoggerStream& operator () (LogLevels level); // log levels input
LoggerStream& operator << (QVariant input); // QVariant input
LoggerStream& operator << (QString input); // QString input
void setFileName(QString fileName);
QString fileName();
bool open();
bool isOpen();
void commit();private:
LoggerStream(QObject *parent = 0);
LoggerStream& operator = (const LoggerStream&);
~LoggerStream();
static LoggerStream *_instance;
QString logLevelString(LogLevels level);
void streamData(QString text);
};#endif // LOGGERSTREAM_H
@LoggerStream.cpp
@
#include "loggerstream.h"#include <QTime>
#include <QDebug>// globals for file IO
QMutex fileMutex;
QTextStream _fileStream;
QFile _logFile;
QString _fileName;
bool _isOpen;// static instance of class
LoggerStream* LoggerStream::_instance = NULL;LoggerStream::LoggerStream(QObject *parent) :
QObject(parent)
{
_isOpen = false;
_fileStream.setDevice(&_logFile);
}LoggerStream * LoggerStream::instance()
{
// create static mutex and check if the instance exists
static QMutex _instanceMutex;
if (_instance == NULL){
_instanceMutex.lock();
if (_instance == NULL)
_instance = new LoggerStream;
_instanceMutex.unlock();
}
// return the instance of the singleton
return _instance;
}void LoggerStream::setFileName(QString fileName)
{
_fileName = fileName;
}QString LoggerStream::fileName()
{
return _fileName;
}bool LoggerStream::open()
{
// check if it already open
if (_isOpen)
return true;// set attributes and open _logFile.setFileName(_fileName); if (!_logFile.open(QIODevice::WriteOnly | QIODevice::Truncate)){ qDebug() << "failed to open file"; return false; } // open success _isOpen = true; return true;
}
void LoggerStream::commit()
{
// close the stream file
_logFile.close();
_isOpen = false;
}bool LoggerStream::isOpen()
{
return _isOpen;
}LoggerStream& LoggerStream::operator () (LogLevels level)
{
// check if file is open
if (!_isOpen)
return *this;// create date / time stamp QTime time = QTime::currentTime(); QDateTime dateTime = QDateTime::currentDateTime(); // create log string including the log level output QString headerString = dateTime.date().toString() + " " + QVariant(time.hour()).toString() + " " + QVariant(time.minute()).toString() + " " + QVariant(time.second()).toString() + " " + QVariant(time.msec()).toString() + " " + logLevelString(level); // stream the data into the QTextStream QString fileText = QString("[") + headerString + QString("]: "); streamData(fileText); return *this;
}
LoggerStream& LoggerStream::operator << (QVariant input)
{
// check if file is open
if (!_isOpen)
return *this;// stream data streamData(input.toString()); return *this;
}
LoggerStream& LoggerStream::operator << (QString input)
{
// check if file is open
if (!_isOpen)
return *this;// stream data streamData(input); return *this;
}
void LoggerStream::streamData(QString text)
{
// check mutex
fileMutex.lock();
_fileStream << text;
_fileStream.flush();
fileMutex.unlock();
}QString LoggerStream::logLevelString(LogLevels level)
{
switch (level) {
case LOG_WARNING:
return "LOG_WARNING";
case LOG_INFO:
return "LOG_INFO";
case LOG_CRITICAL:
return "LOG_CRITICAL";
case LOG_DEBUG1:
return "LOG_DEBUG1";
case LOG_DEBUG2:
return "LOG_DEBUG2";
case LOG_DEBUG3:
return "LOG_DEBUG3";
default:
return "UNKNOWN";
};
}LoggerStream::~LoggerStream()
{
// commit the file
commit();// delete the instance delete _instance;
}
@So here's an implementation of what I would expect to see in my main.cpp
@
// create logger class
LoggerStream *LOG = LoggerStream::instance();
LOG->setFileName(fileName);
if (!LOG->open()){
QMessageBox err;
err.setWindowTitle("Error");
err.setText("Error opening streaming log file");
err.setInformativeText("Logging will not be enabled");
err.exec();
}
LOG(LOG_DEBUG3) << QVariant("hi");
...
...
// on close
LOG->commit();
@The expected output in the file would be:
[TIME STAMP LOG_DEBUG]: hiI haven't thought about how I am going to implement the endl after the last operator in the sequence. For some reason my () operator override is not being hit while streaming the "hi"...Therefore, my output in the file is just "hi".
My assumption would be that the () operator function with the input would get hit first, then the second operator <<.
Any help would be great!
-
Hi,
Just a quick thinking: try setting the io device of _fileStream after you opened _logFile
-
Thank you very much for the reply SGaist.
I have tried your suggestion, which makes way more sense than putting it in the constructor, but I now am getting a new error.
I am actually getting a compilation error when overriding the () operator for some reason in my main code where I call:
@
LOG(LOG_DEBUG3) << QVariant("hi");
@error: C2064: term does not evaluate to a function taking 1 arguments
I believe there is an issue with the () operator in the class.
-
Because you are using the wrong operator. you should implement the streaming operator.
I would recommend taking a look at qDebug() implementation to give you an idea on how to make this work.
-
Thank you SGaist. I have taken a look at the qDebug() source and I did end up getting it working. The operator was correct, I wasn't dereferencing the pointer in the main (added a macro that does it so the syntax looks the same).
added this to the loggerstream.h
@
// macro for log
#define LOG(LEVEL) if(LoggerStream::instance() != NULL) (*LoggerStream::instance())(LEVEL)
@
Allows LOG(LEVEL) syntaxThank you again for your help. The class is now how I want it!