Add support for Apple Unified Logging

If the OS supports it, we will now log to the Apple unified logging
system in addition to the normal stderr output. These logs can be
inspected via the Console application, or the 'log' command line
tool.

See https://developer.apple.com/documentation/os/logging

[ChangeLog][QtCore] Apple Unified Logging is now supported on Apple platforms.

Task-number: QTBUG-38156
Done-with: Jake Petroules <jake.petroules@qt.io>
Change-Id: I2ab92bd192d5b98aaf77e41501ea7b1ca6ef2425
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
bb10
Tor Arne Vestbø 2017-12-08 18:46:44 +01:00
parent b2b32682a9
commit 211791d01c
3 changed files with 133 additions and 0 deletions

View File

@ -71,6 +71,10 @@
#include <android/log.h>
#endif
#ifdef Q_OS_DARWIN
#include <QtCore/private/qcore_mac_p.h>
#endif
#if QT_CONFIG(journald)
# define SD_JOURNAL_SUPPRESS_LOCATION
# include <systemd/sd-journal.h>
@ -1676,6 +1680,9 @@ static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &con
handledStderr |= syslog_default_message_handler(type, context, message);
# elif defined(Q_OS_ANDROID)
handledStderr |= android_default_message_handler(type, context, message);
# elif defined(QT_USE_APPLE_UNIFIED_LOGGING)
if (__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *))
handledStderr |= AppleUnifiedLogger::messageHandler(type, context, message);
# endif
#endif

View File

@ -39,6 +39,9 @@
#include <private/qcore_mac_p.h>
#include <new>
#include "qhash.h"
#include "qpair.h"
#include "qvarlengtharray.h"
QT_BEGIN_NAMESPACE
@ -57,4 +60,96 @@ QCFString::operator CFStringRef() const
return value;
}
// --------------------------------------------------------------------------
#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
bool AppleUnifiedLogger::messageHandler(QtMsgType msgType, const QMessageLogContext &context,
const QString &message, const QString &optionalSubsystem)
{
QString subsystem = optionalSubsystem;
if (subsystem.isNull()) {
static QString bundleIdentifier = []() {
if (CFBundleRef bundle = CFBundleGetMainBundle()) {
if (CFStringRef identifier = CFBundleGetIdentifier(bundle))
return QString::fromCFString(identifier);
}
return QString();
}();
subsystem = bundleIdentifier;
}
const bool isDefault = !context.category || !strcmp(context.category, "default");
os_log_t log = isDefault ? OS_LOG_DEFAULT :
cachedLog(subsystem, QString::fromLatin1(context.category));
os_log_type_t logType = logTypeForMessageType(msgType);
if (!os_log_type_enabled(log, logType))
return false;
// Logging best practices says we should not include symbolication
// information or source file line numbers in messages, as the system
// will automatically captures this information. In our case, what
// the system captures is the call to os_log_with_type below, which
// isn't really useful, but we still don't want to include the context's
// info, as that would clutter the logging output. See rdar://35958308.
// The format must be a string constant, so we can't pass on the
// message. This means we won't be able to take advantage of the
// unified logging's custom format specifiers such as %{BOOL}d.
// We use the 'public' format specifier to prevent the logging
// system from redacting our log message.
os_log_with_type(log, logType, "%{public}s", qPrintable(message));
// When running under Xcode or LLDB, one or more of these variables will
// be set, which triggers libsystem_trace.dyld to log messages to stderr
// as well, via_os_log_impl_mirror_to_stderr. Un-setting these variables
// is not an option, as that would silence normal NSLog or os_log calls,
// so instead we skip our own stderr output. See rdar://36919139.
static bool mirroredToStderr = qEnvironmentVariableIsSet("OS_ACTIVITY_DT_MODE")
|| qEnvironmentVariableIsSet("ACTIVITY_LOG_STDERR")
|| qEnvironmentVariableIsSet("CFLOG_FORCE_STDERR");
return mirroredToStderr;
}
os_log_type_t AppleUnifiedLogger::logTypeForMessageType(QtMsgType msgType)
{
switch (msgType) {
case QtDebugMsg: return OS_LOG_TYPE_DEBUG;
case QtInfoMsg: return OS_LOG_TYPE_INFO;
case QtWarningMsg: return OS_LOG_TYPE_DEFAULT;
case QtCriticalMsg: return OS_LOG_TYPE_ERROR;
case QtFatalMsg: return OS_LOG_TYPE_FAULT;
}
return OS_LOG_TYPE_DEFAULT;
}
os_log_t AppleUnifiedLogger::cachedLog(const QString &subsystem, const QString &category)
{
static QBasicMutex mutex;
QMutexLocker locker(&mutex);
static QHash<QPair<QString, QString>, os_log_t> logs;
const auto cacheKey = qMakePair(subsystem, category);
os_log_t log = logs.value(cacheKey);
if (!log) {
log = os_log_create(subsystem.toLatin1().constData(),
category.toLatin1().constData());
logs.insert(cacheKey, log);
// Technically we should release the os_log_t resource when done
// with it, but since we don't know when a category is disabled
// we keep all cached os_log_t instances until shutdown, where
// the OS will clean them up for us.
}
return log;
}
#endif // QT_USE_APPLE_UNIFIED_LOGGING
// --------------------------------------------------------------------------
QT_END_NAMESPACE

View File

@ -159,6 +159,37 @@ QDebug operator<<(QDebug debug, const QMacAutoReleasePool *pool);
Q_CORE_EXPORT void qt_apple_check_os_version();
// --------------------------------------------------------------------------
#if !defined(QT_BOOTSTRAPPED) && (QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_12) || !defined(Q_OS_MACOS))
#define QT_USE_APPLE_UNIFIED_LOGGING
QT_END_NAMESPACE
#include <os/log.h>
// The compiler isn't smart enough to realize that we're calling these functions
// guarded by __builtin_available, so we need to also tag each function with the
// runtime requirements.
#include <os/availability.h>
#define OS_LOG_AVAILABILITY API_AVAILABLE(macos(10.12), ios(10.0), tvos(10.0), watchos(3.0))
QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT AppleUnifiedLogger
{
public:
static bool messageHandler(QtMsgType msgType, const QMessageLogContext &context, const QString &message,
const QString &subsystem = QString()) OS_LOG_AVAILABILITY;
private:
static os_log_type_t logTypeForMessageType(QtMsgType msgType) OS_LOG_AVAILABILITY;
static os_log_t cachedLog(const QString &subsystem, const QString &category) OS_LOG_AVAILABILITY;
};
#undef OS_LOG_AVAILABILITY
#endif
// --------------------------------------------------------------------------
QT_END_NAMESPACE
#endif // QCORE_MAC_P_H