387 lines
15 KiB
C++
387 lines
15 KiB
C++
/*
|
|
* Copyright (C) 2017-2019 Apple Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <wtf/Lock.h>
|
|
#include <wtf/ThreadSafeRefCounted.h>
|
|
#include <wtf/text/StringBuilder.h>
|
|
|
|
#if USE(JOURNALD)
|
|
#define SD_JOURNAL_SUPPRESS_LOCATION
|
|
#include <systemd/sd-journal.h>
|
|
#endif
|
|
|
|
namespace WTF {
|
|
|
|
template<typename T>
|
|
struct LogArgument {
|
|
template<typename U = T> static typename std::enable_if<std::is_same<U, bool>::value, String>::type toString(bool argument) { return argument ? "true"_s : "false"_s; }
|
|
template<typename U = T> static typename std::enable_if<std::is_same<U, int>::value, String>::type toString(int argument) { return String::number(argument); }
|
|
template<typename U = T> static typename std::enable_if<std::is_same<U, unsigned>::value, String>::type toString(unsigned argument) { return String::number(argument); }
|
|
template<typename U = T> static typename std::enable_if<std::is_same<U, unsigned long>::value, String>::type toString(unsigned long argument) { return String::number(argument); }
|
|
template<typename U = T> static typename std::enable_if<std::is_same<U, long>::value, String>::type toString(long argument) { return String::number(argument); }
|
|
template<typename U = T> static typename std::enable_if<std::is_same<U, unsigned long long>::value, String>::type toString(unsigned long long argument) { return String::number(argument); }
|
|
template<typename U = T> static typename std::enable_if<std::is_same<U, long long>::value, String>::type toString(long long argument) { return String::number(argument); }
|
|
template<typename U = T> static typename std::enable_if<std::is_enum<U>::value, String>::type toString(U argument) { return String::number(static_cast<typename std::underlying_type<U>::type>(argument)); }
|
|
template<typename U = T> static typename std::enable_if<std::is_same<U, float>::value, String>::type toString(float argument) { return String::number(argument); }
|
|
template<typename U = T> static typename std::enable_if<std::is_same<U, double>::value, String>::type toString(double argument) { return String::number(argument); }
|
|
template<typename U = T> static typename std::enable_if<std::is_same<typename std::remove_reference<U>::type, AtomString>::value, String>::type toString(const AtomString& argument) { return argument.string(); }
|
|
template<typename U = T> static typename std::enable_if<std::is_same<typename std::remove_reference<U>::type, String>::value, String>::type toString(String argument) { return argument; }
|
|
template<typename U = T> static typename std::enable_if<std::is_same<typename std::remove_reference<U>::type, StringBuilder*>::value, String>::type toString(StringBuilder* argument) { return argument->toString(); }
|
|
template<typename U = T> static typename std::enable_if<std::is_same<U, const char*>::value, String>::type toString(const char* argument) { return String(argument); }
|
|
template<size_t length> static String toString(const char (&argument)[length]) { return String(argument); }
|
|
};
|
|
|
|
struct JSONLogValue {
|
|
enum class Type { String, JSON };
|
|
Type type { Type::JSON };
|
|
String value;
|
|
};
|
|
|
|
template<class C>
|
|
class HasToJSONString {
|
|
template <class T> static std::true_type testSignature(String (T::*)() const);
|
|
|
|
template <class T> static decltype(testSignature(&T::toJSONString)) test(std::nullptr_t);
|
|
template <class T> static std::false_type test(...);
|
|
|
|
public:
|
|
static constexpr bool value = decltype(test<C>(nullptr))::value;
|
|
};
|
|
|
|
template<typename Argument, bool hasJSON = HasToJSONString<Argument>::value>
|
|
struct ConsoleLogValueImpl;
|
|
|
|
template<typename Argument>
|
|
struct ConsoleLogValueImpl<Argument, true> {
|
|
static JSONLogValue toValue(const Argument& value)
|
|
{
|
|
return JSONLogValue { JSONLogValue::Type::JSON, value.toJSONString() };
|
|
}
|
|
};
|
|
|
|
template<typename Argument>
|
|
struct ConsoleLogValueImpl<Argument, false> {
|
|
static JSONLogValue toValue(const Argument& value)
|
|
{
|
|
return JSONLogValue { JSONLogValue::Type::String, LogArgument<Argument>::toString(value) };
|
|
}
|
|
};
|
|
|
|
template<typename Argument, bool hasJSON = std::is_class<Argument>::value>
|
|
struct ConsoleLogValue;
|
|
|
|
template<typename Argument>
|
|
struct ConsoleLogValue<Argument, true> {
|
|
static JSONLogValue toValue(const Argument& value)
|
|
{
|
|
return ConsoleLogValueImpl<Argument>::toValue(value);
|
|
}
|
|
};
|
|
|
|
// Specialization for non-class types
|
|
template<typename Argument>
|
|
struct ConsoleLogValue<Argument, false> {
|
|
template<typename T>
|
|
static JSONLogValue toValue(T value)
|
|
{
|
|
return JSONLogValue { JSONLogValue::Type::String, LogArgument<T>::toString(value) };
|
|
}
|
|
};
|
|
|
|
WTF_EXPORT_PRIVATE extern Lock loggerObserverLock;
|
|
|
|
class Logger : public ThreadSafeRefCounted<Logger> {
|
|
WTF_MAKE_NONCOPYABLE(Logger);
|
|
public:
|
|
|
|
class Observer {
|
|
public:
|
|
virtual ~Observer() = default;
|
|
// Can be called on any thread.
|
|
virtual void didLogMessage(const WTFLogChannel&, WTFLogLevel, Vector<JSONLogValue>&&) = 0;
|
|
};
|
|
|
|
static Ref<Logger> create(const void* owner)
|
|
{
|
|
return adoptRef(*new Logger(owner));
|
|
}
|
|
|
|
template<typename... Arguments>
|
|
inline void logAlways(WTFLogChannel& channel, UNUSED_FUNCTION const Arguments&... arguments) const
|
|
{
|
|
#if RELEASE_LOG_DISABLED
|
|
// "Standard" WebCore logging goes to stderr, which is captured in layout test output and can generally be a problem
|
|
// on some systems, so don't allow it.
|
|
UNUSED_PARAM(channel);
|
|
#else
|
|
if (!willLog(channel, WTFLogLevel::Always))
|
|
return;
|
|
|
|
log(channel, WTFLogLevel::Always, arguments...);
|
|
#endif
|
|
}
|
|
|
|
template<typename... Arguments>
|
|
inline void error(WTFLogChannel& channel, const Arguments&... arguments) const
|
|
{
|
|
if (!willLog(channel, WTFLogLevel::Error))
|
|
return;
|
|
|
|
log(channel, WTFLogLevel::Error, arguments...);
|
|
}
|
|
|
|
template<typename... Arguments>
|
|
inline void warning(WTFLogChannel& channel, const Arguments&... arguments) const
|
|
{
|
|
if (!willLog(channel, WTFLogLevel::Warning))
|
|
return;
|
|
|
|
log(channel, WTFLogLevel::Warning, arguments...);
|
|
}
|
|
|
|
template<typename... Arguments>
|
|
inline void info(WTFLogChannel& channel, const Arguments&... arguments) const
|
|
{
|
|
if (!willLog(channel, WTFLogLevel::Info))
|
|
return;
|
|
|
|
log(channel, WTFLogLevel::Info, arguments...);
|
|
}
|
|
|
|
template<typename... Arguments>
|
|
inline void debug(WTFLogChannel& channel, const Arguments&... arguments) const
|
|
{
|
|
if (!willLog(channel, WTFLogLevel::Debug))
|
|
return;
|
|
|
|
log(channel, WTFLogLevel::Debug, arguments...);
|
|
}
|
|
|
|
template<typename... Arguments>
|
|
inline void logAlwaysVerbose(WTFLogChannel& channel, const char* file, const char* function, int line, UNUSED_FUNCTION const Arguments&... arguments) const
|
|
{
|
|
#if RELEASE_LOG_DISABLED
|
|
// "Standard" WebCore logging goes to stderr, which is captured in layout test output and can generally be a problem
|
|
// on some systems, so don't allow it.
|
|
UNUSED_PARAM(channel);
|
|
UNUSED_PARAM(file);
|
|
UNUSED_PARAM(function);
|
|
UNUSED_PARAM(line);
|
|
#else
|
|
if (!willLog(channel, WTFLogLevel::Always))
|
|
return;
|
|
|
|
logVerbose(channel, WTFLogLevel::Always, file, function, line, arguments...);
|
|
#endif
|
|
}
|
|
|
|
template<typename... Arguments>
|
|
inline void errorVerbose(WTFLogChannel& channel, const char* file, const char* function, int line, const Arguments&... arguments) const
|
|
{
|
|
if (!willLog(channel, WTFLogLevel::Error))
|
|
return;
|
|
|
|
logVerbose(channel, WTFLogLevel::Error, file, function, line, arguments...);
|
|
}
|
|
|
|
template<typename... Arguments>
|
|
inline void warningVerbose(WTFLogChannel& channel, const char* file, const char* function, int line, const Arguments&... arguments) const
|
|
{
|
|
if (!willLog(channel, WTFLogLevel::Warning))
|
|
return;
|
|
|
|
logVerbose(channel, WTFLogLevel::Warning, file, function, line, arguments...);
|
|
}
|
|
|
|
template<typename... Arguments>
|
|
inline void infoVerbose(WTFLogChannel& channel, const char* file, const char* function, int line, const Arguments&... arguments) const
|
|
{
|
|
if (!willLog(channel, WTFLogLevel::Info))
|
|
return;
|
|
|
|
logVerbose(channel, WTFLogLevel::Info, file, function, line, arguments...);
|
|
}
|
|
|
|
template<typename... Arguments>
|
|
inline void debugVerbose(WTFLogChannel& channel, const char* file, const char* function, int line, const Arguments&... arguments) const
|
|
{
|
|
if (!willLog(channel, WTFLogLevel::Debug))
|
|
return;
|
|
|
|
logVerbose(channel, WTFLogLevel::Debug, file, function, line, arguments...);
|
|
}
|
|
|
|
inline bool willLog(const WTFLogChannel& channel, WTFLogLevel level) const
|
|
{
|
|
if (!m_enabled)
|
|
return false;
|
|
|
|
#if USE(SYSTEMD)
|
|
if (channel.state == WTFLogChannelState::Off)
|
|
return false;
|
|
#endif
|
|
|
|
if (level <= WTFLogLevel::Error)
|
|
return true;
|
|
|
|
if (channel.state == WTFLogChannelState::Off || level > channel.level)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool enabled() const { return m_enabled; }
|
|
void setEnabled(const void* owner, bool enabled)
|
|
{
|
|
ASSERT(owner == m_owner);
|
|
if (owner == m_owner)
|
|
m_enabled = enabled;
|
|
}
|
|
|
|
struct LogSiteIdentifier {
|
|
LogSiteIdentifier(const char* methodName, const void* objectPtr)
|
|
: methodName { methodName }
|
|
, objectPtr { reinterpret_cast<uintptr_t>(objectPtr) }
|
|
{
|
|
}
|
|
|
|
LogSiteIdentifier(const char* className, const char* methodName, const void* objectPtr)
|
|
: className { className }
|
|
, methodName { methodName }
|
|
, objectPtr { reinterpret_cast<uintptr_t>(objectPtr) }
|
|
{
|
|
}
|
|
|
|
WTF_EXPORT_PRIVATE String toString() const;
|
|
|
|
const char* className { nullptr };
|
|
const char* methodName { nullptr };
|
|
const uintptr_t objectPtr { 0 };
|
|
};
|
|
|
|
static inline void addObserver(Observer& observer)
|
|
{
|
|
Locker locker { observerLock() };
|
|
observers().append(observer);
|
|
}
|
|
static inline void removeObserver(Observer& observer)
|
|
{
|
|
Locker locker { observerLock() };
|
|
observers().removeFirstMatching([&observer](auto anObserver) {
|
|
return &anObserver.get() == &observer;
|
|
});
|
|
}
|
|
|
|
private:
|
|
friend class AggregateLogger;
|
|
|
|
Logger(const void* owner)
|
|
: m_owner { owner }
|
|
{
|
|
}
|
|
|
|
template<typename... Argument>
|
|
static inline void log(WTFLogChannel& channel, WTFLogLevel level, const Argument&... arguments)
|
|
{
|
|
auto logMessage = makeString(LogArgument<Argument>::toString(arguments)...);
|
|
|
|
#if RELEASE_LOG_DISABLED
|
|
WTFLog(&channel, "%s", logMessage.utf8().data());
|
|
#endif
|
|
#if USE(OS_LOG) && !RELEASE_LOG_DISABLED
|
|
os_log(channel.osLogChannel, "%{public}s", logMessage.utf8().data());
|
|
#endif
|
|
#if USE(JOURNALD) && !RELEASE_LOG_DISABLED
|
|
sd_journal_send("WEBKIT_SUBSYSTEM=%s", channel.subsystem, "WEBKIT_CHANNEL=%s", channel.name, "MESSAGE=%s", logMessage.utf8().data(), nullptr);
|
|
#endif
|
|
|
|
if (channel.state == WTFLogChannelState::Off || level > channel.level)
|
|
return;
|
|
|
|
if (!observerLock().tryLock())
|
|
return;
|
|
|
|
Locker locker { AdoptLock, observerLock() };
|
|
for (Observer& observer : observers())
|
|
observer.didLogMessage(channel, level, { ConsoleLogValue<Argument>::toValue(arguments)... });
|
|
}
|
|
|
|
template<typename... Argument>
|
|
static inline void logVerbose(WTFLogChannel& channel, WTFLogLevel level, const char* file, const char* function, int line, const Argument&... arguments)
|
|
{
|
|
auto logMessage = makeString(LogArgument<Argument>::toString(arguments)...);
|
|
|
|
#if RELEASE_LOG_DISABLED
|
|
WTFLogVerbose(file, line, function, &channel, "%s", logMessage.utf8().data());
|
|
#endif
|
|
#if USE(OS_LOG) && !RELEASE_LOG_DISABLED
|
|
os_log(channel.osLogChannel, "%{public}s", logMessage.utf8().data());
|
|
UNUSED_PARAM(file);
|
|
UNUSED_PARAM(line);
|
|
UNUSED_PARAM(function);
|
|
#endif
|
|
#if USE(JOURNALD) && !RELEASE_LOG_DISABLED
|
|
auto fileString = makeString("CODE_FILE=", file);
|
|
auto lineString = makeString("CODE_LINE=", line);
|
|
sd_journal_send_with_location(fileString.utf8().data(), lineString.utf8().data(), function, "WEBKIT_SUBSYSTEM=%s", channel.subsystem, "WEBKIT_CHANNEL=%s", channel.name, "MESSAGE=%s", logMessage.utf8().data(), nullptr);
|
|
#endif
|
|
|
|
if (channel.state == WTFLogChannelState::Off || level > channel.level)
|
|
return;
|
|
|
|
if (!observerLock().tryLock())
|
|
return;
|
|
|
|
Locker locker { AdoptLock, observerLock() };
|
|
for (Observer& observer : observers())
|
|
observer.didLogMessage(channel, level, { ConsoleLogValue<Argument>::toValue(arguments)... });
|
|
}
|
|
|
|
WTF_EXPORT_PRIVATE static Vector<std::reference_wrapper<Observer>>& observers() WTF_REQUIRES_LOCK(observerLock());
|
|
|
|
static Lock& observerLock() WTF_RETURNS_LOCK(loggerObserverLock)
|
|
{
|
|
return loggerObserverLock;
|
|
}
|
|
|
|
|
|
bool m_enabled { true };
|
|
const void* m_owner;
|
|
};
|
|
|
|
template<> struct LogArgument<Logger::LogSiteIdentifier> {
|
|
static String toString(const Logger::LogSiteIdentifier& value) { return value.toString(); }
|
|
};
|
|
template<> struct LogArgument<const void*> {
|
|
WTF_EXPORT_PRIVATE static String toString(const void*);
|
|
};
|
|
|
|
} // namespace WTF
|
|
|
|
using WTF::Logger;
|
|
using WTF::JSONLogValue;
|