Merge pull request #5285 from jherico/homer

Move log file writing to a separate thread
This commit is contained in:
Brad Hefta-Gaub 2015-07-09 12:22:13 -07:00
commit 489a8b4b3d
6 changed files with 129 additions and 25 deletions

View file

@ -24,7 +24,7 @@ public:
inline bool extraDebugging() { return _extraDebugging; }
inline void setExtraDebugging(bool debugging) { _extraDebugging = debugging; }
virtual void addMessage(QString) = 0;
virtual void addMessage(const QString&) = 0;
virtual QString getLogData() = 0;
virtual void locateLog() = 0;
@ -32,7 +32,7 @@ signals:
void logReceived(QString message);
private:
bool _extraDebugging;
bool _extraDebugging{ false };
};
#endif // hifi_AbstractLoggerInterface_h

View file

@ -21,11 +21,34 @@ const QString FILENAME_FORMAT = "hifi-log_%1_%2.txt";
const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss";
const QString LOGS_DIRECTORY = "Logs";
class FilePersistThread : public GenericQueueThread < QString > {
public:
FilePersistThread(const FileLogger& logger) : _logger(logger) {
setObjectName("LogFileWriter");
}
protected:
virtual bool processQueueItems(const Queue& messages) {
QFile file(_logger._fileName);
if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
QTextStream out(&file);
foreach(const QString& message, messages) {
out << message;
}
}
return true;
}
private:
const FileLogger& _logger;
};
static FilePersistThread* _persistThreadInstance;
FileLogger::FileLogger(QObject* parent) :
AbstractLoggerInterface(parent),
_logData("")
AbstractLoggerInterface(parent)
{
setExtraDebugging(false);
_persistThreadInstance = new FilePersistThread(*this);
_persistThreadInstance->initialize(true, QThread::LowestPriority);
_fileName = FileUtils::standardPath(LOGS_DIRECTORY);
QHostAddress clientAddress = getLocalAddress();
@ -33,18 +56,24 @@ FileLogger::FileLogger(QObject* parent) :
_fileName.append(QString(FILENAME_FORMAT).arg(clientAddress.toString(), now.toString(DATETIME_FORMAT)));
}
void FileLogger::addMessage(QString message) {
QMutexLocker locker(&_mutex);
emit logReceived(message);
_logData += message;
FileLogger::~FileLogger() {
_persistThreadInstance->terminate();
}
QFile file(_fileName);
if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
QTextStream out(&file);
out << message;
}
void FileLogger::addMessage(const QString& message) {
_persistThreadInstance->queueItem(message);
emit logReceived(message);
}
void FileLogger::locateLog() {
FileUtils::locateFile(_fileName);
}
QString FileLogger::getLogData() {
QString result;
QFile f(_fileName);
if (f.open(QFile::ReadOnly | QFile::Text)) {
result = QTextStream(&f).readAll();
}
return result;
}

View file

@ -13,23 +13,24 @@
#define hifi_FileLogger_h
#include "AbstractLoggerInterface.h"
#include <QMutex>
#include <GenericQueueThread.h>
class FileLogger : public AbstractLoggerInterface {
Q_OBJECT
public:
FileLogger(QObject* parent = NULL);
virtual ~FileLogger();
virtual void addMessage(QString);
virtual QString getLogData() { return _logData; }
virtual void locateLog();
virtual void addMessage(const QString&) override;
virtual QString getLogData() override;
virtual void locateLog() override;
private:
QString _logData;
QString _fileName;
QMutex _mutex;
friend class FilePersistThread;
};
#endif // hifi_FileLogger_h

View file

@ -0,0 +1,72 @@
//
// Created by Bradley Austin Davis 2015/07/08.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_GenericQueueThread_h
#define hifi_GenericQueueThread_h
#include <stdint.h>
#include <QQueue>
#include <QMutex>
#include <QWaitCondition>
#include "GenericThread.h"
#include "NumericalConstants.h"
template <typename T>
class GenericQueueThread : public GenericThread {
public:
using Queue = QQueue<T>;
GenericQueueThread(QObject* parent = nullptr)
: GenericThread(parent) {}
virtual ~GenericQueueThread() {}
void queueItem(const T& t) {
lock();
queueItemInternal(t);
unlock();
_hasItems.wakeAll();
}
protected:
virtual void queueItemInternal(const T& t) {
_items.push_back(t);
}
virtual uint32_t getMaxWait() {
return MSECS_PER_SECOND;
}
virtual bool process() {
if (!_items.size()) {
_hasItemsMutex.lock();
_hasItems.wait(&_hasItemsMutex, getMaxWait());
_hasItemsMutex.unlock();
}
if (!_items.size()) {
return isStillRunning();
}
Queue processItems;
lock();
processItems.swap(_items);
unlock();
return processQueueItems(processItems);
}
virtual bool processQueueItems(const Queue& items) = 0;
Queue _items;
QWaitCondition _hasItems;
QMutex _hasItemsMutex;
};
#endif // hifi_GenericQueueThread_h

View file

@ -14,7 +14,8 @@
#include "GenericThread.h"
GenericThread::GenericThread() :
GenericThread::GenericThread(QObject* parent) :
QObject(parent),
_stopThread(false),
_isThreaded(false) // assume non-threaded, must call initialize()
{
@ -27,13 +28,14 @@ GenericThread::~GenericThread() {
}
}
void GenericThread::initialize(bool isThreaded) {
void GenericThread::initialize(bool isThreaded, QThread::Priority priority) {
_isThreaded = isThreaded;
if (_isThreaded) {
_thread = new QThread(this);
// match the thread name to our object name
_thread->setObjectName(objectName());
_thread->setPriority(priority);
// when the worker thread is started, call our engine's run..
connect(_thread, SIGNAL(started()), this, SLOT(threadRoutine()));

View file

@ -23,12 +23,12 @@
class GenericThread : public QObject {
Q_OBJECT
public:
GenericThread();
GenericThread(QObject* parent = nullptr);
virtual ~GenericThread();
/// Call to start the thread.
/// \param bool isThreaded true by default. false for non-threaded mode and caller must call threadRoutine() regularly.
void initialize(bool isThreaded = true);
void initialize(bool isThreaded = true, QThread::Priority priority = QThread::NormalPriority);
/// Call to stop the thread
void terminate();