+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();
+}
+
+} // namespace
+
+Logger::Logger(QObject* parent)
+ : QObject(parent)
+{
+ static bool registered = []() {
+ qRegisterMetaType<LogEntry>();
+ return true;
+ }();
+ Q_UNUSED(registered)
+
+ connect(this, &Logger::messageLogged, this, &Logger::onMessageLogged);
+
+ qInstallMessageHandler(Logger::messageHandler);
+}
+
+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::LogEntry> Logger::messages() const
+{
+ return _messages;
+}
+
+void 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 {
+ throw ExitException{EXIT_FAILURE, qPrintable(tr("Invalid log level %1; supported are Debug|Info|Warning|Error").arg(level))};
+ }
+ }
+
+ 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()));
+ }
+ }
+
+#ifdef HAVE_SYSLOG
+ _syslogEnabled = Quassel::isOptionSet("syslog");
+
+ // set up options, program name, and facility for later calls to syslog(3)
+ if (_syslogEnabled)
+ openlog("quasselcore", 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();
+ }
+}
+
+void Logger::messageHandler(QtMsgType type, const QMessageLogContext&, const QString& message)
+{
+ Quassel::instance()->logger()->handleMessage(type, message);
+}
+
+void Logger::handleMessage(QtMsgType type, const QString& msg)
+{
+ switch (type) {
+ case QtDebugMsg:
+ handleMessage(LogLevel::Debug, msg);
+ break;
+ case QtInfoMsg:
+ handleMessage(LogLevel::Info, msg);
+ break;
+ case QtWarningMsg:
+ handleMessage(LogLevel::Warning, msg);
+ break;
+ case QtCriticalMsg:
+ handleMessage(LogLevel::Error, msg);
+ break;
+ case QtFatalMsg:
+ handleMessage(LogLevel::Fatal, msg);
+ break;
+ }
+}