Add Logger observer and helper class
[WebKit-https.git] / Source / WebCore / PAL / pal / Logger.h
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/Assertions.h>
29 #include <wtf/HexNumber.h>
30 #include <wtf/Noncopyable.h>
31 #include <wtf/RefCounted.h>
32 #include <wtf/RefPtr.h>
33 #include <wtf/text/StringBuilder.h>
34 #include <wtf/text/WTFString.h>
35
36 namespace PAL {
37
38 template<typename T>
39 struct LogArgument {
40     template<typename U = T> static typename std::enable_if<std::is_same<U, bool>::value, String>::type toString(bool argument) { return argument ? ASCIILiteral("true") : ASCIILiteral("false"); }
41     template<typename U = T> static typename std::enable_if<std::is_same<U, int>::value, String>::type toString(int argument) { return String::number(argument); }
42     template<typename U = T> static typename std::enable_if<std::is_same<U, float>::value, String>::type toString(float argument) { return String::number(argument); }
43     template<typename U = T> static typename std::enable_if<std::is_same<U, double>::value, String>::type toString(double argument) { return String::number(argument); }
44     template<typename U = T> static typename std::enable_if<std::is_same<typename std::remove_reference<U>::type, AtomicString>::value, String>::type toString(AtomicString 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<U, const char*>::value, String>::type toString(const char* argument) { return String(argument); }
47     template<size_t length> static String toString(const char (&argument)[length]) { return String(argument); }
48 };
49
50 class Logger : public RefCounted<Logger> {
51     WTF_MAKE_NONCOPYABLE(Logger);
52 public:
53
54     class Observer {
55     public:
56         virtual ~Observer() { }
57         virtual void didLogMessage(const WTFLogChannel&, const String&) = 0;
58     };
59
60     static Ref<Logger> create(const void* owner)
61     {
62         return adoptRef(*new Logger(owner));
63     }
64
65     template<typename... Arguments>
66     inline void logAlways(WTFLogChannel& channel, const Arguments&... arguments) const
67     {
68 #if RELEASE_LOG_DISABLED
69         // "Standard" WebCore logging goes to stderr, which is captured in layout test output and can generally be a problem
70         //  on some systems, so don't allow it.
71         UNUSED_PARAM(channel);
72 #else
73         if (!willLog(channel, WTFLogLevelAlways))
74             return;
75
76         log(channel, arguments...);
77 #endif
78     }
79
80     template<typename... Arguments>
81     inline void error(WTFLogChannel& channel, const Arguments&... arguments) const
82     {
83         if (!willLog(channel, WTFLogLevelError))
84             return;
85
86         log(channel, arguments...);
87     }
88
89     template<typename... Arguments>
90     inline void warning(WTFLogChannel& channel, const Arguments&... arguments) const
91     {
92         if (!willLog(channel, WTFLogLevelWarning))
93             return;
94
95         log(channel, arguments...);
96     }
97
98     template<typename... Arguments>
99     inline void notice(WTFLogChannel& channel, const Arguments&... arguments) const
100     {
101         if (!willLog(channel, WTFLogLevelNotice))
102             return;
103
104         log(channel, arguments...);
105     }
106
107     template<typename... Arguments>
108     inline void info(WTFLogChannel& channel, const Arguments&... arguments) const
109     {
110         if (!willLog(channel, WTFLogLevelInfo))
111             return;
112
113         log(channel, arguments...);
114     }
115
116     template<typename... Arguments>
117     inline void debug(WTFLogChannel& channel, const Arguments&... arguments) const
118     {
119         if (!willLog(channel, WTFLogLevelDebug))
120             return;
121
122         log(channel, arguments...);
123     }
124
125     inline bool willLog(const WTFLogChannel& channel, WTFLogLevel level) const
126     {
127         if (level != WTFLogLevelAlways && level > channel.level)
128             return false;
129
130         if (channel.level != WTFLogLevelAlways && channel.state == WTFLogChannelOff)
131             return false;
132
133         return m_enabled;
134     }
135
136     bool enabled() const { return m_enabled; }
137     void setEnabled(const void* owner, bool enabled)
138     {
139         ASSERT(owner == m_owner);
140         if (owner == m_owner)
141             m_enabled = enabled;
142     }
143
144     struct MethodAndPointer {
145         MethodAndPointer(const char* methodName, const void* objectPtr)
146             : methodName { methodName }
147             , objectPtr { reinterpret_cast<uintptr_t>(objectPtr) }
148         {
149         }
150
151         MethodAndPointer(const char* className, const char* methodName, const void* objectPtr)
152             : className { className }
153             , methodName { methodName }
154             , objectPtr { reinterpret_cast<uintptr_t>(objectPtr) }
155         {
156         }
157
158         const char* className { nullptr };
159         const char* methodName { nullptr };
160         const uintptr_t objectPtr { 0 };
161     };
162
163     static inline void addObserver(Observer& observer)
164     {
165         observers().append(observer);
166     }
167     static inline void removeObserver(Observer& observer)
168     {
169         observers().removeFirstMatching([&observer](auto anObserver) {
170             return &anObserver.get() == &observer;
171         });
172     }
173
174 private:
175     Logger(const void* owner)
176         : m_owner { owner }
177     {
178     }
179
180     template<typename... Argument>
181     static inline void log(WTFLogChannel& channel, const Argument&... arguments)
182     {
183         String logMessage = makeString(LogArgument<Argument>::toString(arguments)...);
184
185 #if RELEASE_LOG_DISABLED
186         WTFLog(&channel, "%s", logMessage.utf8().data());
187 #else
188         os_log(channel.osLogChannel, "%{public}s", logMessage.utf8().data());
189 #endif
190
191         for (Observer& observer : observers())
192             observer.didLogMessage(channel, logMessage);
193     }
194
195     static Vector<std::reference_wrapper<Observer>>& observers()
196     {
197         static NeverDestroyed<Vector<std::reference_wrapper<Observer>>> observers;
198         return observers;
199     }
200
201     const void* m_owner;
202     bool m_enabled { true };
203 };
204
205 class LogHelper {
206 public:
207     virtual ~LogHelper() = default;
208
209     virtual const Logger& logger() const = 0;
210     virtual const char* className() const = 0;
211     virtual WTFLogChannel& logChannel() const = 0;
212
213     inline bool willLog(WTFLogLevel level) const { return logger().willLog(logChannel(), level); }
214
215 #define ALWAYS_LOG(...)     logger().logAlways(logChannel(), Logger::MethodAndPointer(className(), __func__, this), ##__VA_ARGS__)
216 #define ERROR_LOG(...)      logger().error(logChannel(), Logger::MethodAndPointer(className(), __func__, this), ##__VA_ARGS__)
217 #define WARNING_LOG(...)    logger().warning(logChannel(), Logger::MethodAndPointer(className(), __func__, this), ##__VA_ARGS__)
218 #define NOTICE_LOG(...)     logger().notice(logChannel(), Logger::MethodAndPointer(className(), __func__, this), ##__VA_ARGS__)
219 #define INFO_LOG(...)       logger().info(logChannel(), Logger::MethodAndPointer(className(), __func__, this), ##__VA_ARGS__)
220 #define DEBUG_LOG(...)      logger().debug(logChannel(), Logger::MethodAndPointer(className(), __func__, this), ##__VA_ARGS__)
221
222 };
223
224 template <>
225 struct LogArgument<Logger::MethodAndPointer> {
226     static String toString(const Logger::MethodAndPointer& value)
227     {
228         StringBuilder builder;
229
230         if (value.className) {
231             builder.append(value.className);
232             builder.appendLiteral("::");
233         }
234         builder.append(value.methodName);
235         builder.appendLiteral("(0x");
236         appendUnsigned64AsHex(value.objectPtr, builder);
237         builder.appendLiteral(") ");
238         return builder.toString();
239     }
240 };
241
242 } // namespace PAL