1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #pragma once
8 
9 /*
10  * Simple logging.
11  *
12  * Predefined log levels: FATAL, ERROR, WARNING, INFO, DEBUG
13  *
14  * LOG(WARNING) << "Hello world!";
15  * LOG(INFO) << "Hello " << 1234 << " world!";
16  * LOG_IF(INFO, condition) << "Hello world if condition!";
17  *
18  * Custom log levels may be defined and used using VLOG:
19  * int VERBOSITY_NAME(custom) = VERBOSITY_NAME(WARNING);
20  * VLOG(custom) << "Hello custom world!"
21  *
22  * LOG(FATAL) << "Power is off";
23  * CHECK(condition) <===> LOG_IF(FATAL, !(condition))
24  */
25 
26 #include "td/utils/common.h"
27 #include "td/utils/port/thread_local.h"
28 #include "td/utils/Slice.h"
29 #include "td/utils/StackAllocator.h"
30 #include "td/utils/StringBuilder.h"
31 
32 #include <atomic>
33 #include <type_traits>
34 
35 #define VERBOSITY_NAME(x) verbosity_##x
36 
37 #define GET_VERBOSITY_LEVEL() (::td::get_verbosity_level())
38 #define SET_VERBOSITY_LEVEL(new_level) (::td::set_verbosity_level(new_level))
39 
40 #ifndef STRIP_LOG
41 #define STRIP_LOG VERBOSITY_NAME(DEBUG)
42 #endif
43 #define LOG_IS_STRIPPED(strip_level) \
44   (::std::integral_constant<int, VERBOSITY_NAME(strip_level)>() > ::std::integral_constant<int, STRIP_LOG>())
45 
46 #define LOGGER(interface, options, level, comment) ::td::Logger(interface, options, level, __FILE__, __LINE__, comment)
47 
48 #define LOG_IMPL_FULL(interface, options, strip_level, runtime_level, condition, comment) \
49   LOG_IS_STRIPPED(strip_level) || runtime_level > options.get_level() || !(condition)     \
50       ? (void)0                                                                           \
51       : ::td::detail::Voidify() & LOGGER(interface, options, runtime_level, comment)
52 
53 #define LOG_IMPL(strip_level, level, condition, comment) \
54   LOG_IMPL_FULL(*::td::log_interface, ::td::log_options, strip_level, VERBOSITY_NAME(level), condition, comment)
55 
56 #define LOG(level) LOG_IMPL(level, level, true, ::td::Slice())
57 #define LOG_IF(level, condition) LOG_IMPL(level, level, condition, #condition)
58 
59 #define VLOG(level) LOG_IMPL(DEBUG, level, true, TD_DEFINE_STR(level))
60 #define VLOG_IF(level, condition) LOG_IMPL(DEBUG, level, condition, TD_DEFINE_STR(level) " " #condition)
61 
62 #define LOG_TAG ::td::Logger::tag_
63 #define LOG_TAG2 ::td::Logger::tag2_
64 
65 #if TD_CLANG
66 bool no_return_func() __attribute__((analyzer_noreturn));
67 #endif
68 
no_return_func()69 inline bool no_return_func() {
70   return true;
71 }
72 
73 // clang-format off
74 #define DUMMY_LOG_CHECK(condition) LOG_IF(NEVER, !(condition))
75 
76 #ifdef TD_DEBUG
77   #if TD_MSVC
78     #define LOG_CHECK(condition)        \
79       __analysis_assume(!!(condition)); \
80       LOG_IMPL(FATAL, FATAL, !(condition), #condition)
81   #else
82     #define LOG_CHECK(condition) LOG_IMPL(FATAL, FATAL, !(condition) && no_return_func(), #condition)
83   #endif
84 #else
85   #define LOG_CHECK DUMMY_LOG_CHECK
86 #endif
87 
88 #if NDEBUG
89   #define LOG_DCHECK DUMMY_LOG_CHECK
90 #else
91   #define LOG_DCHECK LOG_CHECK
92 #endif
93 // clang-format on
94 
95 constexpr int VERBOSITY_NAME(PLAIN) = -1;
96 constexpr int VERBOSITY_NAME(FATAL) = 0;
97 constexpr int VERBOSITY_NAME(ERROR) = 1;
98 constexpr int VERBOSITY_NAME(WARNING) = 2;
99 constexpr int VERBOSITY_NAME(INFO) = 3;
100 constexpr int VERBOSITY_NAME(DEBUG) = 4;
101 constexpr int VERBOSITY_NAME(NEVER) = 1024;
102 
103 namespace td {
104 
105 struct LogOptions {
106   std::atomic<int> level{VERBOSITY_NAME(DEBUG) + 1};
107   bool fix_newlines{true};
108   bool add_info{true};
109 
get_levelLogOptions110   int get_level() const {
111     return level.load(std::memory_order_relaxed);
112   }
set_levelLogOptions113   int set_level(int new_level) {
114     return level.exchange(new_level);
115   }
116 
plainLogOptions117   static const LogOptions &plain() {
118     static LogOptions plain_options{0, false, false};
119     return plain_options;
120   }
121 
122   constexpr LogOptions() = default;
LogOptionsLogOptions123   constexpr LogOptions(int level, bool fix_newlines, bool add_info)
124       : level(level), fix_newlines(fix_newlines), add_info(add_info) {
125   }
126 
LogOptionsLogOptions127   LogOptions(const LogOptions &other) : LogOptions(other.level.load(), other.fix_newlines, other.add_info) {
128   }
129 
130   LogOptions &operator=(const LogOptions &other) {
131     if (this == &other) {
132       return *this;
133     }
134     level = other.level.load();
135     fix_newlines = other.fix_newlines;
136     add_info = other.add_info;
137     return *this;
138   }
139   LogOptions(LogOptions &&) = delete;
140   LogOptions &operator=(LogOptions &&) = delete;
141   ~LogOptions() = default;
142 };
143 
144 extern LogOptions log_options;
set_verbosity_level(int level)145 inline int set_verbosity_level(int level) {
146   return log_options.set_level(level);
147 }
get_verbosity_level()148 inline int get_verbosity_level() {
149   return log_options.get_level();
150 }
151 
152 class LogInterface {
153  public:
154   LogInterface() = default;
155   LogInterface(const LogInterface &) = delete;
156   LogInterface &operator=(const LogInterface &) = delete;
157   LogInterface(LogInterface &&) = delete;
158   LogInterface &operator=(LogInterface &&) = delete;
159   virtual ~LogInterface() = default;
160 
161   void append(int log_level, CSlice slice);
162 
after_rotation()163   virtual void after_rotation() {
164   }
165 
get_file_paths()166   virtual vector<string> get_file_paths() {
167     return {};
168   }
169 
170   virtual void do_append(int log_level, CSlice slice) = 0;
171 };
172 
173 extern LogInterface *const default_log_interface;
174 extern LogInterface *log_interface;
175 
176 [[noreturn]] void process_fatal_error(CSlice message);
177 
178 using OnLogMessageCallback = void (*)(int verbosity_level, CSlice message);
179 void set_log_message_callback(int max_verbosity_level, OnLogMessageCallback callback);
180 
181 class Logger {
182   static const size_t BUFFER_SIZE = 128 * 1024;
183 
184  public:
Logger(LogInterface & log,const LogOptions & options,int log_level)185   Logger(LogInterface &log, const LogOptions &options, int log_level)
186       : buffer_(StackAllocator::alloc(BUFFER_SIZE))
187       , log_(log)
188       , sb_(buffer_.as_slice())
189       , options_(options)
190       , log_level_(log_level) {
191   }
192 
193   Logger(LogInterface &log, const LogOptions &options, int log_level, Slice file_name, int line_num, Slice comment);
194 
195   template <class T>
196   Logger &operator<<(T &&other) {
197     sb_ << other;
198     return *this;
199   }
200 
as_cslice()201   MutableCSlice as_cslice() {
202     return sb_.as_cslice();
203   }
is_error()204   bool is_error() const {
205     return sb_.is_error();
206   }
207   Logger(const Logger &) = delete;
208   Logger &operator=(const Logger &) = delete;
209   Logger(Logger &&) = delete;
210   Logger &operator=(Logger &&) = delete;
211   ~Logger();
212 
213   static TD_THREAD_LOCAL const char *tag_;
214   static TD_THREAD_LOCAL const char *tag2_;
215 
216  private:
217   decltype(StackAllocator::alloc(0)) buffer_;
218   LogInterface &log_;
219   StringBuilder sb_;
220   const LogOptions &options_;
221   int log_level_;
222 };
223 
224 class ScopedDisableLog {
225  public:
226   ScopedDisableLog();
227   ScopedDisableLog(const ScopedDisableLog &) = delete;
228   ScopedDisableLog &operator=(const ScopedDisableLog &) = delete;
229   ScopedDisableLog(ScopedDisableLog &&) = delete;
230   ScopedDisableLog &operator=(ScopedDisableLog &&) = delete;
231   ~ScopedDisableLog();
232 };
233 
234 namespace detail {
235 class Voidify {
236  public:
237   template <class T>
238   void operator&(const T &) {
239   }
240 };
241 }  // namespace detail
242 
243 }  // namespace td
244