1/*
2 * Copyright (C) 2017 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/text/StringBuilder.h>
29
30namespace WTF {
31
32template<typename T>
33struct LogArgument {
34 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; }
35 template<typename U = T> static typename std::enable_if<std::is_same<U, int>::value, String>::type toString(int argument) { return String::number(argument); }
36 template<typename U = T> static typename std::enable_if<std::is_same<U, unsigned>::value, String>::type toString(unsigned argument) { return String::number(argument); }
37 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); }
38 template<typename U = T> static typename std::enable_if<std::is_same<U, long>::value, String>::type toString(long argument) { return String::number(argument); }
39 template<typename U = T> static typename std::enable_if<std::is_same<U, unsigned long 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 long>::value, String>::type toString(long argument) { return String::number(argument); }
41 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)); }
42 template<typename U = T> static typename std::enable_if<std::is_same<U, float>::value, String>::type toString(float argument) { return String::numberToStringFixedPrecision(argument); }
43 template<typename U = T> static typename std::enable_if<std::is_same<U, double>::value, String>::type toString(double argument) { return String::numberToStringFixedPrecision(argument); }
44 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(); }
45 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; }
46 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(); }
47 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); }
48 template<size_t length> static String toString(const char (&argument)[length]) { return String(argument); }
49};
50
51struct JSONLogValue {
52 enum class Type { String, JSON };
53 Type type { Type::JSON };
54 String value;
55};
56
57template<class C>
58class HasToJSONString {
59 template <class T> static std::true_type testSignature(String (T::*)() const);
60
61 template <class T> static decltype(testSignature(&T::toJSONString)) test(std::nullptr_t);
62 template <class T> static std::false_type test(...);
63
64public:
65 static const bool value = decltype(test<C>(nullptr))::value;
66};
67
68template<typename Argument, bool hasJSON = HasToJSONString<Argument>::value>
69struct ConsoleLogValueImpl;
70
71template<typename Argument>
72struct ConsoleLogValueImpl<Argument, true> {
73 static JSONLogValue toValue(const Argument& value)
74 {
75 return JSONLogValue { JSONLogValue::Type::JSON, value.toJSONString() };
76 }
77};
78
79template<typename Argument>
80struct ConsoleLogValueImpl<Argument, false> {
81 static JSONLogValue toValue(const Argument& value)
82 {
83 return JSONLogValue { JSONLogValue::Type::String, LogArgument<Argument>::toString(value) };
84 }
85};
86
87template<typename Argument, bool hasJSON = std::is_class<Argument>::value>
88struct ConsoleLogValue;
89
90template<typename Argument>
91struct ConsoleLogValue<Argument, true> {
92 static JSONLogValue toValue(const Argument& value)
93 {
94 return ConsoleLogValueImpl<Argument>::toValue(value);
95 }
96};
97
98// Specialization for non-class types
99template<typename Argument>
100struct ConsoleLogValue<Argument, false> {
101 template<typename T>
102 static JSONLogValue toValue(T value)
103 {
104 return JSONLogValue { JSONLogValue::Type::String, LogArgument<T>::toString(value) };
105 }
106};
107
108class Logger : public RefCounted<Logger> {
109 WTF_MAKE_NONCOPYABLE(Logger);
110public:
111
112 class Observer {
113 public:
114 virtual ~Observer() = default;
115 virtual void didLogMessage(const WTFLogChannel&, WTFLogLevel, Vector<JSONLogValue>&&) = 0;
116 };
117
118 static Ref<Logger> create(const void* owner)
119 {
120 return adoptRef(*new Logger(owner));
121 }
122
123 template<typename... Arguments>
124 inline void logAlways(WTFLogChannel& channel, UNUSED_FUNCTION const Arguments&... arguments) const
125 {
126#if RELEASE_LOG_DISABLED
127 // "Standard" WebCore logging goes to stderr, which is captured in layout test output and can generally be a problem
128 // on some systems, so don't allow it.
129 UNUSED_PARAM(channel);
130#else
131 if (!willLog(channel, WTFLogLevel::Always))
132 return;
133
134 log(channel, WTFLogLevel::Always, arguments...);
135#endif
136 }
137
138 template<typename... Arguments>
139 inline void error(WTFLogChannel& channel, const Arguments&... arguments) const
140 {
141 if (!willLog(channel, WTFLogLevel::Error))
142 return;
143
144 log(channel, WTFLogLevel::Error, arguments...);
145 }
146
147 template<typename... Arguments>
148 inline void warning(WTFLogChannel& channel, const Arguments&... arguments) const
149 {
150 if (!willLog(channel, WTFLogLevel::Warning))
151 return;
152
153 log(channel, WTFLogLevel::Warning, arguments...);
154 }
155
156 template<typename... Arguments>
157 inline void info(WTFLogChannel& channel, const Arguments&... arguments) const
158 {
159 if (!willLog(channel, WTFLogLevel::Info))
160 return;
161
162 log(channel, WTFLogLevel::Info, arguments...);
163 }
164
165 template<typename... Arguments>
166 inline void debug(WTFLogChannel& channel, const Arguments&... arguments) const
167 {
168 if (!willLog(channel, WTFLogLevel::Debug))
169 return;
170
171 log(channel, WTFLogLevel::Debug, arguments...);
172 }
173
174 inline bool willLog(const WTFLogChannel& channel, WTFLogLevel level) const
175 {
176 if (!m_enabled)
177 return false;
178
179 if (level <= WTFLogLevel::Error)
180 return true;
181
182 if (channel.state == WTFLogChannelState::Off || level > channel.level)
183 return false;
184
185 return m_enabled;
186 }
187
188 bool enabled() const { return m_enabled; }
189 void setEnabled(const void* owner, bool enabled)
190 {
191 ASSERT(owner == m_owner);
192 if (owner == m_owner)
193 m_enabled = enabled;
194 }
195
196 struct LogSiteIdentifier {
197 LogSiteIdentifier(const char* methodName, const void* objectPtr)
198 : methodName { methodName }
199 , objectPtr { reinterpret_cast<uintptr_t>(objectPtr) }
200 {
201 }
202
203 LogSiteIdentifier(const char* className, const char* methodName, const void* objectPtr)
204 : className { className }
205 , methodName { methodName }
206 , objectPtr { reinterpret_cast<uintptr_t>(objectPtr) }
207 {
208 }
209
210 WTF_EXPORT String toString() const;
211
212 const char* className { nullptr };
213 const char* methodName { nullptr };
214 const uintptr_t objectPtr { 0 };
215 };
216
217 static inline void addObserver(Observer& observer)
218 {
219 observers().append(observer);
220 }
221 static inline void removeObserver(Observer& observer)
222 {
223 observers().removeFirstMatching([&observer](auto anObserver) {
224 return &anObserver.get() == &observer;
225 });
226 }
227
228private:
229 friend class AggregateLogger;
230
231 Logger(const void* owner)
232 : m_owner { owner }
233 {
234 }
235
236 template<typename... Argument>
237 static inline void log(WTFLogChannel& channel, WTFLogLevel level, const Argument&... arguments)
238 {
239 String logMessage = makeString(LogArgument<Argument>::toString(arguments)...);
240
241#if RELEASE_LOG_DISABLED
242 WTFLog(&channel, "%s", logMessage.utf8().data());
243#else
244 os_log(channel.osLogChannel, "%{public}s", logMessage.utf8().data());
245#endif
246
247 if (channel.state == WTFLogChannelState::Off || level > channel.level)
248 return;
249
250 for (Observer& observer : observers())
251 observer.didLogMessage(channel, level, { ConsoleLogValue<Argument>::toValue(arguments)... });
252 }
253
254 static Vector<std::reference_wrapper<Observer>>& observers()
255 {
256 static NeverDestroyed<Vector<std::reference_wrapper<Observer>>> observers;
257 return observers;
258 }
259
260 bool m_enabled { true };
261 const void* m_owner;
262};
263
264template<> struct LogArgument<Logger::LogSiteIdentifier> {
265 static String toString(const Logger::LogSiteIdentifier& value) { return value.toString(); }
266};
267template<> struct LogArgument<const void*> {
268 WTF_EXPORT static String toString(const void*);
269};
270
271} // namespace WTF
272
273using WTF::Logger;
274using WTF::JSONLogValue;
275