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 }