1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. |
2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) |
3 | |
4 | #pragma once |
5 | |
6 | // Thread safe logger (except for set_error_handler()) |
7 | // Has name, log level, vector of std::shared sink pointers and formatter |
8 | // Upon each log write the logger: |
9 | // 1. Checks if its log level is enough to log the message and if yes: |
10 | // 2. Call the underlying sinks to do the job. |
11 | // 3. Each sink use its own private copy of a formatter to format the message |
12 | // and send to its destination. |
13 | // |
14 | // The use of private formatter per sink provides the opportunity to cache some |
15 | // formatted data, and support for different format per sink. |
16 | |
17 | #include <spdlog/common.h> |
18 | #include <spdlog/details/log_msg.h> |
19 | #include <spdlog/details/backtracer.h> |
20 | |
21 | #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT |
22 | # ifndef _WIN32 |
23 | # error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows |
24 | # endif |
25 | # include <spdlog/details/os.h> |
26 | #endif |
27 | |
28 | #include <vector> |
29 | |
30 | #ifndef SPDLOG_NO_EXCEPTIONS |
31 | # define SPDLOG_LOGGER_CATCH(location) \ |
32 | catch (const std::exception &ex) \ |
33 | { \ |
34 | if (location.filename) \ |
35 | { \ |
36 | err_handler_(fmt_lib::format(SPDLOG_FMT_STRING("{} [{}({})]"), ex.what(), location.filename, location.line)); \ |
37 | } \ |
38 | else \ |
39 | { \ |
40 | err_handler_(ex.what()); \ |
41 | } \ |
42 | } \ |
43 | catch (...) \ |
44 | { \ |
45 | err_handler_("Rethrowing unknown exception in logger"); \ |
46 | throw; \ |
47 | } |
48 | #else |
49 | # define SPDLOG_LOGGER_CATCH(location) |
50 | #endif |
51 | |
52 | namespace spdlog { |
53 | |
54 | class SPDLOG_API logger |
55 | { |
56 | public: |
57 | // Empty logger |
58 | explicit logger(std::string name) |
59 | : name_(std::move(name)) |
60 | , sinks_() |
61 | {} |
62 | |
63 | // Logger with range on sinks |
64 | template<typename It> |
65 | logger(std::string name, It begin, It end) |
66 | : name_(std::move(name)) |
67 | , sinks_(begin, end) |
68 | {} |
69 | |
70 | // Logger with single sink |
71 | logger(std::string name, sink_ptr single_sink) |
72 | : logger(std::move(name), {std::move(single_sink)}) |
73 | {} |
74 | |
75 | // Logger with sinks init list |
76 | logger(std::string name, sinks_init_list sinks) |
77 | : logger(std::move(name), sinks.begin(), sinks.end()) |
78 | {} |
79 | |
80 | virtual ~logger() = default; |
81 | |
82 | logger(const logger &other); |
83 | logger(logger &&other) SPDLOG_NOEXCEPT; |
84 | logger &operator=(logger other) SPDLOG_NOEXCEPT; |
85 | void swap(spdlog::logger &other) SPDLOG_NOEXCEPT; |
86 | |
87 | template<typename... Args> |
88 | void log(source_loc loc, level::level_enum lvl, format_string_t<Args...> fmt, Args &&... args) |
89 | { |
90 | log_(loc, lvl, details::to_string_view(fmt), std::forward<Args>(args)...); |
91 | } |
92 | |
93 | template<typename... Args> |
94 | void log(level::level_enum lvl, format_string_t<Args...> fmt, Args &&... args) |
95 | { |
96 | log(source_loc{}, lvl, fmt, std::forward<Args>(args)...); |
97 | } |
98 | |
99 | template<typename T> |
100 | void log(level::level_enum lvl, const T &msg) |
101 | { |
102 | log(source_loc{}, lvl, msg); |
103 | } |
104 | |
105 | // T cannot be statically converted to format string (including string_view/wstring_view) |
106 | template<class T, typename std::enable_if<!is_convertible_to_any_format_string<const T &>::value, int>::type = 0> |
107 | void log(source_loc loc, level::level_enum lvl, const T &msg) |
108 | { |
109 | log(loc, lvl, "{}" , msg); |
110 | } |
111 | |
112 | void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, string_view_t msg) |
113 | { |
114 | bool log_enabled = should_log(lvl); |
115 | bool traceback_enabled = tracer_.enabled(); |
116 | if (!log_enabled && !traceback_enabled) |
117 | { |
118 | return; |
119 | } |
120 | |
121 | details::log_msg log_msg(log_time, loc, name_, lvl, msg); |
122 | log_it_(log_msg, log_enabled, traceback_enabled); |
123 | } |
124 | |
125 | void log(source_loc loc, level::level_enum lvl, string_view_t msg) |
126 | { |
127 | bool log_enabled = should_log(lvl); |
128 | bool traceback_enabled = tracer_.enabled(); |
129 | if (!log_enabled && !traceback_enabled) |
130 | { |
131 | return; |
132 | } |
133 | |
134 | details::log_msg log_msg(loc, name_, lvl, msg); |
135 | log_it_(log_msg, log_enabled, traceback_enabled); |
136 | } |
137 | |
138 | void log(level::level_enum lvl, string_view_t msg) |
139 | { |
140 | log(source_loc{}, lvl, msg); |
141 | } |
142 | |
143 | template<typename... Args> |
144 | void trace(format_string_t<Args...> fmt, Args &&... args) |
145 | { |
146 | log(level::trace, fmt, std::forward<Args>(args)...); |
147 | } |
148 | |
149 | template<typename... Args> |
150 | void debug(format_string_t<Args...> fmt, Args &&... args) |
151 | { |
152 | log(level::debug, fmt, std::forward<Args>(args)...); |
153 | } |
154 | |
155 | template<typename... Args> |
156 | void info(format_string_t<Args...> fmt, Args &&... args) |
157 | { |
158 | log(level::info, fmt, std::forward<Args>(args)...); |
159 | } |
160 | |
161 | template<typename... Args> |
162 | void warn(format_string_t<Args...> fmt, Args &&... args) |
163 | { |
164 | log(level::warn, fmt, std::forward<Args>(args)...); |
165 | } |
166 | |
167 | template<typename... Args> |
168 | void error(format_string_t<Args...> fmt, Args &&... args) |
169 | { |
170 | log(level::err, fmt, std::forward<Args>(args)...); |
171 | } |
172 | |
173 | template<typename... Args> |
174 | void critical(format_string_t<Args...> fmt, Args &&... args) |
175 | { |
176 | log(level::critical, fmt, std::forward<Args>(args)...); |
177 | } |
178 | |
179 | #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT |
180 | template<typename... Args> |
181 | void log(source_loc loc, level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&... args) |
182 | { |
183 | log_(loc, lvl, details::to_string_view(fmt), std::forward<Args>(args)...); |
184 | } |
185 | |
186 | template<typename... Args> |
187 | void log(level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&... args) |
188 | { |
189 | log(source_loc{}, lvl, fmt, std::forward<Args>(args)...); |
190 | } |
191 | |
192 | void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, wstring_view_t msg) |
193 | { |
194 | bool log_enabled = should_log(lvl); |
195 | bool traceback_enabled = tracer_.enabled(); |
196 | if (!log_enabled && !traceback_enabled) |
197 | { |
198 | return; |
199 | } |
200 | |
201 | memory_buf_t buf; |
202 | details::os::wstr_to_utf8buf(wstring_view_t(msg.data(), msg.size()), buf); |
203 | details::log_msg log_msg(log_time, loc, name_, lvl, string_view_t(buf.data(), buf.size())); |
204 | log_it_(log_msg, log_enabled, traceback_enabled); |
205 | } |
206 | |
207 | void log(source_loc loc, level::level_enum lvl, wstring_view_t msg) |
208 | { |
209 | bool log_enabled = should_log(lvl); |
210 | bool traceback_enabled = tracer_.enabled(); |
211 | if (!log_enabled && !traceback_enabled) |
212 | { |
213 | return; |
214 | } |
215 | |
216 | memory_buf_t buf; |
217 | details::os::wstr_to_utf8buf(wstring_view_t(msg.data(), msg.size()), buf); |
218 | details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size())); |
219 | log_it_(log_msg, log_enabled, traceback_enabled); |
220 | } |
221 | |
222 | void log(level::level_enum lvl, wstring_view_t msg) |
223 | { |
224 | log(source_loc{}, lvl, msg); |
225 | } |
226 | |
227 | template<typename... Args> |
228 | void trace(wformat_string_t<Args...> fmt, Args &&... args) |
229 | { |
230 | log(level::trace, fmt, std::forward<Args>(args)...); |
231 | } |
232 | |
233 | template<typename... Args> |
234 | void debug(wformat_string_t<Args...> fmt, Args &&... args) |
235 | { |
236 | log(level::debug, fmt, std::forward<Args>(args)...); |
237 | } |
238 | |
239 | template<typename... Args> |
240 | void info(wformat_string_t<Args...> fmt, Args &&... args) |
241 | { |
242 | log(level::info, fmt, std::forward<Args>(args)...); |
243 | } |
244 | |
245 | template<typename... Args> |
246 | void warn(wformat_string_t<Args...> fmt, Args &&... args) |
247 | { |
248 | log(level::warn, fmt, std::forward<Args>(args)...); |
249 | } |
250 | |
251 | template<typename... Args> |
252 | void error(wformat_string_t<Args...> fmt, Args &&... args) |
253 | { |
254 | log(level::err, fmt, std::forward<Args>(args)...); |
255 | } |
256 | |
257 | template<typename... Args> |
258 | void critical(wformat_string_t<Args...> fmt, Args &&... args) |
259 | { |
260 | log(level::critical, fmt, std::forward<Args>(args)...); |
261 | } |
262 | #endif |
263 | |
264 | template<typename T> |
265 | void trace(const T &msg) |
266 | { |
267 | log(level::trace, msg); |
268 | } |
269 | |
270 | template<typename T> |
271 | void debug(const T &msg) |
272 | { |
273 | log(level::debug, msg); |
274 | } |
275 | |
276 | template<typename T> |
277 | void info(const T &msg) |
278 | { |
279 | log(level::info, msg); |
280 | } |
281 | |
282 | template<typename T> |
283 | void warn(const T &msg) |
284 | { |
285 | log(level::warn, msg); |
286 | } |
287 | |
288 | template<typename T> |
289 | void error(const T &msg) |
290 | { |
291 | log(level::err, msg); |
292 | } |
293 | |
294 | template<typename T> |
295 | void critical(const T &msg) |
296 | { |
297 | log(level::critical, msg); |
298 | } |
299 | |
300 | // return true logging is enabled for the given level. |
301 | bool should_log(level::level_enum msg_level) const |
302 | { |
303 | return msg_level >= level_.load(std::memory_order_relaxed); |
304 | } |
305 | |
306 | // return true if backtrace logging is enabled. |
307 | bool should_backtrace() const |
308 | { |
309 | return tracer_.enabled(); |
310 | } |
311 | |
312 | void set_level(level::level_enum log_level); |
313 | |
314 | level::level_enum level() const; |
315 | |
316 | const std::string &name() const; |
317 | |
318 | // set formatting for the sinks in this logger. |
319 | // each sink will get a separate instance of the formatter object. |
320 | void set_formatter(std::unique_ptr<formatter> f); |
321 | |
322 | // set formatting for the sinks in this logger. |
323 | // equivalent to |
324 | // set_formatter(make_unique<pattern_formatter>(pattern, time_type)) |
325 | // Note: each sink will get a new instance of a formatter object, replacing the old one. |
326 | void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local); |
327 | |
328 | // backtrace support. |
329 | // efficiently store all debug/trace messages in a circular buffer until needed for debugging. |
330 | void enable_backtrace(size_t n_messages); |
331 | void disable_backtrace(); |
332 | void dump_backtrace(); |
333 | |
334 | // flush functions |
335 | void flush(); |
336 | void flush_on(level::level_enum log_level); |
337 | level::level_enum flush_level() const; |
338 | |
339 | // sinks |
340 | const std::vector<sink_ptr> &sinks() const; |
341 | |
342 | std::vector<sink_ptr> &sinks(); |
343 | |
344 | // error handler |
345 | void set_error_handler(err_handler); |
346 | |
347 | // create new logger with same sinks and configuration. |
348 | virtual std::shared_ptr<logger> clone(std::string logger_name); |
349 | |
350 | protected: |
351 | std::string name_; |
352 | std::vector<sink_ptr> sinks_; |
353 | spdlog::level_t level_{level::info}; |
354 | spdlog::level_t flush_level_{level::off}; |
355 | err_handler custom_err_handler_{nullptr}; |
356 | details::backtracer tracer_; |
357 | |
358 | // common implementation for after templated public api has been resolved |
359 | template<typename... Args> |
360 | void log_(source_loc loc, level::level_enum lvl, string_view_t fmt, Args &&... args) |
361 | { |
362 | bool log_enabled = should_log(lvl); |
363 | bool traceback_enabled = tracer_.enabled(); |
364 | if (!log_enabled && !traceback_enabled) |
365 | { |
366 | return; |
367 | } |
368 | SPDLOG_TRY |
369 | { |
370 | memory_buf_t buf; |
371 | #ifdef SPDLOG_USE_STD_FORMAT |
372 | fmt_lib::vformat_to(std::back_inserter(buf), fmt, fmt_lib::make_format_args(std::forward<Args>(args)...)); |
373 | #else |
374 | fmt::vformat_to(fmt::appender(buf), fmt, fmt::make_format_args(std::forward<Args>(args)...)); |
375 | #endif |
376 | |
377 | details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size())); |
378 | log_it_(log_msg, log_enabled, traceback_enabled); |
379 | } |
380 | SPDLOG_LOGGER_CATCH(loc) |
381 | } |
382 | |
383 | #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT |
384 | template<typename... Args> |
385 | void log_(source_loc loc, level::level_enum lvl, wstring_view_t fmt, Args &&... args) |
386 | { |
387 | bool log_enabled = should_log(lvl); |
388 | bool traceback_enabled = tracer_.enabled(); |
389 | if (!log_enabled && !traceback_enabled) |
390 | { |
391 | return; |
392 | } |
393 | SPDLOG_TRY |
394 | { |
395 | // format to wmemory_buffer and convert to utf8 |
396 | wmemory_buf_t wbuf; |
397 | fmt_lib::vformat_to( |
398 | std::back_inserter(wbuf), fmt, fmt_lib::make_format_args<fmt_lib::wformat_context>(std::forward<Args>(args)...)); |
399 | |
400 | memory_buf_t buf; |
401 | details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf); |
402 | details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size())); |
403 | log_it_(log_msg, log_enabled, traceback_enabled); |
404 | } |
405 | SPDLOG_LOGGER_CATCH(loc) |
406 | } |
407 | #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT |
408 | |
409 | // log the given message (if the given log level is high enough), |
410 | // and save backtrace (if backtrace is enabled). |
411 | void log_it_(const details::log_msg &log_msg, bool log_enabled, bool traceback_enabled); |
412 | virtual void sink_it_(const details::log_msg &msg); |
413 | virtual void flush_(); |
414 | void dump_backtrace_(); |
415 | bool should_flush_(const details::log_msg &msg); |
416 | |
417 | // handle errors during logging. |
418 | // default handler prints the error to stderr at max rate of 1 message/sec. |
419 | void err_handler_(const std::string &msg); |
420 | }; |
421 | |
422 | void swap(logger &a, logger &b); |
423 | |
424 | } // namespace spdlog |
425 | |
426 | #ifdef SPDLOG_HEADER_ONLY |
427 | # include "logger-inl.h" |
428 | #endif |
429 | |