mirror of
https://github.com/YACReader/yacreader
synced 2025-07-17 20:44:32 -04:00
Update QsLog to 2.1 snapshot 46b643d5bcbc
This commit is contained in:
264
third_party/QsLog/QsLog.cpp
vendored
264
third_party/QsLog/QsLog.cpp
vendored
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user