X-Git-Url: https://git.quassel-irc.org/?a=blobdiff_plain;f=src%2Fcommon%2Flogger.cpp;h=809207203adacfb25e9697d0a3746b0cb8500f79;hb=5f771f4a6942248ebe0a5a690f19e509328265cf;hp=26296bb7405f121aa1e75e0d7787ce63dbc2a4d6;hpb=8699dd758516d0ded076811e8ea656adc95e69d0;p=quassel.git diff --git a/src/common/logger.cpp b/src/common/logger.cpp index 26296bb7..80920720 100644 --- a/src/common/logger.cpp +++ b/src/common/logger.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005 by the Quassel Project * + * Copyright (C) 2005-2019 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -15,36 +15,254 @@ * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include + +#ifdef HAVE_SYSLOG +# include +#endif + +#include +#include +#include +#include + #include "logger.h" +#include "quassel.h" -#include +namespace { + +QByteArray msgWithTime(const Logger::LogEntry &msg) +{ + QString levelString; + + switch (msg.logLevel) { + case Logger::LogLevel::Debug: + levelString = "[Debug] "; + break; + case Logger::LogLevel::Info: + levelString = "[Info ] "; + break; + case Logger::LogLevel::Warning: + levelString = "[Warn ] "; + break; + case Logger::LogLevel::Error: + levelString = "[Error] "; + break; + case Logger::LogLevel::Fatal: + levelString = "[FATAL] "; + break; + } + + return (msg.timeStamp.toString("yyyy-MM-dd hh:mm:ss ") + levelString + msg.message + "\n").toUtf8(); +}; + +} + + +Logger::Logger(QObject *parent) + : QObject(parent) +{ + static bool registered = []() { + qRegisterMetaType(); + return true; + }(); + Q_UNUSED(registered) + + connect(this, SIGNAL(messageLogged(Logger::LogEntry)), this, SLOT(onMessageLogged(Logger::LogEntry))); + +#if QT_VERSION < 0x050000 + qInstallMsgHandler(Logger::messageHandler); +#else + qInstallMessageHandler(Logger::messageHandler); +#endif +} + + +Logger::~Logger() +{ + // If we're not initialized yet, output pending messages so they don't get lost + if (!_initialized) { + for (auto &&message : _messages) { + std::cerr << msgWithTime(message).constData(); + } + } +} + + +std::vector Logger::messages() const +{ + return _messages; +} + + +bool Logger::setup(bool keepMessages) +{ + _keepMessages = keepMessages; + // Set maximum level for output (we still store/announce all messages for client-side filtering) + if (Quassel::isOptionSet("loglevel")) { + QString level = Quassel::optionValue("loglevel").toLower(); + if (level == "debug") + _outputLevel = LogLevel::Debug; + else if (level == "info") + _outputLevel = LogLevel::Info; + else if (level == "warning") + _outputLevel = LogLevel::Warning; + else if (level == "error") + _outputLevel = LogLevel::Error; + else { + qCritical() << qPrintable(tr("Invalid log level %1; supported are Debug|Info|Warning|Error").arg(level)); + return false; + } + } + QString logfilename = Quassel::optionValue("logfile"); + if (!logfilename.isEmpty()) { + _logFile.setFileName(logfilename); + if (!_logFile.open(QFile::Append|QFile::Unbuffered|QFile::Text)) { + qCritical() << qPrintable(tr("Could not open log file \"%1\": %2").arg(logfilename, _logFile.errorString())); + } + } + if (!_logFile.isOpen()) { + if (!_logFile.open(stderr, QFile::WriteOnly|QFile::Unbuffered|QFile::Text)) { + qCritical() << qPrintable(tr("Cannot write to stderr: %1").arg(_logFile.errorString())); + } + } -Logger::~Logger() { - //qInstallMsgHandler(0); +#ifdef HAVE_SYSLOG + _syslogEnabled = Quassel::isOptionSet("syslog"); + + Quassel::RunMode mode = Quassel::runMode(); + Quassel::BuildInfo info = Quassel::buildInfo(); + QString prgname = info.applicationName; + + if (mode == Quassel::RunMode::ClientOnly) { + prgname = info.clientApplicationName; + } else if (mode == Quassel::RunMode::CoreOnly) { + prgname = info.coreApplicationName; + } + + _prgname = prgname.toLocal8Bit(); + + // set up options, program name, and facility for later calls to syslog(3) + if (_syslogEnabled) { + openlog(_prgname.constData(), LOG_PID, LOG_USER); + } +#endif + + _initialized = true; + + // Now that we've setup our logging backends, output pending messages + for (auto &&message : _messages) { + outputMessage(message); + } + if (!_keepMessages) { + _messages.clear(); + } + + return true; +} + + +#if QT_VERSION < 0x050000 +void Logger::messageHandler(QtMsgType type, const char *message) +#else +void Logger::messageHandler(QtMsgType type, const QMessageLogContext &, const QString &message) +#endif +{ + Quassel::instance()->logger()->handleMessage(type, message); } -void messageHandler(QtMsgType type, const char *msg) { - switch (type) { + +void Logger::handleMessage(QtMsgType type, const QString &msg) +{ + switch (type) { case QtDebugMsg: - std::cerr << "[DEBUG] " << msg << "\n"; - break; + handleMessage(LogLevel::Debug, msg); + break; +#if QT_VERSION >= 0x050500 + case QtInfoMsg: + handleMessage(LogLevel::Info, msg); + break; +#endif case QtWarningMsg: - std::cerr << "[WARNING] " << msg << "\n"; - break; + handleMessage(LogLevel::Warning, msg); + break; case QtCriticalMsg: - std::cerr << "[CRITICAL] " << msg << "\n"; - break; + handleMessage(LogLevel::Error, msg); + break; case QtFatalMsg: - std::cerr << "[FATAL] " << msg << "\n"; - abort(); // deliberately core dump - } + handleMessage(LogLevel::Fatal, msg); + break; + } } -Logger::Logger() { - //qInstallMsgHandler(messageHandler); + +void Logger::handleMessage(LogLevel level, const QString &msg) +{ + // Use signal connection to make this method thread-safe + emit messageLogged({QDateTime::currentDateTime(), level, msg}); +} + + +void Logger::onMessageLogged(const LogEntry &message) +{ + if (_keepMessages) { + _messages.push_back(message); + } + + // If setup() wasn't called yet, just store the message - will be output later + if (_initialized) { + outputMessage(message); + } +} + + +void Logger::outputMessage(const LogEntry &message) +{ + if (message.logLevel < _outputLevel) { + return; + } + +#ifdef HAVE_SYSLOG + if (_syslogEnabled) { + int prio{LOG_INFO}; + switch (message.logLevel) { + case LogLevel::Debug: + prio = LOG_DEBUG; + break; + case LogLevel::Info: + prio = LOG_INFO; + break; + case LogLevel::Warning: + prio = LOG_WARNING; + break; + case LogLevel::Error: + prio = LOG_ERR; + break; + case LogLevel::Fatal: + prio = LOG_CRIT; + } + syslog(prio, "%s", qPrintable(message.message)); + } +#endif + + if (!_logFile.fileName().isEmpty() || !_syslogEnabled) { + _logFile.write(msgWithTime(message)); + } + +#ifndef Q_OS_MAC + // For fatal messages, write log to dump file + if (message.logLevel == LogLevel::Fatal) { + QFile dumpFile{Quassel::instance()->coreDumpFileName()}; + if (dumpFile.open(QIODevice::Append)) { + dumpFile.write(msgWithTime(message)); + dumpFile.close(); + } + } +#endif + }