4f238ce3ede5fbe84336c2a7b6b8f5fc693d1866
[WebKit.git] / Source / WTF / wtf / Logger.h
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
32 namespace WTF {
33
34 template<typename T>
35 struct 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::number(argument); }
45     template<typename U = T> static typename std::enable_if<std::is_same<U, double>::value, String>::type toString(double argument) { return String::number(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
53 struct JSONLogValue {
54     enum class Type { String, JSON };
55     Type type { Type::JSON };
56     String value;
57 };
58
59 template<class C>
60 class 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
66 public:
67     static constexpr bool value = decltype(test<C>(nullptr))::value;
68 };
69
70 template<typename Argument, bool hasJSON = HasToJSONString<Argument>::value>
71 struct ConsoleLogValueImpl;
72
73 template<typename Argument>
74 struct ConsoleLogValueImpl<Argument, true> {
75     static JSONLogValue toValue(const Argument& value)
76     {
77         return JSONLogValue { JSONLogValue::Type::JSON, value.toJSONString() };
78     }
79 };
80
81 template<typename Argument>
82 struct ConsoleLogValueImpl<Argument, false> {
83     static JSONLogValue toValue(const Argument& value)
84     {
85         return JSONLogValue { JSONLogValue::Type::String, LogArgument<Argument>::toString(value) };
86     }
87 };
88
89 template<typename Argument, bool hasJSON = std::is_class<Argument>::value>
90 struct ConsoleLogValue;
91
92 template<typename Argument>
93 struct 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
101 template<typename Argument>
102 struct 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
110 class Logger : public ThreadSafeRefCounted<Logger> {
111     WTF_MAKE_NONCOPYABLE(Logger);
112 public:
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 true;
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_PRIVATE 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
233 private:
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
280 template<> struct LogArgument<Logger::LogSiteIdentifier> {
281     static String toString(const Logger::LogSiteIdentifier& value) { return value.toString(); }
282 };
283 template<> struct LogArgument<const void*> {
284     WTF_EXPORT_PRIVATE static String toString(const void*);
285 };
286
287 } // namespace WTF
288
289 using WTF::Logger;
290 using WTF::JSONLogValue;