Update logger

This commit is contained in:
Jonas Kvinge 2020-03-08 18:40:39 +01:00
parent 8deb0ed556
commit e55d9cafb6
7 changed files with 155 additions and 105 deletions

View File

@ -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)

View File

@ -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}
)

View File

@ -1,25 +0,0 @@
/*
* Strawberry Music Player
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#cmakedefine01 Backtrace_FOUND
#if Backtrace_FOUND
# define HAVE_BACKTRACE 1
# include <${Backtrace_HEADER}>
#endif

View File

@ -14,9 +14,21 @@
limitations under the License.
*/
#include "backtrace_inc.h"
#include <QtGlobal>
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <cxxabi.h>
#include <glib.h>
#ifdef HAVE_BACKTRACE
# include <execinfo.h>
#endif
#include <QByteArray>
#include <QList>
#include <QMap>
@ -25,15 +37,10 @@
#include <QRegExp>
#include <QDateTime>
#include <QIODevice>
#include <QBuffer>
#include <QtMessageHandler>
#include <QMessageLogContext>
#include <cxxabi.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <QDebug>
#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 <class T>
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 T>
class DebugBase : public QDebug {
public:
DebugBase() : QDebug(sNullDevice) {}
DebugBase(QtMsgType t) : QDebug(t) {}
T& space() { return static_cast<T&>(QDebug::space()); }
T& noSpace() { return static_cast<T&>(QDebug::nospace()); }
};
Q_UNUSED(context);
// Debug message will be stored in a buffer.
class BufferedDebug : public DebugBase<BufferedDebug> {
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<QBuffer> buf_;
};
// Debug message will be logged immediately.
class LoggedDebug : public DebugBase<LoggedDebug> {
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<BufferedDebug>(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 <class T>
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<LoggedDebug>(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

View File

@ -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

View File

@ -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
)

View File

@ -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