1 /***************************************************************************
2 * Copyright (C) 2005-2019 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
37 QByteArray msgWithTime(const Logger::LogEntry &msg)
39 return (msg.toString() + "\n").toUtf8();
45 Logger::Logger(QObject *parent)
48 static bool registered = []() {
49 qRegisterMetaType<LogEntry>();
54 connect(this, SIGNAL(messageLogged(Logger::LogEntry)), this, SLOT(onMessageLogged(Logger::LogEntry)));
56 #if QT_VERSION < 0x050000
57 qInstallMsgHandler(Logger::messageHandler);
59 qInstallMessageHandler(Logger::messageHandler);
66 // If we're not initialized yet, output pending messages so they don't get lost
68 for (auto &&message : _messages) {
69 std::cerr << msgWithTime(message).constData();
75 std::vector<Logger::LogEntry> Logger::messages() const
81 bool Logger::setup(bool keepMessages)
83 _keepMessages = keepMessages;
85 // Set maximum level for output (we still store/announce all messages for client-side filtering)
86 if (Quassel::isOptionSet("loglevel")) {
87 QString level = Quassel::optionValue("loglevel").toLower();
89 _outputLevel = LogLevel::Debug;
90 else if (level == "info")
91 _outputLevel = LogLevel::Info;
92 else if (level == "warning")
93 _outputLevel = LogLevel::Warning;
94 else if (level == "error")
95 _outputLevel = LogLevel::Error;
97 qCritical() << qPrintable(tr("Invalid log level %1; supported are Debug|Info|Warning|Error").arg(level));
102 QString logfilename = Quassel::optionValue("logfile");
103 if (!logfilename.isEmpty()) {
104 _logFile.setFileName(logfilename);
105 if (!_logFile.open(QFile::Append|QFile::Unbuffered|QFile::Text)) {
106 qCritical() << qPrintable(tr("Could not open log file \"%1\": %2").arg(logfilename, _logFile.errorString()));
109 if (!_logFile.isOpen()) {
110 if (!_logFile.open(stderr, QFile::WriteOnly|QFile::Unbuffered|QFile::Text)) {
111 qCritical() << qPrintable(tr("Cannot write to stderr: %1").arg(_logFile.errorString()));
116 _syslogEnabled = Quassel::isOptionSet("syslog");
118 Quassel::RunMode mode = Quassel::runMode();
119 Quassel::BuildInfo info = Quassel::buildInfo();
120 QString prgname = info.applicationName;
122 if (mode == Quassel::RunMode::ClientOnly) {
123 prgname = info.clientApplicationName;
124 } else if (mode == Quassel::RunMode::CoreOnly) {
125 prgname = info.coreApplicationName;
128 _prgname = prgname.toLocal8Bit();
130 // set up options, program name, and facility for later calls to syslog(3)
131 if (_syslogEnabled) {
132 openlog(_prgname.constData(), LOG_PID, LOG_USER);
138 // Now that we've setup our logging backends, output pending messages
139 for (auto &&message : _messages) {
140 outputMessage(message);
142 if (!_keepMessages) {
150 #if QT_VERSION < 0x050000
151 void Logger::messageHandler(QtMsgType type, const char *message)
153 void Logger::messageHandler(QtMsgType type, const QMessageLogContext &, const QString &message)
156 Quassel::instance()->logger()->handleMessage(type, message);
160 void Logger::handleMessage(QtMsgType type, const QString &msg)
164 handleMessage(LogLevel::Debug, msg);
166 #if QT_VERSION >= 0x050500
168 handleMessage(LogLevel::Info, msg);
172 handleMessage(LogLevel::Warning, msg);
175 handleMessage(LogLevel::Error, msg);
178 handleMessage(LogLevel::Fatal, msg);
184 void Logger::handleMessage(LogLevel level, const QString &msg)
186 // Use signal connection to make this method thread-safe
187 emit messageLogged({QDateTime::currentDateTime(), level, msg});
191 void Logger::onMessageLogged(const LogEntry &message)
194 _messages.push_back(message);
197 // If setup() wasn't called yet, just store the message - will be output later
199 outputMessage(message);
204 void Logger::outputMessage(const LogEntry &message)
206 if (message.logLevel < _outputLevel) {
211 if (_syslogEnabled) {
213 switch (message.logLevel) {
214 case LogLevel::Debug:
220 case LogLevel::Warning:
223 case LogLevel::Error:
226 case LogLevel::Fatal:
229 syslog(prio, "%s", qPrintable(message.message));
233 if (!_logFile.fileName().isEmpty() || !_syslogEnabled) {
234 _logFile.write(msgWithTime(message));
238 // For fatal messages, write log to dump file
239 if (message.logLevel == LogLevel::Fatal) {
240 QFile dumpFile{Quassel::instance()->coreDumpFileName()};
241 if (dumpFile.open(QIODevice::Append)) {
242 dumpFile.write(msgWithTime(message));
251 QString Logger::LogEntry::toString() const
256 case Logger::LogLevel::Debug:
257 levelString = "[Debug] ";
259 case Logger::LogLevel::Info:
260 levelString = "[Info ] ";
262 case Logger::LogLevel::Warning:
263 levelString = "[Warn ] ";
265 case Logger::LogLevel::Error:
266 levelString = "[Error] ";
268 case Logger::LogLevel::Fatal:
269 levelString = "[FATAL] ";
273 return timeStamp.toString("yyyy-MM-dd hh:mm:ss ") + levelString + message;