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