1/*
2 * Copyright (C) 2017-2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include <wtf/Lock.h>
29#include <wtf/ThreadSafeRefCounted.h>
30#include <wtf/text/StringBuilder.h>
31
32namespace WTF {
33
34template<typename T>
35struct LogArgument {
36 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; }
37 template<typename U = T> static typename std::enable_if<std::is_same<U, int>::value, String>::type toString(int argument) { return String::number(argument); }
38 template<typename U = T> static typename std::enable_if<std::is_same<U, unsigned>::value, String>::type toString(unsigned argument) { return String::number(argument); }
39 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); }
40 template<typename U = T> static typename std::enable_if<std::is_same<U, long>::value, String>::type toString(long argument) { return String::number(argument); }
41 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); }
42 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); }
43 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)); }
44 template<typename U = T> static typename std::enable_if<std::is_same<U, float>::value, String>::type toString(float argument) { return String::numberToStringFixedPrecision(argument); }
45 template<typename U = T> static typename std::enable_if<std::is_same<U, double>::value, String>::type toString(double argument) { return String::numberToStringFixedPrecision(argument); }
46 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(); }
47 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; }
48 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(); }
49 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); }
50 template<size_t length> static String toString(const char (&argument)[length]) { return String(argument); }
51};
52
53struct JSONLogValue {
54 enum class Type { String, JSON };
55 Type type { Type::JSON };
56 String value;
57};
58
59template<class C>
60class HasToJSONString {
61 template <class T> static std::true_type testSignature(String (T::*)() const);
62
63 template <class T> static decltype(testSignature(&T::toJSONString)) test(std::nullptr_t);
64 template <class T> static std::false_type test(...);
65
66public:
67 static constexpr bool value = decltype(test<C>(nullptr))::value;
68};
69
70template<typename Argument, bool hasJSON = HasToJSONString<Argument>::value>
71struct ConsoleLogValueImpl;
72
73template<typename Argument>
74struct ConsoleLogValueImpl<Argument, true> {
75 static JSONLogValue toValue(const Argument& value)
76 {
77 return JSONLogValue { JSONLogValue::Type::JSON, value.toJSONString() };
78 }
79};
80
81template<typename Argument>
82struct ConsoleLogValueImpl<Argument, false> {
83 static JSONLogValue toValue(const Argument& value)
84 {
85 return JSONLogValue { JSONLogValue::Type::String, LogArgument<Argument>::toString(value) };
86 }
87};
88
89template<typename Argument, bool hasJSON = std::is_class<Argument>::value>
90struct ConsoleLogValue;
91
92template<typename Argument>
93struct ConsoleLogValue<Argument, true> {
94 static JSONLogValue toValue(const Argument& value)
95 {
96 return ConsoleLogValueImpl<Argument>::toValue(value);
97 }
98};
99
100// Specialization for non-class types
101template<typename Argument>
102struct ConsoleLogValue<Argument, false> {
103 template<typename T>
104 static JSONLogValue toValue(T value)
105 {
106 return JSONLogValue { JSONLogValue::Type::String, LogArgument<T>::toString(value) };
107 }
108};
109
110class Logger : public ThreadSafeRefCounted<Logger> {
111 WTF_MAKE_NONCOPYABLE(Logger);
112public:
113
114 class Observer {
115 public:
116 virtual ~Observer() = default;
117 // Can be called on any thread.
118 virtual void didLogMessage(const WTFLogChannel&, WTFLogLevel, Vector<JSONLogValue>&&) = 0;
119 };
120
121 static Ref<Logger> create(const void* owner)
122 {
123 return adoptRef(*new Logger(owner));
124 }
125
126 template<typename... Arguments>
127 inline void logAlways(WTFLogChannel& channel, UNUSED_FUNCTION const Arguments&... arguments) const
128 {
129#if RELEASE_LOG_DISABLED
130 // "Standard" WebCore logging goes to stderr, which is captured in layout test output and can generally be a problem
131 // on some systems, so don't allow it.
132 UNUSED_PARAM(channel);
133#else
134 if (!willLog(channel, WTFLogLevel::Always))
135 return;
136
137 log(channel, WTFLogLevel::Always, arguments...);
138#endif
139 }
140
141 template<typename... Arguments>
142 inline void error(WTFLogChannel& channel, const Arguments&... arguments) const
143 {
144 if (!willLog(channel, WTFLogLevel::Error))
145 return;
146
147 log(channel, WTFLogLevel::Error, arguments...);
148 }
149
150 template<typename... Arguments>
151 inline void warning(WTFLogChannel& channel, const Arguments&... arguments) const
152 {
153 if (!willLog(channel, WTFLogLevel::Warning))
154 return;
155
156 log(channel, WTFLogLevel::Warning, arguments...);
157 }
158
159 template<typename... Arguments>
160 inline void info(WTFLogChannel& channel, const Arguments&... arguments) const
161 {
162 if (!willLog(channel, WTFLogLevel::Info))
163 return;
164
165 log(channel, WTFLogLevel::Info, arguments...);
166 }
167
168 template<typename... Arguments>
169 inline void debug(WTFLogChannel& channel, const Arguments&... arguments) const
170 {
171 if (!willLog(channel, WTFLogLevel::Debug))
172 return;
173
174 log(channel, WTFLogLevel::Debug, arguments...);
175 }
176
177 inline bool willLog(const WTFLogChannel& channel, WTFLogLevel level) const
178 {
179 if (!m_enabled)
180 return false;
181
182 if (level <= WTFLogLevel::Error)
183 return true;
184
185 if (channel.state == WTFLogChannelState::Off || level > channel.level)
186 return false;
187
188 return m_enabled;
189 }
190
191 bool enabled() const { return m_enabled; }
192 void setEnabled(const void* owner, bool enabled)
193 {
194 ASSERT(owner == m_owner);
195 if (owner == m_owner)
196 m_enabled = enabled;
197 }
198
199 struct LogSiteIdentifier {
200 LogSiteIdentifier(const char* methodName, const void* objectPtr)
201 : methodName { methodName }
202 , objectPtr { reinterpret_cast<uintptr_t>(objectPtr) }
203 {
204 }
205
206 LogSiteIdentifier(const char* className, const char* methodName, const void* objectPtr)
207 : className { className }
208 , methodName { methodName }
209 , objectPtr { reinterpret_cast<uintptr_t>(objectPtr) }
210 {
211 }
212
213 WTF_EXPORT String toString() const;
214
215 const char* className { nullptr };
216 const char* methodName { nullptr };
217 const uintptr_t objectPtr { 0 };
218 };
219
220 static inline void addObserver(Observer& observer)
221 {
222 auto lock = holdLock(observerLock());
223 observers().append(observer);
224 }
225 static inline void removeObserver(Observer& observer)
226 {
227 auto lock = holdLock(observerLock());
228 observers().removeFirstMatching([&observer](auto anObserver) {
229 return &anObserver.get() == &observer;
230 });
231 }
232
233private:
234 friend class AggregateLogger;
235
236 Logger(const void* owner)
237 : m_owner { owner }
238 {
239 }
240
241 template<typename... Argument>
242 static inline void log(WTFLogChannel& channel, WTFLogLevel level, const Argument&... arguments)
243 {
244 String logMessage = makeString(LogArgument<Argument>::toString(arguments)...);
245
246#if RELEASE_LOG_DISABLED
247 WTFLog(&channel, "%s", logMessage.utf8().data());
248#else
249 os_log(channel.osLogChannel, "%{public}s", logMessage.utf8().data());
250#endif
251
252 if (channel.state == WTFLogChannelState::Off || level > channel.level)
253 return;
254
255 auto lock = tryHoldLock(observerLock());
256 if (!lock)
257 return;
258
259 for (Observer& observer : observers())
260 observer.didLogMessage(channel, level, { ConsoleLogValue<Argument>::toValue(arguments)... });
261 }
262
263 static Vector<std::reference_wrapper<Observer>>& observers()
264 {
265 static NeverDestroyed<Vector<std::reference_wrapper<Observer>>> observers;
266 return observers;
267 }
268
269 static Lock& observerLock()
270 {
271 static NeverDestroyed<Lock> observerLock;
272 return observerLock;
273 }
274
275
276 bool m_enabled { true };
277 const void* m_owner;
278};
279
280template<> struct LogArgument<Logger::LogSiteIdentifier> {
281 static String toString(const Logger::LogSiteIdentifier& value) { return value.toString(); }
282};
283template<> struct LogArgument<const void*> {
284 WTF_EXPORT static String toString(const void*);
285};
286
287} // namespace WTF
288
289using WTF::Logger;
290using WTF::JSONLogValue;
291