diff --git a/CMakeLists.txt b/CMakeLists.txt index cefb796e8..67a420314 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,9 @@ find_package(PkgConfig REQUIRED) find_package(Boost REQUIRED) find_package(Threads) find_package(Backtrace QUIET) +if(Backtrace_FOUND) + set(HAVE_BACKTRACE ON) +endif() find_package(GnuTLS) find_package(Protobuf REQUIRED) find_library(PROTOBUF_STATIC_LIBRARY libprotobuf.a libprotobuf) diff --git a/ext/libstrawberry-common/CMakeLists.txt b/ext/libstrawberry-common/CMakeLists.txt index a459bfbb8..16be27356 100644 --- a/ext/libstrawberry-common/CMakeLists.txt +++ b/ext/libstrawberry-common/CMakeLists.txt @@ -1,14 +1,3 @@ -if (Backtrace_FOUND) - include_directories(${Backtrace_INCLUDE_DIRS}) -endif (Backtrace_FOUND) - -include_directories(${PROTOBUF_INCLUDE_DIRS}) -include_directories(${CMAKE_CURRENT_BINARY_DIR}) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -include_directories(${CMAKE_SOURCE_DIR}/src) - -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/backtrace_inc.h.in ${CMAKE_CURRENT_BINARY_DIR}/backtrace_inc.h) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Wextra -Wpedantic -Woverloaded-virtual -fpermissive") @@ -17,6 +6,19 @@ if(APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") endif() +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +include_directories(${CMAKE_SOURCE_DIR}) +include_directories(${CMAKE_SOURCE_DIR}/src) +include_directories(${CMAKE_BINARY_DIR}/src) + +if (Backtrace_FOUND) + include_directories(${Backtrace_INCLUDE_DIRS}) +endif(Backtrace_FOUND) + +include_directories(${PROTOBUF_INCLUDE_DIRS}) + set(SOURCES core/closure.cpp core/logging.cpp @@ -49,7 +51,6 @@ if (Backtrace_FOUND) endif (Backtrace_FOUND) target_link_libraries(libstrawberry-common - #${PROTOBUF_LIBRARY} ${TAGLIB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) diff --git a/ext/libstrawberry-common/backtrace_inc.h.in b/ext/libstrawberry-common/backtrace_inc.h.in deleted file mode 100644 index 80e4f4af5..000000000 --- a/ext/libstrawberry-common/backtrace_inc.h.in +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Strawberry Music Player - * Copyright 2013, Jonas Kvinge - * - * Strawberry 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 3 of the License, or - * (at your option) any later version. - * - * Strawberry 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. - * - * You should have received a copy of the GNU General Public License - * along with Strawberry. If not, see . - * - */ - -#cmakedefine01 Backtrace_FOUND - -#if Backtrace_FOUND -# define HAVE_BACKTRACE 1 -# include <${Backtrace_HEADER}> -#endif diff --git a/ext/libstrawberry-common/core/logging.cpp b/ext/libstrawberry-common/core/logging.cpp index e5a410943..0bb7cc863 100644 --- a/ext/libstrawberry-common/core/logging.cpp +++ b/ext/libstrawberry-common/core/logging.cpp @@ -14,9 +14,21 @@ limitations under the License. */ -#include "backtrace_inc.h" - #include + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_BACKTRACE +# include +#endif + #include #include #include @@ -25,15 +37,10 @@ #include #include #include +#include #include #include - -#include -#include -#include -#include -#include -#include +#include #include "logging.h" @@ -50,28 +57,69 @@ static const char *kMessageHandlerMagic = "__logging_message__"; static const int kMessageHandlerMagicLength = strlen(kMessageHandlerMagic); static QtMessageHandler sOriginalMessageHandler = nullptr; -void GLog(const char *domain, int level, const char *message, void *user_data) { +template +static T CreateLogger(Level level, const QString& class_name, int line, const char* category); - Q_UNUSED(domain); - Q_UNUSED(user_data); +void GLog(const char *domain, int level, const char *message, void*) { switch (level) { case G_LOG_FLAG_RECURSION: case G_LOG_FLAG_FATAL: case G_LOG_LEVEL_ERROR: - case G_LOG_LEVEL_CRITICAL: qLog(Error) << message; break; - case G_LOG_LEVEL_WARNING: qLog(Warning) << message; break; + case G_LOG_LEVEL_CRITICAL: + qLogCat(Error, domain) << message; + break; + case G_LOG_LEVEL_WARNING: + qLogCat(Warning, domain) << message; + break; case G_LOG_LEVEL_MESSAGE: - case G_LOG_LEVEL_INFO: qLog(Info) << message; break; + case G_LOG_LEVEL_INFO: + qLogCat(Info, domain) << message; + break; case G_LOG_LEVEL_DEBUG: - default: qLog(Debug) << message; break; + default: + qLogCat(Debug, domain) << message; + break; } } -static void MessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message) { +template +class DebugBase : public QDebug { + public: + DebugBase() : QDebug(sNullDevice) {} + DebugBase(QtMsgType t) : QDebug(t) {} + T& space() { return static_cast(QDebug::space()); } + T& noSpace() { return static_cast(QDebug::nospace()); } +}; - Q_UNUSED(context); +// Debug message will be stored in a buffer. +class BufferedDebug : public DebugBase { + public: + BufferedDebug() : DebugBase() {} + BufferedDebug(QtMsgType) : DebugBase(), buf_(new QBuffer, later_deleter) { + buf_->open(QIODevice::WriteOnly); + + // QDebug doesn't have a method to set a new io device, but swap() allows the devices to be swapped between two instances. + QDebug other(buf_.get()); + swap(other); + } + + // Delete function for the buffer. Since a base class is holding a reference to the raw pointer, + // it shouldn't be deleted until after the deletion of this object is complete. + static void later_deleter(QBuffer* b) { b->deleteLater(); } + + std::shared_ptr buf_; +}; + +// Debug message will be logged immediately. +class LoggedDebug : public DebugBase { + public: + LoggedDebug() : DebugBase() {} + LoggedDebug(QtMsgType t) : DebugBase(t) { nospace() << kMessageHandlerMagic; } +}; + +static void MessageHandler(QtMsgType type, const QMessageLogContext&, const QString &message) { if (strncmp(kMessageHandlerMagic, message.toLocal8Bit().data(), kMessageHandlerMagicLength) == 0) { fprintf(stderr, "%s\n", message.toLocal8Bit().data() + kMessageHandlerMagicLength); @@ -81,14 +129,25 @@ static void MessageHandler(QtMsgType type, const QMessageLogContext &context, co Level level = Level_Debug; switch (type) { case QtFatalMsg: - case QtCriticalMsg: level = Level_Error; break; - case QtWarningMsg: level = Level_Warning; break; + case QtCriticalMsg: + level = Level_Error; + break; + case QtWarningMsg: + level = Level_Warning; + break; case QtDebugMsg: - default: level = Level_Debug; break; + default: + level = Level_Debug; + break; } - for (const QString &line : message.split('\n')) { - CreateLogger(level, "unknown", -1) << line.toLocal8Bit().constData(); + for (const QString& line : message.split('\n')) { + BufferedDebug d = CreateLogger(level, "unknown", -1, nullptr); + d << line.toLocal8Bit().constData(); + if (d.buf_) { + d.buf_->close(); + fprintf(stderr, "%s\n", d.buf_->buffer().data()); + } } if (type == QtFatalMsg) { @@ -145,7 +204,7 @@ void SetLevels(const QString &levels) { } -QString ParsePrettyFunction(const char *pretty_function) { +static QString ParsePrettyFunction(const char *pretty_function) { // Get the class name out of the function name. QString class_name = pretty_function; @@ -168,7 +227,8 @@ QString ParsePrettyFunction(const char *pretty_function) { return class_name; } -QDebug CreateLogger(Level level, const QString &class_name, int line) { +template +static T CreateLogger(Level level, const QString &class_name, int line, const char* category) { // Map the level to a string const char *level_name = nullptr; @@ -180,32 +240,33 @@ QDebug CreateLogger(Level level, const QString &class_name, int line) { case Level_Fatal: level_name = " FATAL "; break; } + QString filter_category = (category != nullptr) ? category : class_name; // Check the settings to see if we're meant to show or hide this message. Level threshold_level = sDefaultLevel; - if (sClassLevels && sClassLevels->contains(class_name)) { - threshold_level = sClassLevels->value(class_name); + if (sClassLevels && sClassLevels->contains(filter_category)) { + threshold_level = sClassLevels->value(filter_category); } if (level > threshold_level) { - return QDebug(sNullDevice); + return T(); } QString function_line = class_name; if (line != -1) { function_line += ":" + QString::number(line); } + if (category) { + function_line += "(" + QString(category) + ")"; + } QtMsgType type = QtDebugMsg; if (level == Level_Fatal) { type = QtFatalMsg; } - QDebug ret(type); - ret.nospace() << kMessageHandlerMagic - << QDateTime::currentDateTime() - .toString("hh:mm:ss.zzz") - .toLatin1() - .constData() << level_name + T ret(type); + ret.nospace() << QDateTime::currentDateTime().toString("hh:mm:ss.zzz").toLatin1().constData() + << level_name << function_line.leftJustified(32).toLatin1().constData(); return ret.space(); @@ -268,21 +329,26 @@ void DumpStackTrace() { #endif } -QDebug CreateLoggerFatal(int line, const char *class_name) { return qCreateLogger(line, class_name, Fatal); } -QDebug CreateLoggerError(int line, const char *class_name) { return qCreateLogger(line, class_name, Error); } +// These are the functions that create loggers for the rest of Clementine. +// It's okay that the LoggedDebug instance is copied to a QDebug in these. It +// doesn't override any behavior that should be needed after return. +#define qCreateLogger(line, pretty_function, category, level) logging::CreateLogger(logging::Level_##level, logging::ParsePrettyFunction(pretty_function), line, category) + +QDebug CreateLoggerFatal(int line, const char *pretty_function, const char* category) { return qCreateLogger(line, pretty_function, category, Fatal); } +QDebug CreateLoggerError(int line, const char *pretty_function, const char* category) { return qCreateLogger(line, pretty_function, category, Error); } #ifdef QT_NO_WARNING_OUTPUT -QNoDebug CreateLoggerWarning(int, const char*) { return QNoDebug(); } + QNoDebug CreateLoggerWarning(int, const char*, const char*) { return QNoDebug(); } #else -QDebug CreateLoggerWarning(int line, const char *class_name) { return qCreateLogger(line, class_name, Warning); } + QDebug CreateLoggerWarning(int line, const char *pretty_function, const char* category) { return qCreateLogger(line, pretty_function, category, Warning); } #endif // QT_NO_WARNING_OUTPUT #ifdef QT_NO_DEBUG_OUTPUT -QNoDebug CreateLoggerInfo(int, const char*) { return QNoDebug(); } -QNoDebug CreateLoggerDebug(int, const char*) { return QNoDebug(); } + QNoDebug CreateLoggerInfo(int, const char*, const char*) { return QNoDebug(); } + QNoDebug CreateLoggerDebug(int, const char*, const char*) { return QNoDebug(); } #else -QDebug CreateLoggerInfo(int line, const char *class_name) { return qCreateLogger(line, class_name, Info); } -QDebug CreateLoggerDebug(int line, const char *class_name) { return qCreateLogger(line, class_name, Debug); } + QDebug CreateLoggerInfo(int line, const char *pretty_function, const char* category) { return qCreateLogger(line, pretty_function, category, Info); } + QDebug CreateLoggerDebug(int line, const char *pretty_function, const char* category) { return qCreateLogger(line, pretty_function, category, Debug); } #endif // QT_NO_DEBUG_OUTPUT } // namespace logging diff --git a/ext/libstrawberry-common/core/logging.h b/ext/libstrawberry-common/core/logging.h index fb0d07370..ee71609bc 100644 --- a/ext/libstrawberry-common/core/logging.h +++ b/ext/libstrawberry-common/core/logging.h @@ -26,14 +26,20 @@ #ifdef QT_NO_DEBUG_STREAM # define qLog(level) while (false) QNoDebug() +# define qLogCat(level, category) while (false) QNoDebug() #else -#define qLog(level) logging::CreateLogger##level(__LINE__, __PRETTY_FUNCTION__) +# define qLog(level) logging::CreateLogger##level(__LINE__, __PRETTY_FUNCTION__, nullptr) -#define qCreateLogger(line, class_name, level) logging::CreateLogger(logging::Level_##level, logging::ParsePrettyFunction(class_name), line) -#endif // QT_NO_DEBUG_STREAM +// This macro specifies a separate category for message filtering. +// The default qLog will use the class name extracted from the function name for this purpose. +// The category is also printed in the message along with the class name. +# define qLogCat(level, category) logging::CreateLogger##level(__LINE__, __PRETTY_FUNCTION__, category) + +#endif // QT_NO_DEBUG_STREAM namespace logging { - class NullDevice : public QIODevice { + +class NullDevice : public QIODevice { protected: qint64 readData(char*, qint64) { return -1; } qint64 writeData(const char*, qint64 len) { return len; } @@ -52,33 +58,30 @@ enum Level { void DumpStackTrace(); - QString ParsePrettyFunction(const char *pretty_function); - QDebug CreateLogger(Level level, const QString &class_name, int line); - -QDebug CreateLoggerFatal(int line, const char *class_name); -QDebug CreateLoggerError(int line, const char *class_name); +QDebug CreateLoggerFatal(int line, const char *pretty_function, const char* category); +QDebug CreateLoggerError(int line, const char *pretty_function, const char* category); #ifdef QT_NO_WARNING_OUTPUT -QNoDebug CreateLoggerWarning(int, const char*); + QNoDebug CreateLoggerWarning(int, const char*, const char*); #else -QDebug CreateLoggerWarning(int line, const char *class_name); + QDebug CreateLoggerWarning(int line, const char *pretty_function, const char* category); #endif // QT_NO_WARNING_OUTPUT #ifdef QT_NO_DEBUG_OUTPUT -QNoDebug CreateLoggerInfo(int, const char*); -QNoDebug CreateLoggerDebug(int, const char*); + QNoDebug CreateLoggerInfo(int, const char*, const char*); + QNoDebug CreateLoggerDebug(int, const char*, const char*); #else -QDebug CreateLoggerInfo(int line, const char *class_name); -QDebug CreateLoggerDebug(int line, const char *class_name); -#endif // QT_NO_DEBUG_OUTPUT + QDebug CreateLoggerInfo(int line, const char *pretty_function, const char* category); + QDebug CreateLoggerDebug(int line, const char *pretty_function, const char* category); +#endif // QT_NO_DEBUG_OUTPUT -void GLog(const char *domain, int level, const char *message, void *user_data); +void GLog(const char* domain, int level, const char* message, void* user_data); extern const char *kDefaultLogLevels; -} + +} // namespace logging QDebug operator<<(QDebug debug, std::chrono::seconds secs); #endif // LOGGING_H - diff --git a/ext/libstrawberry-tagreader/CMakeLists.txt b/ext/libstrawberry-tagreader/CMakeLists.txt index d55497a61..a6c4cdad0 100644 --- a/ext/libstrawberry-tagreader/CMakeLists.txt +++ b/ext/libstrawberry-tagreader/CMakeLists.txt @@ -1,10 +1,3 @@ -include_directories(${PROTOBUF_INCLUDE_DIRS}) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -include_directories(${CMAKE_CURRENT_BINARY_DIR}) -include_directories(${CMAKE_SOURCE_DIR}/ext/libstrawberry-common) -include_directories(${CMAKE_SOURCE_DIR}/src) -include_directories(${CMAKE_BINARY_DIR}/src) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Wextra -Wpedantic -Woverloaded-virtual -fpermissive") @@ -13,6 +6,14 @@ if(APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") endif() +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +include_directories(${CMAKE_SOURCE_DIR}/ext/libstrawberry-common) +include_directories(${CMAKE_SOURCE_DIR}/src) +include_directories(${CMAKE_BINARY_DIR}/src) + +include_directories(${PROTOBUF_INCLUDE_DIRS}) + set(MESSAGES tagreadermessages.proto ) diff --git a/src/config.h.in b/src/config.h.in index 1755932e5..e8ee1b242 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -25,6 +25,7 @@ #define CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}" #cmakedefine DEBUG +#cmakedefine HAVE_BACKTRACE #cmakedefine HAVE_GIO #cmakedefine HAVE_DBUS #cmakedefine HAVE_X11