我正在使用一个小应用程序,需要生成一个非常详细的日志。我已经实现了一个简单的单例Logger类,看起来像这样
#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();
}
现在我对Qt和C ++有些新意,也许这都是错的,所以对我来说很容易:)。现在我的性能下降了22%,而非记录,使用这种方法,这是我能够管理的最好的方法。我认为性能打击来自于创建QtConcurrent线程。
我想我的问题是,这是最好的方法,还是有更好的方法来实现这一点,将会看到更接近完全没有记录的性能。我知道无论应用程序的日志记录速度会有多慢,但我尽量尽量减少这种情况。
答案 0 :(得分:3)
由于您愿意忍受异步日志记录,因此您通常希望在单个线程中完成日志记录,并且具有向其提供数据的线程安全队列。有很多线程安全的队列,比如我在previous answer中发布的队列。您(显然)想要使用Qt原语重写它,但我相信它的相似性足以使重写主要更改您使用的名称。
从那里开始,日志变得更加简单。日志记录线程只是从队列中检索数据,将其写入日志并重复。由于日志记录线程是唯一直接触及日志的线程,因此它根本不需要执行任何锁定 - 它本质上是单线程代码,并且所有锁定都在队列中处理。
答案 1 :(得分:1)
有两种方法可以解决这个问题:
最佳做法?
通常,您应该将日志排入队列,该队列由工作线程排队。有许多可能的实现(无锁队列,池队列,等等),但没关系,只需选择一个,你已经获得了非常好的性能。
当然,简单地重用现有框架会好得多。你看过Boost.Log还是Pantheios?
要注意的另一件事是你不应该记录这么多。日志在应用程序中很少见。
assert
自由地获得完整的核心转储,以防出现问题,更有帮助条件记录技巧可能有点违反直觉。我们的想法是使用对象的析构函数来实际记录,并且只有当std::uncaught_exception()
返回true
时才会这样做。当然这意味着这个日志记录应该是无异常的,所以除非你对你的编程技巧非常有信心,否则我建议你预先格式化这个消息。
答案 2 :(得分:0)
我正在使用qDebug,qWarning等进行日志记录。然后,以这种方式将事件消息重定向到日志文件:
...
qInstallMsgHandler(messageHandler);
...
#define LOG_FILE "path/to/file.log"
void messageHandler(QtMsgType type, const char *msg)
{
#define NOW QDateTime::currentDateTime().toString("MM:dd: hh:mm:ss")
QString out;
switch (type) {
case QtDebugMsg: out = QString("%1: %2\n").arg(NOW).arg(msg); break;
case QtWarningMsg: out = QString("%1: warning: %2\n").arg(NOW).arg(msg); break;
case QtCriticalMsg: out = QString("%1: critical: %2\n").arg(NOW).arg(msg); break;
case QtFatalMsg: out = QString("%1: fatal: %2\n").arg(NOW).arg(msg); break;
default: return;
}
QFile file(LOG_FILE);
if (file.open(QIODevice::WriteOnly | QIODevice::Append))
QTextStream(&file) << out;
#undef NOW
}
这种方法是线程安全的,虽然它不是最有效的方法,因为文件IO没有被缓存。