/* * 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 #include #include #if USE(JOURNALD) #define SD_JOURNAL_SUPPRESS_LOCATION #include #endif namespace WTF { template struct LogArgument { template static typename std::enable_if::value, String>::type toString(bool argument) { return argument ? "true"_s : "false"_s; } template static typename std::enable_if::value, String>::type toString(int argument) { return String::number(argument); } template static typename std::enable_if::value, String>::type toString(unsigned argument) { return String::number(argument); } template static typename std::enable_if::value, String>::type toString(unsigned long argument) { return String::number(argument); } template static typename std::enable_if::value, String>::type toString(long argument) { return String::number(argument); } template static typename std::enable_if::value, String>::type toString(unsigned long long argument) { return String::number(argument); } template static typename std::enable_if::value, String>::type toString(long long argument) { return String::number(argument); } template static typename std::enable_if::value, String>::type toString(U argument) { return String::number(static_cast::type>(argument)); } template static typename std::enable_if::value, String>::type toString(float argument) { return String::number(argument); } template static typename std::enable_if::value, String>::type toString(double argument) { return String::number(argument); } template static typename std::enable_if::type, AtomString>::value, String>::type toString(const AtomString& argument) { return argument.string(); } template static typename std::enable_if::type, String>::value, String>::type toString(String argument) { return argument; } template static typename std::enable_if::type, StringBuilder*>::value, String>::type toString(StringBuilder* argument) { return argument->toString(); } template static typename std::enable_if::value, String>::type toString(const char* argument) { return String(argument); } template 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 HasToJSONString { template static std::true_type testSignature(String (T::*)() const); template static decltype(testSignature(&T::toJSONString)) test(std::nullptr_t); template static std::false_type test(...); public: static constexpr bool value = decltype(test(nullptr))::value; }; template::value> struct ConsoleLogValueImpl; template struct ConsoleLogValueImpl { static JSONLogValue toValue(const Argument& value) { return JSONLogValue { JSONLogValue::Type::JSON, value.toJSONString() }; } }; template struct ConsoleLogValueImpl { static JSONLogValue toValue(const Argument& value) { return JSONLogValue { JSONLogValue::Type::String, LogArgument::toString(value) }; } }; template::value> struct ConsoleLogValue; template struct ConsoleLogValue { static JSONLogValue toValue(const Argument& value) { return ConsoleLogValueImpl::toValue(value); } }; // Specialization for non-class types template struct ConsoleLogValue { template static JSONLogValue toValue(T value) { return JSONLogValue { JSONLogValue::Type::String, LogArgument::toString(value) }; } }; WTF_EXPORT_PRIVATE extern Lock loggerObserverLock; class Logger : public ThreadSafeRefCounted { WTF_MAKE_NONCOPYABLE(Logger); public: class Observer { public: virtual ~Observer() = default; // Can be called on any thread. virtual void didLogMessage(const WTFLogChannel&, WTFLogLevel, Vector&&) = 0; }; static Ref create(const void* owner) { return adoptRef(*new Logger(owner)); } template 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 inline void error(WTFLogChannel& channel, const Arguments&... arguments) const { if (!willLog(channel, WTFLogLevel::Error)) return; log(channel, WTFLogLevel::Error, arguments...); } template inline void warning(WTFLogChannel& channel, const Arguments&... arguments) const { if (!willLog(channel, WTFLogLevel::Warning)) return; log(channel, WTFLogLevel::Warning, arguments...); } template inline void info(WTFLogChannel& channel, const Arguments&... arguments) const { if (!willLog(channel, WTFLogLevel::Info)) return; log(channel, WTFLogLevel::Info, arguments...); } template inline void debug(WTFLogChannel& channel, const Arguments&... arguments) const { if (!willLog(channel, WTFLogLevel::Debug)) return; log(channel, WTFLogLevel::Debug, arguments...); } template 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 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 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 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 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(objectPtr) } { } LogSiteIdentifier(const char* className, const char* methodName, const void* objectPtr) : className { className } , methodName { methodName } , objectPtr { reinterpret_cast(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 static inline void log(WTFLogChannel& channel, WTFLogLevel level, const Argument&... arguments) { auto logMessage = makeString(LogArgument::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::toValue(arguments)... }); } template static inline void logVerbose(WTFLogChannel& channel, WTFLogLevel level, const char* file, const char* function, int line, const Argument&... arguments) { auto logMessage = makeString(LogArgument::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::toValue(arguments)... }); } WTF_EXPORT_PRIVATE static Vector>& 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 { static String toString(const Logger::LogSiteIdentifier& value) { return value.toString(); } }; template<> struct LogArgument { WTF_EXPORT_PRIVATE static String toString(const void*); }; } // namespace WTF using WTF::Logger; using WTF::JSONLogValue;