2012-12-05 19:45:28 +04:00
|
|
|
/*
|
|
|
|
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* for more details.
|
|
|
|
*/
|
|
|
|
|
2014-07-11 02:31:24 +04:00
|
|
|
#include "logger.h"
|
2012-12-05 19:45:28 +04:00
|
|
|
|
2018-04-10 14:07:20 +03:00
|
|
|
#include "config.h"
|
|
|
|
|
2013-10-02 17:28:33 +04:00
|
|
|
#include <QDir>
|
|
|
|
#include <QStringList>
|
2014-10-11 18:58:47 +04:00
|
|
|
#include <QThread>
|
2018-10-31 15:21:41 +03:00
|
|
|
#include <QtGlobal>
|
2015-10-20 14:22:48 +03:00
|
|
|
#include <qmetaobject.h>
|
2013-10-02 17:28:33 +04:00
|
|
|
|
2018-04-10 13:20:33 +03:00
|
|
|
#include <zlib.h>
|
|
|
|
|
2020-07-01 16:12:25 +03:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
#include <io.h> // for stdout
|
|
|
|
#endif
|
|
|
|
|
2014-11-10 00:34:07 +03:00
|
|
|
namespace OCC {
|
2012-12-05 19:45:28 +04:00
|
|
|
|
2020-05-12 18:33:13 +03:00
|
|
|
QtMessageHandler s_originalMessageHandler = nullptr;
|
|
|
|
|
2015-02-14 16:21:37 +03:00
|
|
|
static void mirallLogCatcher(QtMsgType type, const QMessageLogContext &ctx, const QString &message)
|
|
|
|
{
|
2015-10-20 14:22:48 +03:00
|
|
|
auto logger = Logger::instance();
|
2020-05-12 18:33:13 +03:00
|
|
|
if (type == QtDebugMsg && !logger->logDebug()) {
|
|
|
|
if (s_originalMessageHandler) {
|
|
|
|
s_originalMessageHandler(type, ctx, message);
|
|
|
|
}
|
|
|
|
} else if (!logger->isNoop()) {
|
2015-10-20 14:22:48 +03:00
|
|
|
logger->doLog(qFormatLogMessage(type, ctx, message));
|
|
|
|
}
|
2018-10-31 15:21:41 +03:00
|
|
|
|
|
|
|
#if defined(Q_OS_WIN)
|
|
|
|
// Make application terminate in a way that can be caught by the crash reporter
|
|
|
|
if(type == QtFatalMsg) {
|
|
|
|
Utility::crash();
|
|
|
|
}
|
|
|
|
#endif
|
2015-02-14 16:21:37 +03:00
|
|
|
}
|
2013-10-02 17:28:33 +04:00
|
|
|
|
2016-12-08 19:30:41 +03:00
|
|
|
|
2014-05-26 15:01:29 +04:00
|
|
|
Logger *Logger::instance()
|
2012-12-05 19:45:28 +04:00
|
|
|
{
|
2014-05-26 15:01:29 +04:00
|
|
|
static Logger log;
|
|
|
|
return &log;
|
2012-12-05 19:45:28 +04:00
|
|
|
}
|
|
|
|
|
2014-05-26 15:01:29 +04:00
|
|
|
Logger::Logger(QObject *parent)
|
|
|
|
: QObject(parent)
|
2012-12-05 19:45:28 +04:00
|
|
|
{
|
2018-03-26 22:44:46 +03:00
|
|
|
qSetMessagePattern("[%{function} \t%{message}");
|
2017-03-28 16:56:07 +03:00
|
|
|
#ifndef NO_MSG_HANDLER
|
2020-05-12 18:33:13 +03:00
|
|
|
s_originalMessageHandler = qInstallMessageHandler(mirallLogCatcher);
|
2015-02-19 18:49:39 +03:00
|
|
|
#else
|
|
|
|
Q_UNUSED(mirallLogCatcher)
|
|
|
|
#endif
|
2012-12-05 19:45:28 +04:00
|
|
|
}
|
|
|
|
|
2014-05-26 15:01:29 +04:00
|
|
|
Logger::~Logger()
|
|
|
|
{
|
2015-02-19 18:49:39 +03:00
|
|
|
#ifndef NO_MSG_HANDLER
|
2020-05-18 21:39:16 +03:00
|
|
|
qInstallMessageHandler(nullptr);
|
2015-02-19 18:49:39 +03:00
|
|
|
#endif
|
2012-12-05 19:45:28 +04:00
|
|
|
}
|
|
|
|
|
2014-05-26 15:01:29 +04:00
|
|
|
|
2013-08-07 16:58:56 +04:00
|
|
|
void Logger::postGuiLog(const QString &title, const QString &message)
|
|
|
|
{
|
|
|
|
emit guiLog(title, message);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Logger::postOptionalGuiLog(const QString &title, const QString &message)
|
|
|
|
{
|
|
|
|
emit optionalGuiLog(title, message);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Logger::postGuiMessage(const QString &title, const QString &message)
|
|
|
|
{
|
2013-10-02 17:55:15 +04:00
|
|
|
emit guiMessage(title, message);
|
2013-08-07 16:58:56 +04:00
|
|
|
}
|
|
|
|
|
2012-12-05 19:45:28 +04:00
|
|
|
void Logger::log(Log log)
|
|
|
|
{
|
|
|
|
QString msg;
|
|
|
|
if (_showTime) {
|
|
|
|
msg = log.timeStamp.toString(QLatin1String("MM-dd hh:mm:ss:zzz")) + QLatin1Char(' ');
|
|
|
|
}
|
|
|
|
|
2014-10-11 18:58:47 +04:00
|
|
|
msg += QString().sprintf("%p ", (void *)QThread::currentThread());
|
2012-12-05 19:45:28 +04:00
|
|
|
msg += log.message;
|
|
|
|
// _logs.append(log);
|
|
|
|
// std::cout << qPrintable(log.message) << std::endl;
|
2013-10-02 17:28:33 +04:00
|
|
|
|
2015-02-14 16:21:37 +03:00
|
|
|
doLog(msg);
|
|
|
|
}
|
|
|
|
|
2015-10-20 14:22:48 +03:00
|
|
|
/**
|
|
|
|
* Returns true if doLog does nothing and need not to be called
|
|
|
|
*/
|
|
|
|
bool Logger::isNoop() const
|
|
|
|
{
|
2018-04-10 14:07:20 +03:00
|
|
|
QMutexLocker lock(&_mutex);
|
2016-12-08 19:30:41 +03:00
|
|
|
return !_logstream && !_logWindowActivated;
|
2015-10-20 14:22:48 +03:00
|
|
|
}
|
|
|
|
|
2018-04-10 14:07:20 +03:00
|
|
|
bool Logger::isLoggingToFile() const
|
|
|
|
{
|
|
|
|
QMutexLocker lock(&_mutex);
|
|
|
|
return _logstream;
|
|
|
|
}
|
2015-10-20 14:22:48 +03:00
|
|
|
|
2015-02-14 16:21:37 +03:00
|
|
|
void Logger::doLog(const QString &msg)
|
|
|
|
{
|
2013-10-03 16:21:45 +04:00
|
|
|
{
|
|
|
|
QMutexLocker lock(&_mutex);
|
|
|
|
if (_logstream) {
|
|
|
|
(*_logstream) << msg << endl;
|
|
|
|
if (_doFileFlush)
|
|
|
|
_logstream->flush();
|
|
|
|
}
|
2013-10-02 17:28:33 +04:00
|
|
|
}
|
2016-12-08 19:30:41 +03:00
|
|
|
emit logWindowLog(msg);
|
2012-12-05 19:45:28 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Logger::mirallLog(const QString &message)
|
|
|
|
{
|
|
|
|
Log log_;
|
2017-09-25 12:49:11 +03:00
|
|
|
log_.timeStamp = QDateTime::currentDateTimeUtc();
|
2012-12-05 19:45:28 +04:00
|
|
|
log_.message = message;
|
|
|
|
|
|
|
|
Logger::instance()->log(log_);
|
|
|
|
}
|
|
|
|
|
2016-12-08 19:30:41 +03:00
|
|
|
void Logger::setLogWindowActivated(bool activated)
|
|
|
|
{
|
|
|
|
QMutexLocker locker(&_mutex);
|
|
|
|
_logWindowActivated = activated;
|
|
|
|
}
|
|
|
|
|
2013-10-02 17:28:33 +04:00
|
|
|
void Logger::setLogFile(const QString &name)
|
|
|
|
{
|
2013-10-03 16:21:45 +04:00
|
|
|
QMutexLocker locker(&_mutex);
|
2013-10-02 17:28:33 +04:00
|
|
|
if (_logstream) {
|
2018-11-12 20:46:39 +03:00
|
|
|
_logstream.reset(nullptr);
|
2013-10-02 17:28:33 +04:00
|
|
|
_logFile.close();
|
|
|
|
}
|
|
|
|
|
2013-10-02 17:55:15 +04:00
|
|
|
if (name.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-02 17:28:33 +04:00
|
|
|
bool openSucceeded = false;
|
|
|
|
if (name == QLatin1String("-")) {
|
2018-09-10 13:06:25 +03:00
|
|
|
openSucceeded = _logFile.open(stdout, QIODevice::WriteOnly);
|
2013-10-02 17:28:33 +04:00
|
|
|
} else {
|
|
|
|
_logFile.setFileName(name);
|
|
|
|
openSucceeded = _logFile.open(QIODevice::WriteOnly);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!openSucceeded) {
|
2013-10-03 16:21:45 +04:00
|
|
|
locker.unlock(); // Just in case postGuiMessage has a qDebug()
|
2013-10-02 17:28:33 +04:00
|
|
|
postGuiMessage(tr("Error"),
|
|
|
|
QString(tr("<nobr>File '%1'<br/>cannot be opened for writing.<br/><br/>"
|
|
|
|
"The log output can <b>not</b> be saved!</nobr>"))
|
|
|
|
.arg(name));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_logstream.reset(new QTextStream(&_logFile));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Logger::setLogExpire(int expire)
|
|
|
|
{
|
|
|
|
_logExpire = expire;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Logger::setLogDir(const QString &dir)
|
|
|
|
{
|
|
|
|
_logDirectory = dir;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Logger::setLogFlush(bool flush)
|
|
|
|
{
|
|
|
|
_doFileFlush = flush;
|
|
|
|
}
|
|
|
|
|
2017-05-09 18:04:04 +03:00
|
|
|
void Logger::setLogDebug(bool debug)
|
|
|
|
{
|
2018-01-21 12:43:27 +03:00
|
|
|
QLoggingCategory::setFilterRules(debug ? QStringLiteral("sync.*.debug=true\ngui.*.debug=true") : QString());
|
2017-05-09 18:04:04 +03:00
|
|
|
_logDebug = debug;
|
|
|
|
}
|
|
|
|
|
2018-04-10 14:07:20 +03:00
|
|
|
QString Logger::temporaryFolderLogDirPath() const
|
|
|
|
{
|
|
|
|
QString dirName = APPLICATION_SHORTNAME + QString("-logdir");
|
|
|
|
return QDir::temp().filePath(dirName);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Logger::setupTemporaryFolderLogDir()
|
|
|
|
{
|
|
|
|
auto dir = temporaryFolderLogDirPath();
|
|
|
|
if (!QDir().mkpath(dir))
|
|
|
|
return;
|
|
|
|
setLogDebug(true);
|
|
|
|
setLogExpire(4 /*hours*/);
|
|
|
|
setLogDir(dir);
|
|
|
|
_temporaryFolderLogDir = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Logger::disableTemporaryFolderLogDir()
|
|
|
|
{
|
|
|
|
if (!_temporaryFolderLogDir)
|
|
|
|
return;
|
|
|
|
|
|
|
|
enterNextLogFile();
|
|
|
|
setLogDir(QString());
|
|
|
|
setLogDebug(false);
|
|
|
|
setLogFile(QString());
|
|
|
|
_temporaryFolderLogDir = false;
|
|
|
|
}
|
|
|
|
|
2018-04-10 13:20:33 +03:00
|
|
|
static bool compressLog(const QString &originalName, const QString &targetName)
|
|
|
|
{
|
|
|
|
QFile original(originalName);
|
|
|
|
if (!original.open(QIODevice::ReadOnly))
|
|
|
|
return false;
|
|
|
|
auto compressed = gzopen(targetName.toUtf8(), "wb");
|
|
|
|
if (!compressed) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (!original.atEnd()) {
|
|
|
|
auto data = original.read(1024 * 1024);
|
|
|
|
auto written = gzwrite(compressed, data.data(), data.size());
|
|
|
|
if (written != data.size()) {
|
|
|
|
gzclose(compressed);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
gzclose(compressed);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-10-02 17:28:33 +04:00
|
|
|
void Logger::enterNextLogFile()
|
|
|
|
{
|
|
|
|
if (!_logDirectory.isEmpty()) {
|
2018-04-10 13:20:33 +03:00
|
|
|
|
2013-10-02 17:28:33 +04:00
|
|
|
QDir dir(_logDirectory);
|
|
|
|
if (!dir.exists()) {
|
|
|
|
dir.mkpath(".");
|
|
|
|
}
|
|
|
|
|
2018-04-10 14:07:20 +03:00
|
|
|
// Tentative new log name, will be adjusted if one like this already exists
|
|
|
|
QDateTime now = QDateTime::currentDateTime();
|
|
|
|
QString newLogName = now.toString("yyyyMMdd_HHmm") + "_owncloud.log";
|
|
|
|
|
|
|
|
// Expire old log files and deal with conflicts
|
2018-04-10 13:20:33 +03:00
|
|
|
QStringList files = dir.entryList(QStringList("*owncloud.log.*"),
|
2013-10-02 17:28:33 +04:00
|
|
|
QDir::Files);
|
2018-04-10 13:20:33 +03:00
|
|
|
QRegExp rx(R"(.*owncloud\.log\.(\d+).*)");
|
2018-04-10 14:07:20 +03:00
|
|
|
int maxNumber = -1;
|
2013-10-02 17:28:33 +04:00
|
|
|
foreach (const QString &s, files) {
|
2018-04-10 14:07:20 +03:00
|
|
|
if (_logExpire > 0) {
|
|
|
|
QFileInfo fileInfo(dir.absoluteFilePath(s));
|
|
|
|
if (fileInfo.lastModified().addSecs(60 * 60 * _logExpire) < now) {
|
|
|
|
dir.remove(s);
|
2013-10-02 17:28:33 +04:00
|
|
|
}
|
|
|
|
}
|
2018-04-10 14:07:20 +03:00
|
|
|
if (s.startsWith(newLogName) && rx.exactMatch(s)) {
|
|
|
|
maxNumber = qMax(maxNumber, rx.cap(1).toInt());
|
|
|
|
}
|
2013-10-02 17:28:33 +04:00
|
|
|
}
|
2018-04-10 14:07:20 +03:00
|
|
|
newLogName.append("." + QString::number(maxNumber + 1));
|
2013-10-02 17:28:33 +04:00
|
|
|
|
2018-04-10 13:20:33 +03:00
|
|
|
auto previousLog = _logFile.fileName();
|
2018-04-10 14:07:20 +03:00
|
|
|
setLogFile(dir.filePath(newLogName));
|
2018-04-10 13:20:33 +03:00
|
|
|
|
|
|
|
if (!previousLog.isEmpty()) {
|
|
|
|
QString compressedName = previousLog + ".gz";
|
|
|
|
if (compressLog(previousLog, compressedName)) {
|
|
|
|
QFile::remove(previousLog);
|
|
|
|
} else {
|
|
|
|
QFile::remove(compressedName);
|
|
|
|
}
|
|
|
|
}
|
2013-10-02 17:28:33 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-10 00:34:07 +03:00
|
|
|
} // namespace OCC
|