File indexing completed on 2024-10-06 12:54:05
0001 // SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org> 0002 // SPDX-FileCopyrightText: 2002 Holger Freyther <freyther@kde.org> 0003 // SPDX-FileCopyrightText: 2008 Volker Krause <vkrause@kde.org> 0004 // SPDX-FileCopyrightText: 2023 Tobias Fella <fella@posteo.de> 0005 // SPDX-License-Identifier: LGPL-2.0-or-later 0006 0007 #include "logger.h" 0008 0009 #include <QDateTime> 0010 #include <QDir> 0011 #include <QFileInfo> 0012 #include <QLoggingCategory> 0013 #include <QMutex> 0014 #include <QStandardPaths> 0015 0016 static QLoggingCategory::CategoryFilter oldCategoryFilter = nullptr; 0017 static QtMessageHandler oldHandler = nullptr; 0018 static bool e2eeDebugEnabled = false; 0019 0020 class FileDebugStream : public QIODevice 0021 { 0022 Q_OBJECT 0023 public: 0024 FileDebugStream() 0025 : mType(QtCriticalMsg) 0026 { 0027 open(WriteOnly); 0028 } 0029 0030 bool isSequential() const override 0031 { 0032 return true; 0033 } 0034 qint64 readData(char *, qint64) override 0035 { 0036 return 0; 0037 } 0038 qint64 readLineData(char *, qint64) override 0039 { 0040 return 0; 0041 } 0042 0043 qint64 writeData(const char *data, qint64 len) override 0044 { 0045 if (!mFileName.isEmpty()) { 0046 QFile outputFile(mFileName); 0047 outputFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Unbuffered); 0048 outputFile.write(data, len); 0049 outputFile.putChar('\n'); 0050 outputFile.close(); 0051 } 0052 0053 return len; 0054 } 0055 0056 void setFileName(const QString &fileName) 0057 { 0058 mFileName = fileName; 0059 } 0060 0061 void setType(QtMsgType type) 0062 { 0063 mType = type; 0064 } 0065 0066 private: 0067 QString mFileName; 0068 QtMsgType mType; 0069 }; 0070 0071 class DebugPrivate 0072 { 0073 public: 0074 DebugPrivate() 0075 : origHandler(nullptr) 0076 { 0077 } 0078 0079 ~DebugPrivate() 0080 { 0081 qInstallMessageHandler(origHandler); 0082 file.close(); 0083 } 0084 0085 void log(QtMsgType type, const QMessageLogContext &context, const QString &message) 0086 { 0087 QMutexLocker locker(&mutex); 0088 QByteArray buf; 0089 QTextStream str(&buf); 0090 str << QDateTime::currentDateTime().toString(Qt::ISODate) << " ["; 0091 switch (type) { 0092 case QtDebugMsg: 0093 str << "DEBUG"; 0094 break; 0095 case QtInfoMsg: 0096 str << "INFO "; 0097 break; 0098 case QtWarningMsg: 0099 str << "WARN "; 0100 break; 0101 case QtFatalMsg: 0102 str << "FATAL"; 0103 break; 0104 case QtCriticalMsg: 0105 str << "CRITICAL"; 0106 break; 0107 } 0108 str << "] " << context.category << ": "; 0109 if (context.file && *context.file && context.line) { 0110 str << context.file << ":" << context.line << ": "; 0111 } 0112 if (context.function && *context.function) { 0113 str << context.function << ": "; 0114 } 0115 str << message << "\n"; 0116 str.flush(); 0117 file.write(buf.constData(), buf.size()); 0118 file.flush(); 0119 0120 if (oldHandler && (strcmp(context.category, "quotient.e2ee") != 0 || e2eeDebugEnabled)) { 0121 oldHandler(type, context, message); 0122 } 0123 } 0124 0125 void setName(const QString &appName) 0126 { 0127 name = appName; 0128 0129 if (file.isOpen()) { 0130 file.close(); 0131 } 0132 auto filePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QDir::separator() + appName; 0133 0134 QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QDir::separator()); 0135 auto entryList = dir.entryList({appName + QStringLiteral(".*")}); 0136 std::sort(entryList.begin(), entryList.end(), [](const auto &left, const auto &right) { 0137 auto leftIndex = left.split(".").last().toInt(); 0138 auto rightIndex = right.split(".").last().toInt(); 0139 return leftIndex > rightIndex; 0140 }); 0141 for (const auto &entry : entryList) { 0142 bool ok = false; 0143 const auto index = entry.split(".").last().toInt(&ok); 0144 if (!ok) { 0145 continue; 0146 } 0147 QFileInfo info(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QDir::separator() + entry); 0148 if (info.exists()) { 0149 QFile file(info.absoluteFilePath()); 0150 if (index > 50) { 0151 file.remove(); 0152 continue; 0153 } 0154 const QString newName = filePath + QStringLiteral(".%1").arg(index + 1); 0155 const bool success = file.copy(newName); 0156 if (success) { 0157 file.remove(); 0158 } else { 0159 qFatal("Cannot rename log file '%s' to '%s': %s", 0160 qUtf8Printable(file.fileName()), 0161 qUtf8Printable(newName), 0162 qUtf8Printable(file.errorString())); 0163 } 0164 } 0165 } 0166 0167 QFileInfo finfo(filePath); 0168 if (!finfo.absoluteDir().exists()) { 0169 QDir().mkpath(finfo.absolutePath()); 0170 } 0171 file.setFileName(filePath + QStringLiteral(".0")); 0172 file.open(QIODevice::WriteOnly | QIODevice::Unbuffered); 0173 } 0174 0175 void setOrigHandler(QtMessageHandler origHandler_) 0176 { 0177 origHandler = origHandler_; 0178 } 0179 0180 QMutex mutex; 0181 QFile file; 0182 QString name; 0183 QtMessageHandler origHandler; 0184 QByteArray loggingCategory; 0185 }; 0186 0187 Q_GLOBAL_STATIC(DebugPrivate, sInstance) 0188 0189 void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message) 0190 { 0191 switch (type) { 0192 case QtDebugMsg: 0193 case QtInfoMsg: 0194 case QtWarningMsg: 0195 case QtCriticalMsg: 0196 sInstance()->log(type, context, message); 0197 break; 0198 case QtFatalMsg: 0199 sInstance()->log(QtInfoMsg, context, message); 0200 abort(); 0201 } 0202 } 0203 0204 void filter(QLoggingCategory *category) 0205 { 0206 if (qstrcmp(category->categoryName(), "quotient.e2ee") == 0) { 0207 category->setEnabled(QtDebugMsg, true); 0208 } else if (oldCategoryFilter) { 0209 oldCategoryFilter(category); 0210 } 0211 } 0212 0213 void initLogging() 0214 { 0215 e2eeDebugEnabled = QLoggingCategory("quotient.e2ee", QtDebugMsg).isEnabled(QtDebugMsg); 0216 oldCategoryFilter = QLoggingCategory::installFilter(filter); 0217 oldHandler = qInstallMessageHandler(messageHandler); 0218 sInstance->setOrigHandler(oldHandler); 0219 sInstance->setName("neochat.log"); 0220 } 0221 0222 #include "logger.moc"