I have a small app that I am working with that needs to produce a very verbose log. I have implemented a simple singleton Logger class which looks like this
#ifndef LOGGER_H
#define LOGGER_H
#include <QObject>
#include <QMutex>
#include <QFile>
class Logger : public QObject
{
Q_OBJECT
public:
static Logger* sharedInstance()
{
static QMutex mutex;
if (!m_sharedInstance)
{
mutex.lock();
if(!m_sharedInstance)
m_sharedInstance = new Logger;
mutex.unlock();
}
return m_sharedInstance;
}
static void dropInstance()
{
static QMutex mutex;
mutex.lock();
delete m_sharedInstance;
m_sharedInstance = 0;
mutex.unlock();
}
void setLogFilePathAndOpen(QString path);
void logMessage(QString message);
void logMessageWorker(QString message);
void closeLog();
private:
Logger() {}
Logger(const Logger &);
Logger& operator=(const Logger &);
static Logger *m_sharedInstance;
QFile m_logFile;
signals:
public slots:
};
#endif // LOGGER_H
#include <QDateTime>
#include <QtConcurrentRun>
#include "logger.h"
Logger* Logger::m_sharedInstance = 0;
void Logger::setLogFilePathAndOpen(QString path)
{
m_logFile.setFileName(path);
m_logFile.open(QIODevice::Append | QIODevice::Text);
}
void Logger::logMessage(QString message)
{
//TODO calling QtConcurrent causes about a 22% drop in performance. Look at other ways to do this.
QtConcurrent::run(this, &Logger::logMessageWorker, message);
}
void Logger::logMessageWorker(QString message)
{
QTextStream out(&m_logFile);
out << tr("%1: %2\n").arg(QDateTime::currentDateTime().toString()).arg(message);
}
void Logger::closeLog()
{
m_logFile.close();
}
Now I am somewhat new to Qt and C++ and perhaps this is all wrong so go easy on me :). Now I get about a 22% performance drop, versus not logging, using this method which is about the best I've been able to manage. I imagine the performance hit comes from creating the QtConcurrent thread.
I guess my question is, would this be the best approach or is there a better way to implement this that will see even closer performance to not logging at all. I am aware that no matter what the application will be slower with logging but I am trying to minimise this as much as possible.
Since you're willing to put up with asynchronous logging, you generally want the logging done in a single thread, and have a thread-safe queue feeding data to it. There are quite a few thread-safe queues around, such as one I posted in a previous answer. You'd (apparently) want to rewrite that using Qt primitives, but I believe that's similar enough that the rewrite will mostly be changing the names you use.
From there, the logging becomes quite a bit simpler. The logging thread just retrieves data from the queue, writes it to the log, and repeats. Since the logging thread is the only one that touches the log directly, it doesn't have to do any locking at all -- it's essentially single-threaded code, and all the locking is handled in the the queue.