File indexing completed on 2025-01-05 04:37:30
0001 /* 0002 SPDX-FileCopyrightText: 2005 Joris Guisson <joris.guisson@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "log.h" 0008 #include <cstdlib> 0009 #include <iostream> 0010 0011 #include <QDateTime> 0012 #include <QDebug> 0013 #include <QFile> 0014 #include <QList> 0015 #include <QMutex> 0016 #include <QTextStream> 0017 0018 #include <KIO/CopyJob> 0019 0020 #include "autorotatelogjob.h" 0021 #include "compressfilejob.h" 0022 #include "error.h" 0023 #include <interfaces/logmonitorinterface.h> 0024 #include <util/fileops.h> 0025 0026 namespace bt 0027 { 0028 const Uint32 MAX_LOG_FILE_SIZE = 10 * 1024 * 1024; // 10 MB 0029 0030 static void QtMessageOutput(QtMsgType type, const QMessageLogContext &ctxt, const QString &msg); 0031 0032 class Log::Private 0033 { 0034 public: 0035 Log *parent; 0036 QTextStream *out; 0037 QFile *fptr; 0038 bool to_cout; 0039 unsigned int filter; 0040 QList<LogMonitorInterface *> monitors; 0041 QString tmp; 0042 QMutex mutex; 0043 AutoRotateLogJob *rotate_job; 0044 0045 public: 0046 Private(Log *parent) 0047 : parent(parent) 0048 , out(nullptr) 0049 , fptr(nullptr) 0050 , to_cout(false) 0051 , filter(0) 0052 , rotate_job(nullptr) 0053 { 0054 } 0055 0056 ~Private() 0057 { 0058 cleanup(); 0059 } 0060 0061 void cleanup() 0062 { 0063 delete out; 0064 out = nullptr; 0065 0066 delete fptr; 0067 fptr = nullptr; 0068 } 0069 0070 void setFilter(unsigned int f) 0071 { 0072 filter = f; 0073 } 0074 0075 void rotateLogs(const QString &file) 0076 { 0077 if (bt::Exists(file + QStringLiteral("-10.gz"))) 0078 bt::Delete(file + QStringLiteral("-10.gz"), true); 0079 0080 // move all log files one up 0081 for (Uint32 i = 10; i > 1; i--) { 0082 QString prev = QStringLiteral("%1-%2.gz").arg(file).arg(i - 1); 0083 QString curr = QStringLiteral("%1-%2.gz").arg(file).arg(i); 0084 if (bt::Exists(prev)) 0085 QFile::rename(prev, curr); 0086 } 0087 0088 // move current log to 1 and zip it 0089 QFile::rename(file, file + QStringLiteral("-1")); 0090 CompressFileJob *gzip = new CompressFileJob(file + QStringLiteral("-1")); 0091 gzip->start(); 0092 } 0093 0094 void setOutputFile(const QString &file, bool rotate, bool handle_qt_messages) 0095 { 0096 QMutexLocker lock(&mutex); 0097 0098 if (handle_qt_messages) 0099 qInstallMessageHandler(QtMessageOutput); 0100 0101 cleanup(); 0102 0103 if (bt::Exists(file) && rotate) 0104 rotateLogs(file); 0105 0106 fptr = new QFile(file); 0107 if (!fptr->open(QIODevice::WriteOnly)) { 0108 QString err = fptr->errorString(); 0109 std::cout << "Failed to open log file " << file.toLocal8Bit().constData() << ": " << err.toLocal8Bit().constData() << std::endl; 0110 cleanup(); 0111 return; 0112 } 0113 0114 out = new QTextStream(fptr); 0115 } 0116 0117 void write(const QString &line) 0118 { 0119 tmp += line; 0120 } 0121 0122 void finishLine() 0123 { 0124 QString final = QDateTime::currentDateTime().toString() + QStringLiteral(": ") + tmp; 0125 0126 // only add stuff when we are not rotating the logs 0127 // this could result in the loss of some messages 0128 if (!rotate_job && fptr != nullptr) { 0129 if (out) 0130 *out << final << Qt::endl; 0131 0132 fptr->flush(); 0133 if (to_cout) 0134 std::cout << final.toLocal8Bit().constData() << std::endl; 0135 ; 0136 } 0137 0138 if (monitors.count() > 0) { 0139 QList<LogMonitorInterface *>::iterator i = monitors.begin(); 0140 while (i != monitors.end()) { 0141 LogMonitorInterface *lmi = *i; 0142 lmi->message(final, filter); 0143 ++i; 0144 } 0145 } 0146 tmp.clear(); 0147 } 0148 0149 void endline() 0150 { 0151 finishLine(); 0152 if (fptr && fptr->size() > MAX_LOG_FILE_SIZE && !rotate_job) { 0153 tmp = QStringLiteral("Log larger then 10 MB, rotating"); 0154 finishLine(); 0155 QString file = fptr->fileName(); 0156 fptr->close(); // close the log file 0157 out->setDevice(nullptr); 0158 rotateLogs(file); 0159 logRotateDone(); 0160 } 0161 } 0162 0163 void logRotateDone() 0164 { 0165 fptr->open(QIODevice::WriteOnly); 0166 out->setDevice(fptr); 0167 rotate_job = nullptr; 0168 } 0169 }; 0170 0171 Log::Log() 0172 { 0173 priv = new Private(this); 0174 } 0175 0176 Log::~Log() 0177 { 0178 qInstallMessageHandler(nullptr); 0179 delete priv; 0180 } 0181 0182 void Log::setOutputFile(const QString &file, bool rotate, bool handle_qt_messages) 0183 { 0184 priv->setOutputFile(file, rotate, handle_qt_messages); 0185 } 0186 0187 void Log::addMonitor(LogMonitorInterface *m) 0188 { 0189 priv->monitors.append(m); 0190 } 0191 0192 void Log::removeMonitor(LogMonitorInterface *m) 0193 { 0194 int index = priv->monitors.indexOf(m); 0195 if (index != -1) 0196 priv->monitors.takeAt(index); 0197 } 0198 0199 void Log::setOutputToConsole(bool on) 0200 { 0201 priv->to_cout = on; 0202 } 0203 0204 void Log::logRotateDone() 0205 { 0206 priv->logRotateDone(); 0207 } 0208 0209 Log &endl(Log &lg) 0210 { 0211 lg.priv->endline(); 0212 lg.priv->mutex.unlock(); // unlock after end of line 0213 return lg; 0214 } 0215 0216 Log &Log::operator<<(const QUrl &url) 0217 { 0218 return operator<<(url.toString()); 0219 } 0220 0221 Log &Log::operator<<(const QString &s) 0222 { 0223 priv->write(s); 0224 return *this; 0225 } 0226 0227 Log &Log::operator<<(const char *s) 0228 { 0229 priv->write(QString::fromUtf8(s)); 0230 return *this; 0231 } 0232 0233 Log &Log::operator<<(Uint64 v) 0234 { 0235 return operator<<(QString::number(v)); 0236 } 0237 0238 Log &Log::operator<<(Int64 v) 0239 { 0240 return operator<<(QString::number(v)); 0241 } 0242 0243 void Log::setFilter(unsigned int filter) 0244 { 0245 priv->setFilter(filter); 0246 } 0247 0248 void Log::lock() 0249 { 0250 priv->mutex.lock(); 0251 } 0252 0253 Q_GLOBAL_STATIC(Log, global_log) 0254 0255 Log &Out(unsigned int arg) 0256 { 0257 global_log->setFilter(arg); 0258 global_log->lock(); 0259 return *global_log; 0260 } 0261 0262 void InitLog(const QString &file, bool rotate, bool handle_qt_messages, bool to_stdout) 0263 { 0264 global_log->setOutputFile(file, rotate, handle_qt_messages); 0265 global_log->setOutputToConsole(to_stdout); 0266 } 0267 0268 void AddLogMonitor(LogMonitorInterface *m) 0269 { 0270 global_log->addMonitor(m); 0271 } 0272 0273 void RemoveLogMonitor(LogMonitorInterface *m) 0274 { 0275 global_log->removeMonitor(m); 0276 } 0277 0278 static void QtMessageOutput(QtMsgType type, const QMessageLogContext &, const QString &msg) 0279 { 0280 switch (type) { 0281 case QtDebugMsg: 0282 Out(SYS_GEN | LOG_DEBUG) << "Qt Debug: " << msg << endl; 0283 break; 0284 case QtWarningMsg: 0285 case QtInfoMsg: 0286 Out(SYS_GEN | LOG_NOTICE) << "Qt Warning: " << msg << endl; 0287 fprintf(stderr, "Warning: %s\n", msg.toUtf8().constData()); 0288 break; 0289 case QtCriticalMsg: 0290 Out(SYS_GEN | LOG_IMPORTANT) << "Qt Critical: " << msg << endl; 0291 fprintf(stderr, "Critical: %s\n", msg.toUtf8().constData()); 0292 break; 0293 case QtFatalMsg: 0294 Out(SYS_GEN | LOG_IMPORTANT) << "Qt Fatal: " << msg << endl; 0295 fprintf(stderr, "Fatal: %s\n", msg.toUtf8().constData()); 0296 abort(); 0297 break; 0298 } 0299 } 0300 }