Update QsLog to 2.1 snapshot 46b643d5bcbc

This commit is contained in:
Felix Kauselmann
2020-07-24 19:05:01 +02:00
parent c13ec618d0
commit 1568a5f253
45 changed files with 2579 additions and 269 deletions

View File

@ -26,147 +26,152 @@
#include "QsLog.h"
#include "QsLogDest.h"
#ifdef QS_LOG_SEPARATE_THREAD
#include <QThreadPool>
#include <QRunnable>
#include <QThread>
#include <QWaitCondition>
#include <queue>
#endif
#include <QMutex>
#include <QVector>
#include <QDateTime>
#include <QLatin1String>
#include <QtGlobal>
#include <cassert>
#include <cstdlib>
#include <stdexcept>
#include <algorithm>
#include <vector>
namespace QsLogging
{
typedef QVector<DestinationPtr> DestinationList;
static const char TraceString[] = "TRACE";
static const char DebugString[] = "DEBUG";
static const char InfoString[] = "INFO ";
static const char WarnString[] = "WARN ";
static const char ErrorString[] = "ERROR";
static const char FatalString[] = "FATAL";
// not using Qt::ISODate because we need the milliseconds too
static const QString fmtDateTime("yyyy-MM-ddThh:mm:ss.zzz");
static Logger* sInstance = 0;
static const char* LevelToText(Level theLevel)
{
switch (theLevel) {
case TraceLevel:
return TraceString;
case DebugLevel:
return DebugString;
case InfoLevel:
return InfoString;
case WarnLevel:
return WarnString;
case ErrorLevel:
return ErrorString;
case FatalLevel:
return FatalString;
case OffLevel:
return "";
default: {
assert(!"bad log level");
return InfoString;
}
}
}
using DestinationList = std::vector<DestinationPtrU>;
#ifdef QS_LOG_SEPARATE_THREAD
class LogWriterRunnable : public QRunnable
//! Messages can be enqueued from other threads and will be logged one by one.
//! Note: std::queue was used instead of QQueue because it accepts types missing operator=.
class LoggerThread : public QThread
{
public:
LogWriterRunnable(QString message, Level level);
virtual void run();
void enqueue(const LogMessage& message)
{
QMutexLocker locker(&mutex);
messageQueue.push(message);
waitCondition.wakeOne();
}
void requestStop()
{
QMutexLocker locker(&mutex);
requestInterruption();
waitCondition.wakeOne();
}
protected:
virtual void run()
{
while (true) {
QMutexLocker locker(&mutex);
if (messageQueue.empty() && !isInterruptionRequested()) {
waitCondition.wait(&mutex);
}
if (isInterruptionRequested()) {
break;
}
const LogMessage msg = messageQueue.front();
messageQueue.pop();
locker.unlock();
Logger::instance().write(msg);
}
}
private:
QString mMessage;
Level mLevel;
QMutex mutex;
QWaitCondition waitCondition;
std::queue<LogMessage> messageQueue;
};
#endif
class LoggerImpl
{
public:
LoggerImpl();
~LoggerImpl();
#ifdef QS_LOG_SEPARATE_THREAD
QThreadPool threadPool;
bool shutDownLoggerThread();
LoggerThread loggerThread;
#endif
QMutex logMutex;
Level level;
DestinationList destList;
};
#ifdef QS_LOG_SEPARATE_THREAD
LogWriterRunnable::LogWriterRunnable(QString message, Level level)
: QRunnable()
, mMessage(message)
, mLevel(level)
{
}
void LogWriterRunnable::run()
{
Logger::instance().write(mMessage, mLevel);
}
#endif
LoggerImpl::LoggerImpl()
: level(InfoLevel)
{
// assume at least file + console
destList.reserve(2);
destList.reserve(2); // assume at least file + console
#ifdef QS_LOG_SEPARATE_THREAD
threadPool.setMaxThreadCount(1);
threadPool.setExpiryTimeout(-1);
loggerThread.start(QThread::LowPriority);
#endif
}
Logger::Logger()
: d(new LoggerImpl)
LoggerImpl::~LoggerImpl()
{
#ifdef QS_LOG_SEPARATE_THREAD
#if defined(Q_OS_WIN) && defined(QSLOG_IS_SHARED_LIBRARY)
// Waiting on the thread here is too late and can lead to deadlocks. More details:
// * Another reason not to do anything scary in your DllMain:
// https://blogs.msdn.microsoft.com/oldnewthing/20040128-00/?p=40853
// * Dynamic Link libraries best practices:
// https://msdn.microsoft.com/en-us/library/windows/desktop/dn633971%28v=vs.85%29.aspx#general_best_practices
Q_ASSERT(loggerThread.isFinished());
if (!loggerThread.isFinished()) {
qCritical("You must shut down the QsLog thread, otherwise deadlocks can occur!");
}
#endif
shutDownLoggerThread();
#endif
}
#ifdef QS_LOG_SEPARATE_THREAD
bool LoggerImpl::shutDownLoggerThread()
{
if (loggerThread.isFinished()) {
return true;
}
loggerThread.requestStop();
return loggerThread.wait();
}
#endif
Logger& Logger::instance()
{
if (!sInstance)
sInstance = new Logger;
return *sInstance;
}
void Logger::destroyInstance()
{
delete sInstance;
sInstance = 0;
static Logger instance;
return instance;
}
// tries to extract the level from a string log message. If available, conversionSucceeded will
// contain the conversion result.
Level Logger::levelFromLogMessage(const QString& logMessage, bool* conversionSucceeded)
{
using namespace QsLogging;
if (conversionSucceeded)
*conversionSucceeded = true;
if (logMessage.startsWith(QLatin1String(TraceString)))
if (logMessage.startsWith(QLatin1String(LevelName(TraceLevel))))
return TraceLevel;
if (logMessage.startsWith(QLatin1String(DebugString)))
if (logMessage.startsWith(QLatin1String(LevelName(DebugLevel))))
return DebugLevel;
if (logMessage.startsWith(QLatin1String(InfoString)))
if (logMessage.startsWith(QLatin1String(LevelName(InfoLevel))))
return InfoLevel;
if (logMessage.startsWith(QLatin1String(WarnString)))
if (logMessage.startsWith(QLatin1String(LevelName(WarnLevel))))
return WarnLevel;
if (logMessage.startsWith(QLatin1String(ErrorString)))
if (logMessage.startsWith(QLatin1String(LevelName(ErrorLevel))))
return ErrorLevel;
if (logMessage.startsWith(QLatin1String(FatalString)))
if (logMessage.startsWith(QLatin1String(LevelName(FatalLevel))))
return FatalLevel;
if (conversionSucceeded)
@ -174,19 +179,54 @@ Level Logger::levelFromLogMessage(const QString& logMessage, bool* conversionSuc
return OffLevel;
}
Logger::~Logger()
Logger::~Logger() noexcept = default;
#if defined(Q_OS_WIN)
bool Logger::shutDownLoggerThread()
{
#ifdef QS_LOG_SEPARATE_THREAD
d->threadPool.waitForDone();
return d->shutDownLoggerThread();
#else
return true;
#endif
delete d;
d = 0;
}
#endif
void Logger::addDestination(DestinationPtrU&& destination)
{
Q_ASSERT(destination.get());
QMutexLocker lock(&d->logMutex);
d->destList.emplace_back(std::move(destination));
}
void Logger::addDestination(DestinationPtr destination)
DestinationPtrU Logger::removeDestination(const QString &type)
{
assert(destination.data());
d->destList.push_back(destination);
QMutexLocker lock(&d->logMutex);
const auto it = std::find_if(d->destList.begin(), d->destList.end(), [&type](const DestinationPtrU& dest){
return dest->type() == type;
});
if (it != d->destList.end()) {
auto removed = std::move(*it);
d->destList.erase(it);
return removed;
}
return DestinationPtrU();
}
bool Logger::hasDestinationOfType(const char* type) const
{
QMutexLocker lock(&d->logMutex);
const QLatin1String latin1Type(type);
for (const auto& dest : d->destList) {
if (dest->type() == latin1Type) {
return true;
}
}
return false;
}
void Logger::setLoggingLevel(Level newLevel)
@ -199,50 +239,36 @@ Level Logger::loggingLevel() const
return d->level;
}
//! creates the complete log message and passes it to the logger
void Logger::Helper::writeToLog()
Logger::Helper::~Helper() noexcept
{
const char* const levelName = LevelToText(level);
const QString completeMessage(QString("%1 %2 %3")
.arg(levelName)
.arg(QDateTime::currentDateTime().toString(fmtDateTime))
.arg(buffer)
);
Logger::instance().enqueueWrite(completeMessage, level);
const LogMessage msg(buffer, QDateTime::currentDateTimeUtc(), level);
Logger::instance().enqueueWrite(msg);
}
Logger::Helper::~Helper()
Logger::Logger()
: d(new LoggerImpl)
{
try {
writeToLog();
}
catch(std::exception&) {
// you shouldn't throw exceptions from a sink
assert(!"exception in logger helper destructor");
//CHANGED throw;
}
qRegisterMetaType<LogMessage>("QsLogging::LogMessage");
}
//! directs the message to the task queue or writes it directly
void Logger::enqueueWrite(const QString& message, Level level)
void Logger::enqueueWrite(const LogMessage& message)
{
#ifdef QS_LOG_SEPARATE_THREAD
LogWriterRunnable *r = new LogWriterRunnable(message, level);
d->threadPool.start(r);
d->loggerThread.enqueue(message);
#else
write(message, level);
write(message);
#endif
}
//! Sends the message to all the destinations. The level for this message is passed in case
//! it's useful for processing in the destination.
void Logger::write(const QString& message, Level level)
void Logger::write(const LogMessage& message)
{
QMutexLocker lock(&d->logMutex);
for (DestinationList::iterator it = d->destList.begin(),
endIt = d->destList.end();it != endIt;++it) {
(*it)->write(message, level);
for (auto& dest : d->destList) {
dest->write(message);
}
}