File indexing completed on 2024-11-10 04:41:03
0001 /* 0002 SPDX-FileCopyrightText: 2008 Volker Krause <vkrause@kde.org> 0003 0004 Inspired by kdelibs/kdecore/io/kdebug.h 0005 SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org> 0006 SPDX-FileCopyrightText: 2002 Holger Freyther <freyther@kde.org> 0007 0008 SPDX-License-Identifier: LGPL-2.0-or-later 0009 */ 0010 0011 #include "akdebug.h" 0012 0013 #include "private/standarddirs_p.h" 0014 0015 #include <QDateTime> 0016 #include <QDir> 0017 #include <QFile> 0018 #include <QFileInfo> 0019 #include <QLoggingCategory> 0020 #include <QMutex> 0021 0022 #include <KCrash> 0023 0024 #include <cassert> 0025 0026 class FileDebugStream : public QIODevice 0027 { 0028 Q_OBJECT 0029 public: 0030 FileDebugStream() 0031 : mType(QtCriticalMsg) 0032 { 0033 open(WriteOnly); 0034 } 0035 0036 bool isSequential() const override 0037 { 0038 return true; 0039 } 0040 qint64 readData(char * /*data*/, qint64 /*maxlen*/) override 0041 { 0042 return 0; 0043 } 0044 qint64 readLineData(char * /*data*/, qint64 /*maxlen*/) override 0045 { 0046 return 0; 0047 } 0048 0049 qint64 writeData(const char *data, qint64 len) override 0050 { 0051 if (!mFileName.isEmpty()) { 0052 QFile outputFile(mFileName); 0053 outputFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Unbuffered); 0054 outputFile.write(data, len); 0055 outputFile.putChar('\n'); 0056 outputFile.close(); 0057 } 0058 0059 return len; 0060 } 0061 0062 void setFileName(const QString &fileName) 0063 { 0064 mFileName = fileName; 0065 } 0066 0067 void setType(QtMsgType type) 0068 { 0069 mType = type; 0070 } 0071 0072 private: 0073 QString mFileName; 0074 QtMsgType mType; 0075 }; 0076 0077 class DebugPrivate 0078 { 0079 public: 0080 DebugPrivate() 0081 : origHandler(nullptr) 0082 { 0083 } 0084 0085 ~DebugPrivate() 0086 { 0087 qInstallMessageHandler(origHandler); 0088 file.close(); 0089 } 0090 0091 static QString errorLogFileName(const QString &name) 0092 { 0093 return Akonadi::StandardDirs::saveDir("data") + QDir::separator() + name + QLatin1StringView(".error"); 0094 } 0095 0096 QString errorLogFileName() const 0097 { 0098 return errorLogFileName(name); 0099 } 0100 0101 void log(QtMsgType type, const QMessageLogContext &context, const QString &msg) 0102 { 0103 QMutexLocker locker(&mutex); 0104 #ifdef QT_NO_DEBUG_OUTPUT 0105 if (type == QtDebugMsg) { 0106 return; 0107 } 0108 #endif 0109 QByteArray buf; 0110 QTextStream str(&buf); 0111 str << QDateTime::currentDateTime().toString(Qt::ISODate) << " ["; 0112 switch (type) { 0113 case QtDebugMsg: 0114 str << "DEBUG"; 0115 break; 0116 case QtInfoMsg: 0117 str << "INFO "; 0118 break; 0119 case QtWarningMsg: 0120 str << "WARN "; 0121 break; 0122 case QtFatalMsg: 0123 str << "FATAL"; 0124 break; 0125 case QtCriticalMsg: 0126 str << "CRITICAL"; 0127 break; 0128 } 0129 str << "] " << context.category << ": "; 0130 if (context.file && *context.file && context.line) { 0131 str << context.file << ":" << context.line << ": "; 0132 } 0133 if (context.function && *context.function) { 0134 str << context.function << ": "; 0135 } 0136 str << msg << "\n"; 0137 str.flush(); 0138 file.write(buf.constData(), buf.size()); 0139 file.flush(); 0140 0141 if (origHandler) { 0142 origHandler(type, context, msg); 0143 } 0144 } 0145 0146 void setName(const QString &appName) 0147 { 0148 name = appName; 0149 0150 if (file.isOpen()) { 0151 file.close(); 0152 } 0153 QFileInfo finfo(errorLogFileName()); 0154 if (!finfo.absoluteDir().exists()) { 0155 QDir().mkpath(finfo.absolutePath()); 0156 } 0157 file.setFileName(errorLogFileName()); 0158 file.open(QIODevice::WriteOnly | QIODevice::Unbuffered); 0159 } 0160 0161 void setOrigHandler(QtMessageHandler origHandler_) 0162 { 0163 origHandler = origHandler_; 0164 } 0165 0166 void setOrigCategoryFilter(QLoggingCategory::CategoryFilter origFilter_) 0167 { 0168 origFilter = origFilter_; 0169 } 0170 0171 QMutex mutex; 0172 QFile file; 0173 QString name; 0174 QtMessageHandler origHandler; 0175 QLoggingCategory::CategoryFilter origFilter; 0176 QByteArray loggingCategory; 0177 }; 0178 0179 Q_GLOBAL_STATIC(DebugPrivate, sInstance) // NOLINT(readability-redundant-member-init) 0180 0181 void akMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) 0182 { 0183 switch (type) { 0184 case QtDebugMsg: 0185 case QtInfoMsg: 0186 case QtWarningMsg: 0187 case QtCriticalMsg: 0188 sInstance()->log(type, context, msg); 0189 break; 0190 case QtFatalMsg: 0191 sInstance()->log(QtInfoMsg, context, msg); 0192 abort(); 0193 } 0194 } 0195 0196 void akCategoryFilter(QLoggingCategory *category) 0197 { 0198 if ((qstrcmp(category->categoryName(), sInstance()->loggingCategory.constData()) == 0) 0199 || (qstrcmp(category->categoryName(), "org.kde.pim.akonadiprivate") == 0)) { 0200 category->setEnabled(QtDebugMsg, true); 0201 category->setEnabled(QtInfoMsg, true); 0202 category->setEnabled(QtWarningMsg, true); 0203 category->setEnabled(QtCriticalMsg, true); 0204 category->setEnabled(QtFatalMsg, true); 0205 } else if (sInstance()->origFilter) { 0206 sInstance()->origFilter(category); 0207 } 0208 } 0209 0210 void akInit(const QString &appName) 0211 { 0212 KCrash::initialize(); 0213 0214 const QString name = QFileInfo(appName).fileName(); 0215 const auto errorLogFile = DebugPrivate::errorLogFileName(name); 0216 QFileInfo infoOld(errorLogFile + QLatin1StringView(".old")); 0217 if (infoOld.exists()) { 0218 QFile fileOld(infoOld.absoluteFilePath()); 0219 const bool success = fileOld.remove(); 0220 if (!success) { 0221 qFatal("Cannot remove old log file '%s': %s", qUtf8Printable(fileOld.fileName()), qUtf8Printable(fileOld.errorString())); 0222 } 0223 } 0224 0225 QFileInfo info(errorLogFile); 0226 if (info.exists()) { 0227 QFile file(info.absoluteFilePath()); 0228 const QString oldName = errorLogFile + QLatin1StringView(".old"); 0229 const bool success = file.copy(oldName); 0230 if (!success) { 0231 qFatal("Cannot rename log file '%s' to '%s': %s", qUtf8Printable(file.fileName()), qUtf8Printable(oldName), qUtf8Printable(file.errorString())); 0232 } 0233 } 0234 0235 QtMessageHandler origHandler = qInstallMessageHandler(akMessageHandler); 0236 sInstance()->setName(name); 0237 sInstance()->setOrigHandler(origHandler); 0238 } 0239 0240 void akMakeVerbose(const QByteArray &category) 0241 { 0242 sInstance()->loggingCategory = category; 0243 QLoggingCategory::CategoryFilter oldFilter = QLoggingCategory::installFilter(akCategoryFilter); 0244 sInstance()->setOrigCategoryFilter(oldFilter); 0245 } 0246 0247 #include "akdebug.moc"