logger.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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. #ifndef REALM_UTIL_LOGGER_HPP
  19. #define REALM_UTIL_LOGGER_HPP
  20. #include <realm/util/features.h>
  21. #include <realm/util/thread.hpp>
  22. #include <realm/util/file.hpp>
  23. #include <cstring>
  24. #include <ostream>
  25. #include <string>
  26. #include <utility>
  27. namespace realm::util {
  28. /// All Logger objects store a reference to a LevelThreshold object which it
  29. /// uses to efficiently query about the current log level threshold
  30. /// (`level_threshold.get()`). All messages logged with a level that is lower
  31. /// than the current threshold will be dropped. For the sake of efficiency, this
  32. /// test happens before the message is formatted.
  33. ///
  34. /// A logger is not inherently thread-safe, but specific implementations can be
  35. /// (see ThreadSafeLogger). For a logger to be thread-safe, the implementation
  36. /// of do_log() must be thread-safe and the referenced LevelThreshold object
  37. /// must have a thread-safe get() method.
  38. ///
  39. /// Examples:
  40. ///
  41. /// logger.error("Overlong message from master coordinator");
  42. /// logger.info("Listening for peers on %1:%2", listen_address, listen_port);
  43. class Logger {
  44. public:
  45. template <class... Params>
  46. void trace(const char* message, Params&&...);
  47. template <class... Params>
  48. void debug(const char* message, Params&&...);
  49. template <class... Params>
  50. void detail(const char* message, Params&&...);
  51. template <class... Params>
  52. void info(const char* message, Params&&...);
  53. template <class... Params>
  54. void warn(const char* message, Params&&...);
  55. template <class... Params>
  56. void error(const char* message, Params&&...);
  57. template <class... Params>
  58. void fatal(const char* message, Params&&...);
  59. /// Specifies criticality when passed to log(). Functions as a criticality
  60. /// threshold when returned from LevelThreshold::get().
  61. ///
  62. /// error Be silent unless when there is an error.
  63. /// warn Be silent unless when there is an error or a warning.
  64. /// info Reveal information about what is going on, but in a
  65. /// minimalistic fashion to avoid general overhead from logging
  66. /// and to keep volume down.
  67. /// detail Same as 'info', but prioritize completeness over minimalism.
  68. /// debug Reveal information that can aid debugging, no longer paying
  69. /// attention to efficiency.
  70. /// trace A version of 'debug' that allows for very high volume
  71. /// output.
  72. enum class Level { all, trace, debug, detail, info, warn, error, fatal, off };
  73. template <class... Params>
  74. void log(Level, const char* message, Params&&...);
  75. /// Shorthand for `int(level) >= int(level_threshold.get())`.
  76. bool would_log(Level level) const noexcept;
  77. class LevelThreshold;
  78. const LevelThreshold& level_threshold;
  79. virtual ~Logger() noexcept;
  80. protected:
  81. Logger(const LevelThreshold&) noexcept;
  82. static void do_log(Logger&, Level, const std::string& message);
  83. virtual void do_log(Level, const std::string& message) = 0;
  84. static const char* get_level_prefix(Level) noexcept;
  85. private:
  86. template <class... Params>
  87. REALM_NOINLINE void do_log(Level, const char* message, Params&&...);
  88. };
  89. template <class C, class T>
  90. std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>&, Logger::Level);
  91. template <class C, class T>
  92. std::basic_istream<C, T>& operator>>(std::basic_istream<C, T>&, Logger::Level&);
  93. class Logger::LevelThreshold {
  94. public:
  95. virtual Level get() const noexcept = 0;
  96. };
  97. /// A root logger that is not thread-safe and allows for the log level threshold
  98. /// to be changed over time. The initial log level threshold is
  99. /// Logger::Level::info.
  100. class RootLogger : private Logger::LevelThreshold, public Logger {
  101. public:
  102. void set_level_threshold(Level) noexcept;
  103. protected:
  104. RootLogger();
  105. private:
  106. Level m_level_threshold = Level::info;
  107. Level get() const noexcept override final;
  108. };
  109. /// A logger that writes to STDERR. This logger is not thread-safe.
  110. ///
  111. /// Since this class is a RootLogger, it contains modifiable a log level
  112. /// threshold.
  113. class StderrLogger : public RootLogger {
  114. protected:
  115. void do_log(Level, const std::string&) override final;
  116. };
  117. /// A logger that writes to a stream. This logger is not thread-safe.
  118. ///
  119. /// Since this class is a RootLogger, it contains modifiable a log level
  120. /// threshold.
  121. class StreamLogger : public RootLogger {
  122. public:
  123. explicit StreamLogger(std::ostream&) noexcept;
  124. protected:
  125. void do_log(Level, const std::string&) override final;
  126. private:
  127. std::ostream& m_out;
  128. };
  129. /// A logger that writes to a file. This logger is not thread-safe.
  130. ///
  131. /// Since this class is a RootLogger, it contains modifiable a log level
  132. /// threshold.
  133. class FileLogger : public StreamLogger {
  134. public:
  135. explicit FileLogger(std::string path);
  136. explicit FileLogger(util::File);
  137. private:
  138. util::File m_file;
  139. util::File::Streambuf m_streambuf;
  140. std::ostream m_out;
  141. };
  142. class AppendToFileLogger : public StreamLogger {
  143. public:
  144. explicit AppendToFileLogger(std::string path);
  145. explicit AppendToFileLogger(util::File);
  146. private:
  147. util::File m_file;
  148. util::File::Streambuf m_streambuf;
  149. std::ostream m_out;
  150. };
  151. /// A thread-safe logger. This logger ignores the level threshold of the base
  152. /// logger. Instead, it introduces new a LevelThreshold object with a fixed
  153. /// value to achieve thread safety.
  154. class ThreadSafeLogger : private Logger::LevelThreshold, public Logger {
  155. public:
  156. explicit ThreadSafeLogger(Logger& base_logger, Level = Level::info);
  157. protected:
  158. void do_log(Level, const std::string&) override final;
  159. private:
  160. const Level m_level_threshold; // Immutable for thread safety
  161. Logger& m_base_logger;
  162. Mutex m_mutex;
  163. Level get() const noexcept override final;
  164. };
  165. /// A logger that adds a fixed prefix to each message. This logger inherits the
  166. /// LevelThreshold object of the specified base logger. This logger is
  167. /// thread-safe if, and only if the base logger is thread-safe.
  168. class PrefixLogger : public Logger {
  169. public:
  170. PrefixLogger(std::string prefix, Logger& base_logger) noexcept;
  171. protected:
  172. void do_log(Level, const std::string&) override final;
  173. private:
  174. const std::string m_prefix;
  175. Logger& m_base_logger;
  176. };
  177. // Implementation
  178. template <class... Params>
  179. inline void Logger::trace(const char* message, Params&&... params)
  180. {
  181. log(Level::trace, message, std::forward<Params>(params)...); // Throws
  182. }
  183. template <class... Params>
  184. inline void Logger::debug(const char* message, Params&&... params)
  185. {
  186. log(Level::debug, message, std::forward<Params>(params)...); // Throws
  187. }
  188. template <class... Params>
  189. inline void Logger::detail(const char* message, Params&&... params)
  190. {
  191. log(Level::detail, message, std::forward<Params>(params)...); // Throws
  192. }
  193. template <class... Params>
  194. inline void Logger::info(const char* message, Params&&... params)
  195. {
  196. log(Level::info, message, std::forward<Params>(params)...); // Throws
  197. }
  198. template <class... Params>
  199. inline void Logger::warn(const char* message, Params&&... params)
  200. {
  201. log(Level::warn, message, std::forward<Params>(params)...); // Throws
  202. }
  203. template <class... Params>
  204. inline void Logger::error(const char* message, Params&&... params)
  205. {
  206. log(Level::error, message, std::forward<Params>(params)...); // Throws
  207. }
  208. template <class... Params>
  209. inline void Logger::fatal(const char* message, Params&&... params)
  210. {
  211. log(Level::fatal, message, std::forward<Params>(params)...); // Throws
  212. }
  213. template <class... Params>
  214. inline void Logger::log(Level level, const char* message, Params&&... params)
  215. {
  216. if (would_log(level))
  217. do_log(level, message, std::forward<Params>(params)...); // Throws
  218. #if REALM_DEBUG
  219. else {
  220. // Do the string formatting even if it won't be logged to hopefully
  221. // catch invalid format strings
  222. static_cast<void>(format(message, std::forward<Params>(params)...)); // Throws
  223. }
  224. #endif
  225. }
  226. inline bool Logger::would_log(Level level) const noexcept
  227. {
  228. return int(level) >= int(level_threshold.get());
  229. }
  230. inline Logger::~Logger() noexcept {}
  231. inline Logger::Logger(const LevelThreshold& lt) noexcept
  232. : level_threshold(lt)
  233. {
  234. }
  235. inline void Logger::do_log(Logger& logger, Level level, const std::string& message)
  236. {
  237. logger.do_log(level, std::move(message)); // Throws
  238. }
  239. template <class... Params>
  240. void Logger::do_log(Level level, const char* message, Params&&... params)
  241. {
  242. do_log(level, format(message, std::forward<Params>(params)...)); // Throws
  243. }
  244. template <class C, class T>
  245. std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& out, Logger::Level level)
  246. {
  247. switch (level) {
  248. case Logger::Level::all:
  249. out << "all";
  250. return out;
  251. case Logger::Level::trace:
  252. out << "trace";
  253. return out;
  254. case Logger::Level::debug:
  255. out << "debug";
  256. return out;
  257. case Logger::Level::detail:
  258. out << "detail";
  259. return out;
  260. case Logger::Level::info:
  261. out << "info";
  262. return out;
  263. case Logger::Level::warn:
  264. out << "warn";
  265. return out;
  266. case Logger::Level::error:
  267. out << "error";
  268. return out;
  269. case Logger::Level::fatal:
  270. out << "fatal";
  271. return out;
  272. case Logger::Level::off:
  273. out << "off";
  274. return out;
  275. }
  276. REALM_ASSERT(false);
  277. return out;
  278. }
  279. template <class C, class T>
  280. std::basic_istream<C, T>& operator>>(std::basic_istream<C, T>& in, Logger::Level& level)
  281. {
  282. std::basic_string<C, T> str;
  283. auto check = [&](const char* name) {
  284. size_t n = strlen(name);
  285. if (n != str.size())
  286. return false;
  287. for (size_t i = 0; i < n; ++i) {
  288. if (in.widen(name[i]) != str[i])
  289. return false;
  290. }
  291. return true;
  292. };
  293. if (in >> str) {
  294. if (check("all")) {
  295. level = Logger::Level::all;
  296. }
  297. else if (check("trace")) {
  298. level = Logger::Level::trace;
  299. }
  300. else if (check("debug")) {
  301. level = Logger::Level::debug;
  302. }
  303. else if (check("detail")) {
  304. level = Logger::Level::detail;
  305. }
  306. else if (check("info")) {
  307. level = Logger::Level::info;
  308. }
  309. else if (check("warn")) {
  310. level = Logger::Level::warn;
  311. }
  312. else if (check("error")) {
  313. level = Logger::Level::error;
  314. }
  315. else if (check("fatal")) {
  316. level = Logger::Level::fatal;
  317. }
  318. else if (check("off")) {
  319. level = Logger::Level::off;
  320. }
  321. else {
  322. in.setstate(std::ios_base::failbit);
  323. }
  324. }
  325. return in;
  326. }
  327. inline void RootLogger::set_level_threshold(Level new_level_threshold) noexcept
  328. {
  329. m_level_threshold = new_level_threshold;
  330. }
  331. inline RootLogger::RootLogger()
  332. : Logger::LevelThreshold()
  333. , Logger(static_cast<Logger::LevelThreshold&>(*this))
  334. {
  335. }
  336. inline Logger::Level RootLogger::get() const noexcept
  337. {
  338. return m_level_threshold;
  339. }
  340. inline StreamLogger::StreamLogger(std::ostream& out) noexcept
  341. : m_out(out)
  342. {
  343. }
  344. inline FileLogger::FileLogger(std::string path)
  345. : StreamLogger(m_out)
  346. , m_file(path, util::File::mode_Write) // Throws
  347. , m_streambuf(&m_file) // Throws
  348. , m_out(&m_streambuf) // Throws
  349. {
  350. }
  351. inline FileLogger::FileLogger(util::File file)
  352. : StreamLogger(m_out)
  353. , m_file(std::move(file))
  354. , m_streambuf(&m_file) // Throws
  355. , m_out(&m_streambuf) // Throws
  356. {
  357. }
  358. inline AppendToFileLogger::AppendToFileLogger(std::string path)
  359. : StreamLogger(m_out)
  360. , m_file(path, util::File::mode_Append) // Throws
  361. , m_streambuf(&m_file) // Throws
  362. , m_out(&m_streambuf) // Throws
  363. {
  364. }
  365. inline AppendToFileLogger::AppendToFileLogger(util::File file)
  366. : StreamLogger(m_out)
  367. , m_file(std::move(file))
  368. , m_streambuf(&m_file) // Throws
  369. , m_out(&m_streambuf) // Throws
  370. {
  371. }
  372. inline ThreadSafeLogger::ThreadSafeLogger(Logger& base_logger, Level threshold)
  373. : Logger::LevelThreshold()
  374. , Logger(static_cast<Logger::LevelThreshold&>(*this))
  375. , m_level_threshold(threshold)
  376. , m_base_logger(base_logger)
  377. {
  378. }
  379. inline Logger::Level ThreadSafeLogger::get() const noexcept
  380. {
  381. return m_level_threshold;
  382. }
  383. inline PrefixLogger::PrefixLogger(std::string prefix, Logger& base_logger) noexcept
  384. : Logger(base_logger.level_threshold)
  385. , m_prefix(std::move(prefix))
  386. , m_base_logger(base_logger)
  387. {
  388. }
  389. } // namespace realm::util
  390. #endif // REALM_UTIL_LOGGER_HPP