[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_OBJECT

    public:
    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]: hi

    I 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!



  • Correction, change [] to () in the operator


  • Lifetime Qt Champion

    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.


  • Lifetime Qt Champion

    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) syntax

    Thank you again for your help. The class is now how I want it!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.