added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / util / logger.hpp
1 /*************************************************************************
2  *
3  * Copyright 2016 Realm Inc.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  **************************************************************************/
18
19 #ifndef REALM_UTIL_LOGGER_HPP
20 #define REALM_UTIL_LOGGER_HPP
21
22 #include <cstring>
23 #include <utility>
24 #include <string>
25 #include <locale>
26 #include <sstream>
27 #include <iostream>
28
29 #include <realm/util/features.h>
30 #include <realm/util/thread.hpp>
31 #include <realm/util/file.hpp>
32
33 namespace realm {
34 namespace util {
35
36
37 /// All Logger objects store a reference to a LevelThreshold object which it
38 /// uses to efficiently query about the current log level threshold
39 /// (`level_threshold.get()`). All messages logged with a level that is lower
40 /// than the current threshold will be dropped. For the sake of efficiency, this
41 /// test happens before the message is formatted.
42 ///
43 /// A logger is not inherently thread-safe, but specific implementations can be
44 /// (see ThreadSafeLogger). For a logger to be thread-safe, the implementation
45 /// of do_log() must be thread-safe and the referenced LevelThreshold object
46 /// must have a thread-safe get() method.
47 ///
48 /// Examples:
49 ///
50 ///    logger.error("Overlong message from master coordinator");
51 ///    logger.info("Listening for peers on %1:%2", listen_address, listen_port);
52 class Logger {
53 public:
54     template <class... Params>
55     void trace(const char* message, Params&&...);
56     template <class... Params>
57     void debug(const char* message, Params&&...);
58     template <class... Params>
59     void detail(const char* message, Params&&...);
60     template <class... Params>
61     void info(const char* message, Params&&...);
62     template <class... Params>
63     void warn(const char* message, Params&&...);
64     template <class... Params>
65     void error(const char* message, Params&&...);
66     template <class... Params>
67     void fatal(const char* message, Params&&...);
68
69     /// Specifies criticality when passed to log(). Functions as a criticality
70     /// threshold when returned from LevelThreshold::get().
71     ///
72     ///     error   Be silent unless when there is an error.
73     ///     warn    Be silent unless when there is an error or a warning.
74     ///     info    Reveal information about what is going on, but in a
75     ///             minimalistic fashion to avoid general overhead from logging
76     ///             and to keep volume down.
77     ///     detail  Same as 'info', but prioritize completeness over minimalism.
78     ///     debug   Reveal information that can aid debugging, no longer paying
79     ///             attention to efficiency.
80     ///     trace   A version of 'debug' that allows for very high volume
81     ///             output.
82     enum class Level { all, trace, debug, detail, info, warn, error, fatal, off };
83
84     template <class... Params>
85     void log(Level, const char* message, Params&&...);
86
87     /// Shorthand for `int(level) >= int(level_threshold.get())`.
88     bool would_log(Level level) const noexcept;
89
90     class LevelThreshold;
91
92     const LevelThreshold& level_threshold;
93
94     virtual ~Logger() noexcept;
95
96 protected:
97     Logger(const LevelThreshold&) noexcept;
98
99     static void do_log(Logger&, Level, std::string message);
100
101     virtual void do_log(Level, std::string message) = 0;
102
103     static const char* get_level_prefix(Level) noexcept;
104
105 private:
106     struct State;
107
108     template <class... Params>
109     REALM_NOINLINE void do_log(Level, const char* message, Params&&...);
110     void log_impl(State&);
111     template <class Param, class... Params>
112     void log_impl(State&, Param&&, Params&&...);
113     template <class Param>
114     static void subst(State&, Param&&);
115 };
116
117 template <class C, class T>
118 std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>&, Logger::Level);
119
120 template <class C, class T>
121 std::basic_istream<C, T>& operator>>(std::basic_istream<C, T>&, Logger::Level&);
122
123 class Logger::LevelThreshold {
124 public:
125     virtual Level get() const noexcept = 0;
126 };
127
128
129 /// A root logger that is not thread-safe and allows for the log level threshold
130 /// to be changed over time. The initial log level threshold is
131 /// Logger::Level::info.
132 class RootLogger : private Logger::LevelThreshold, public Logger {
133 public:
134     void set_level_threshold(Level) noexcept;
135
136 protected:
137     RootLogger();
138
139 private:
140     Level m_level_threshold = Level::info;
141     Level get() const noexcept override final;
142 };
143
144
145 /// A logger that writes to STDERR. This logger is not thread-safe.
146 ///
147 /// Since this class is a RootLogger, it contains modifiable a log level
148 /// threshold.
149 class StderrLogger : public RootLogger {
150 protected:
151     void do_log(Level, std::string) override final;
152 };
153
154
155 /// A logger that writes to a stream. This logger is not thread-safe.
156 ///
157 /// Since this class is a RootLogger, it contains modifiable a log level
158 /// threshold.
159 class StreamLogger : public RootLogger {
160 public:
161     explicit StreamLogger(std::ostream&) noexcept;
162
163 protected:
164     void do_log(Level, std::string) override final;
165
166 private:
167     std::ostream& m_out;
168 };
169
170
171 /// A logger that writes to a file. This logger is not thread-safe.
172 ///
173 /// Since this class is a RootLogger, it contains modifiable a log level
174 /// threshold.
175 class FileLogger : public StreamLogger {
176 public:
177     explicit FileLogger(std::string path);
178     explicit FileLogger(util::File);
179
180 private:
181     util::File m_file;
182     util::File::Streambuf m_streambuf;
183     std::ostream m_out;
184 };
185
186
187 /// A thread-safe logger. This logger ignores the level threshold of the base
188 /// logger. Instead, it introduces new a LevelThreshold object with a fixed
189 /// value to achieve thread safety.
190 class ThreadSafeLogger : private Logger::LevelThreshold, public Logger {
191 public:
192     explicit ThreadSafeLogger(Logger& base_logger, Level = Level::info);
193
194 protected:
195     void do_log(Level, std::string) override final;
196
197 private:
198     const Level m_level_threshold; // Immutable for thread safety
199     Logger& m_base_logger;
200     Mutex m_mutex;
201     Level get() const noexcept override final;
202 };
203
204
205 /// A logger that adds a fixed prefix to each message. This logger inherits the
206 /// LevelThreshold object of the specified base logger. This logger is
207 /// thread-safe if, and only if the base logger is thread-safe.
208 class PrefixLogger : public Logger {
209 public:
210     PrefixLogger(std::string prefix, Logger& base_logger) noexcept;
211
212 protected:
213     void do_log(Level, std::string) override final;
214
215 private:
216     const std::string m_prefix;
217     Logger& m_base_logger;
218 };
219
220
221 // Implementation
222
223 struct Logger::State {
224     Logger::Level m_level;
225     std::string m_message;
226     std::string m_search;
227     int m_param_num = 1;
228     std::ostringstream m_formatter;
229     std::locale m_locale = std::locale::classic();
230     State(Logger::Level level, const char* s)
231         : m_level(level)
232         , m_message(s)
233         , m_search(m_message)
234     {
235         m_formatter.imbue(m_locale);
236     }
237 };
238
239 template <class... Params>
240 inline void Logger::trace(const char* message, Params&&... params)
241 {
242     log(Level::trace, message, std::forward<Params>(params)...); // Throws
243 }
244
245 template <class... Params>
246 inline void Logger::debug(const char* message, Params&&... params)
247 {
248     log(Level::debug, message, std::forward<Params>(params)...); // Throws
249 }
250
251 template <class... Params>
252 inline void Logger::detail(const char* message, Params&&... params)
253 {
254     log(Level::detail, message, std::forward<Params>(params)...); // Throws
255 }
256
257 template <class... Params>
258 inline void Logger::info(const char* message, Params&&... params)
259 {
260     log(Level::info, message, std::forward<Params>(params)...); // Throws
261 }
262
263 template <class... Params>
264 inline void Logger::warn(const char* message, Params&&... params)
265 {
266     log(Level::warn, message, std::forward<Params>(params)...); // Throws
267 }
268
269 template <class... Params>
270 inline void Logger::error(const char* message, Params&&... params)
271 {
272     log(Level::error, message, std::forward<Params>(params)...); // Throws
273 }
274
275 template <class... Params>
276 inline void Logger::fatal(const char* message, Params&&... params)
277 {
278     log(Level::fatal, message, std::forward<Params>(params)...); // Throws
279 }
280
281 template <class... Params>
282 inline void Logger::log(Level level, const char* message, Params&&... params)
283 {
284     if (would_log(level))
285         do_log(level, message, std::forward<Params>(params)...); // Throws
286 }
287
288 inline bool Logger::would_log(Level level) const noexcept
289 {
290     return int(level) >= int(level_threshold.get());
291 }
292
293 inline Logger::~Logger() noexcept
294 {
295 }
296
297 inline Logger::Logger(const LevelThreshold& lt) noexcept
298     : level_threshold(lt)
299 {
300 }
301
302 inline void Logger::do_log(Logger& logger, Level level, std::string message)
303 {
304     logger.do_log(level, std::move(message)); // Throws
305 }
306
307 template <class... Params>
308 void Logger::do_log(Level level, const char* message, Params&&... params)
309 {
310     State state(level, message);
311     log_impl(state, std::forward<Params>(params)...); // Throws
312 }
313
314 inline void Logger::log_impl(State& state)
315 {
316     do_log(state.m_level, std::move(state.m_message)); // Throws
317 }
318
319 template <class Param, class... Params>
320 inline void Logger::log_impl(State& state, Param&& param, Params&&... params)
321 {
322     subst(state, std::forward<Param>(param));         // Throws
323     log_impl(state, std::forward<Params>(params)...); // Throws
324 }
325
326 template <class Param>
327 void Logger::subst(State& state, Param&& param)
328 {
329     state.m_formatter << "%" << state.m_param_num;
330     std::string key = state.m_formatter.str();
331     state.m_formatter.str(std::string());
332     std::string::size_type j = state.m_search.find(key);
333     if (j != std::string::npos) {
334         state.m_formatter << std::forward<Param>(param);
335         std::string str = state.m_formatter.str();
336         state.m_formatter.str(std::string());
337         state.m_message.replace(j, key.size(), str);
338         state.m_search.replace(j, key.size(), std::string(str.size(), '\0'));
339     }
340     ++state.m_param_num;
341 }
342
343 template <class C, class T>
344 std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& out, Logger::Level level)
345 {
346     switch (level) {
347         case Logger::Level::all:
348             out << "all";
349             return out;
350         case Logger::Level::trace:
351             out << "trace";
352             return out;
353         case Logger::Level::debug:
354             out << "debug";
355             return out;
356         case Logger::Level::detail:
357             out << "detail";
358             return out;
359         case Logger::Level::info:
360             out << "info";
361             return out;
362         case Logger::Level::warn:
363             out << "warn";
364             return out;
365         case Logger::Level::error:
366             out << "error";
367             return out;
368         case Logger::Level::fatal:
369             out << "fatal";
370             return out;
371         case Logger::Level::off:
372             out << "off";
373             return out;
374     }
375     REALM_ASSERT(false);
376     return out;
377 }
378
379 template <class C, class T>
380 std::basic_istream<C, T>& operator>>(std::basic_istream<C, T>& in, Logger::Level& level)
381 {
382     std::basic_string<C, T> str;
383     auto check = [&](const char* name) {
384         size_t n = strlen(name);
385         if (n != str.size())
386             return false;
387         for (size_t i = 0; i < n; ++i) {
388             if (in.widen(name[i]) != str[i])
389                 return false;
390         }
391         return true;
392     };
393     if (in >> str) {
394         if (check("all")) {
395             level = Logger::Level::all;
396         }
397         else if (check("trace")) {
398             level = Logger::Level::trace;
399         }
400         else if (check("debug")) {
401             level = Logger::Level::debug;
402         }
403         else if (check("detail")) {
404             level = Logger::Level::detail;
405         }
406         else if (check("info")) {
407             level = Logger::Level::info;
408         }
409         else if (check("warn")) {
410             level = Logger::Level::warn;
411         }
412         else if (check("error")) {
413             level = Logger::Level::error;
414         }
415         else if (check("fatal")) {
416             level = Logger::Level::fatal;
417         }
418         else if (check("off")) {
419             level = Logger::Level::off;
420         }
421         else {
422             in.setstate(std::ios_base::failbit);
423         }
424     }
425     return in;
426 }
427
428 inline void RootLogger::set_level_threshold(Level new_level_threshold) noexcept
429 {
430     m_level_threshold = new_level_threshold;
431 }
432
433 inline RootLogger::RootLogger()
434     : Logger::LevelThreshold()
435     , Logger(static_cast<Logger::LevelThreshold&>(*this))
436 {
437 }
438
439 inline Logger::Level RootLogger::get() const noexcept
440 {
441     return m_level_threshold;
442 }
443
444 inline void StderrLogger::do_log(Level level, std::string message)
445 {
446     std::cerr << get_level_prefix(level) << message << '\n'; // Throws
447     std::cerr.flush();                                       // Throws
448 }
449
450 inline StreamLogger::StreamLogger(std::ostream& out) noexcept
451     : m_out(out)
452 {
453 }
454
455 inline void StreamLogger::do_log(Level level, std::string message)
456 {
457     m_out << get_level_prefix(level) << message << '\n'; // Throws
458     m_out.flush();                                       // Throws
459 }
460
461 inline FileLogger::FileLogger(std::string path)
462     : StreamLogger(m_out)
463     , m_file(path, util::File::mode_Write) // Throws
464     , m_streambuf(&m_file)                 // Throws
465     , m_out(&m_streambuf)                  // Throws
466 {
467 }
468
469 inline FileLogger::FileLogger(util::File file)
470     : StreamLogger(m_out)
471     , m_file(std::move(file))
472     , m_streambuf(&m_file) // Throws
473     , m_out(&m_streambuf)  // Throws
474 {
475 }
476
477 inline ThreadSafeLogger::ThreadSafeLogger(Logger& base_logger, Level threshold)
478     : Logger::LevelThreshold()
479     , Logger(static_cast<Logger::LevelThreshold&>(*this))
480     , m_level_threshold(threshold)
481     , m_base_logger(base_logger)
482 {
483 }
484
485 inline void ThreadSafeLogger::do_log(Level level, std::string message)
486 {
487     LockGuard l(m_mutex);
488     Logger::do_log(m_base_logger, level, message); // Throws
489 }
490
491 inline Logger::Level ThreadSafeLogger::get() const noexcept
492 {
493     return m_level_threshold;
494 }
495
496 inline PrefixLogger::PrefixLogger(std::string prefix, Logger& base_logger) noexcept
497     : Logger(base_logger.level_threshold)
498     , m_prefix(std::move(prefix))
499     , m_base_logger(base_logger)
500 {
501 }
502
503 inline void PrefixLogger::do_log(Level level, std::string message)
504 {
505     Logger::do_log(m_base_logger, level, m_prefix + message); // Throws
506 }
507
508 } // namespace util
509 } // namespace realm
510
511 #endif // REALM_UTIL_LOGGER_HPP