]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/include/seastar/util/log.hh
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / seastar / include / seastar / util / log.hh
CommitLineData
11fdf7f2
TL
1/*
2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
6 *
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,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18/*
19 * Copyright (C) 2015 Cloudius Systems, Ltd.
20 */
21#pragma once
22
23#include <seastar/core/sstring.hh>
f67539c2
TL
24#include <seastar/util/concepts.hh>
25#include <seastar/util/log-impl.hh>
26#include <seastar/core/lowres_clock.hh>
20effc67
TL
27#include <seastar/util/std-compat.hh>
28
11fdf7f2
TL
29#include <unordered_map>
30#include <exception>
31#include <iosfwd>
32#include <atomic>
33#include <mutex>
34#include <boost/lexical_cast.hpp>
f67539c2 35#include <fmt/format.h>
11fdf7f2
TL
36
37
38/// \addtogroup logging
39/// @{
40
41namespace seastar {
42
43/// \brief log level used with \see {logger}
44/// used with the logger.do_log method.
45/// Levels are in increasing order. That is if you want to see debug(3) logs you
46/// will also see error(0), warn(1), info(2).
47///
48enum class log_level {
49 error,
50 warn,
51 info,
52 debug,
53 trace,
54};
55
56std::ostream& operator<<(std::ostream& out, log_level level);
57std::istream& operator>>(std::istream& in, log_level& level);
58}
59
60// Boost doesn't auto-deduce the existence of the streaming operators for some reason
61
62namespace boost {
63template<>
64seastar::log_level lexical_cast(const std::string& source);
65
66}
67
68namespace seastar {
69
70class logger;
71class logger_registry;
72
9f95a23c 73/// \brief Logger class for ostream or syslog.
11fdf7f2
TL
74///
75/// Java style api for logging.
76/// \code {.cpp}
77/// static seastar::logger logger("lsa-api");
78/// logger.info("Triggering compaction");
79/// \endcode
80/// The output format is: (depending on level)
81/// DEBUG %Y-%m-%d %T,%03d [shard 0] - "your msg" \n
82///
f67539c2 83/// It is possible to rate-limit log messages, see \ref logger::rate_limit.
11fdf7f2
TL
84class logger {
85 sstring _name;
86 std::atomic<log_level> _level = { log_level::info };
9f95a23c
TL
87 static std::ostream* _out;
88 static std::atomic<bool> _ostream;
11fdf7f2 89 static std::atomic<bool> _syslog;
1e59de90
TL
90 static unsigned _shard_field_width;
91 static inline thread_local bool silent = false;
f67539c2
TL
92
93public:
94 class log_writer {
95 public:
96 virtual ~log_writer() = default;
97 virtual internal::log_buf::inserter_iterator operator()(internal::log_buf::inserter_iterator) = 0;
11fdf7f2 98 };
f67539c2
TL
99 template <typename Func>
100 SEASTAR_CONCEPT(requires requires (Func fn, internal::log_buf::inserter_iterator it) {
101 it = fn(it);
102 })
103 class lambda_log_writer : public log_writer {
104 Func _func;
105 public:
106 lambda_log_writer(Func&& func) : _func(std::forward<Func>(func)) { }
107 virtual ~lambda_log_writer() override = default;
108 virtual internal::log_buf::inserter_iterator operator()(internal::log_buf::inserter_iterator it) override { return _func(it); }
11fdf7f2 109 };
f67539c2 110
20effc67
TL
111 /// \cond internal
112 /// \brief used to hold the log format string and the caller's source_location.
113 struct format_info {
114 /// implicitly construct format_info from a const char* format string.
115 /// \param fmt - {fmt} style format string
116 format_info(const char* format, compat::source_location loc = compat::source_location::current()) noexcept
117 : format(format)
118 , loc(loc)
119 {}
120 /// implicitly construct format_info from a std::string_view format string.
121 /// \param fmt - {fmt} style format string_view
122 format_info(std::string_view format, compat::source_location loc = compat::source_location::current()) noexcept
123 : format(format)
124 , loc(loc)
125 {}
126 /// implicitly construct format_info with no format string.
127 format_info(compat::source_location loc = compat::source_location::current()) noexcept
128 : format()
129 , loc(loc)
130 {}
131 std::string_view format;
132 compat::source_location loc;
133 };
134
f67539c2
TL
135private:
136
137 // We can't use an std::function<> as it potentially allocates.
138 void do_log(log_level level, log_writer& writer);
20effc67
TL
139 void failed_to_log(std::exception_ptr ex, format_info fmt) noexcept;
140
141 class silencer {
20effc67 142 public:
1e59de90
TL
143 silencer() noexcept {
144 silent = true;
145 }
20effc67
TL
146
147 ~silencer() {
1e59de90 148 silent = false;
20effc67
TL
149 }
150 };
151
f67539c2
TL
152public:
153 /// Apply a rate limit to log message(s)
154 ///
155 /// Pass this to \ref logger::log() to apply a rate limit to the message.
156 /// The rate limit is applied to all \ref logger::log() calls this rate
157 /// limit is passed to. Example:
158 ///
159 /// void handle_request() {
160 /// static thread_local logger::rate_limit my_rl(std::chrono::seconds(10));
161 /// // ...
162 /// my_log.log(log_level::info, my_rl, "a message we don't want to log on every request, only at most once each 10 seconds");
163 /// // ...
164 /// }
165 ///
166 /// The rate limit ensures that at most one message per interval will be
167 /// logged. If there were messages dropped due to rate-limiting the
168 /// following snippet will be prepended to the first non-dropped log
169 /// messages:
170 ///
171 /// (rate limiting dropped $N similar messages)
172 ///
173 /// Where $N is the number of messages dropped.
174 class rate_limit {
175 friend class logger;
176
177 using clock = lowres_clock;
178
179 private:
180 clock::duration _interval;
181 clock::time_point _next;
182 uint64_t _dropped_messages = 0;
183
184 private:
185 bool check();
186 bool has_dropped_messages() const { return bool(_dropped_messages); }
187 uint64_t get_and_reset_dropped_messages() {
188 return std::exchange(_dropped_messages, 0);
189 }
190
191 public:
192 explicit rate_limit(std::chrono::milliseconds interval);
193 };
194
11fdf7f2
TL
195public:
196 explicit logger(sstring name);
197 logger(logger&& x);
198 ~logger();
199
f67539c2 200 bool is_shard_zero() noexcept;
11fdf7f2
TL
201
202 /// Test if desired log level is enabled
203 ///
204 /// \param level - enum level value (info|error...)
205 /// \return true if the log level has been enabled.
f67539c2 206 bool is_enabled(log_level level) const noexcept {
1e59de90 207 return __builtin_expect(level <= _level.load(std::memory_order_relaxed), false) && !silent;
11fdf7f2
TL
208 }
209
210 /// logs to desired level if enabled, otherwise we ignore the log line
211 ///
20effc67
TL
212 /// \param fmt - {fmt} style format string (implictly converted to struct logger::format_info)
213 /// or a logger::format_info passed down the call chain.
f67539c2
TL
214 /// \param args - args to print string
215 ///
216 template <typename... Args>
20effc67 217 void log(log_level level, format_info fmt, Args&&... args) noexcept {
f67539c2
TL
218 if (is_enabled(level)) {
219 try {
220 lambda_log_writer writer([&] (internal::log_buf::inserter_iterator it) {
20effc67
TL
221#if FMT_VERSION >= 80000
222 return fmt::format_to(it, fmt::runtime(fmt.format), std::forward<Args>(args)...);
223#else
224 return fmt::format_to(it, fmt.format, std::forward<Args>(args)...);
225#endif
f67539c2
TL
226 });
227 do_log(level, writer);
228 } catch (...) {
20effc67 229 failed_to_log(std::current_exception(), std::move(fmt));
f67539c2
TL
230 }
231 }
232 }
233
234 /// logs with a rate limit to desired level if enabled, otherwise we ignore the log line
235 ///
236 /// If there were messages dropped due to rate-limiting the following snippet
237 /// will be prepended to the first non-dropped log messages:
238 ///
239 /// (rate limiting dropped $N similar messages)
240 ///
241 /// Where $N is the number of messages dropped.
242 ///
243 /// \param rl - the \ref rate_limit to apply to this log
20effc67
TL
244 /// \param fmt - {fmt} style format string (implictly converted to struct logger::format_info)
245 /// or a logger::format_info passed down the call chain.
11fdf7f2
TL
246 /// \param args - args to print string
247 ///
248 template <typename... Args>
20effc67 249 void log(log_level level, rate_limit& rl, format_info fmt, Args&&... args) noexcept {
f67539c2
TL
250 if (is_enabled(level) && rl.check()) {
251 try {
252 lambda_log_writer writer([&] (internal::log_buf::inserter_iterator it) {
253 if (rl.has_dropped_messages()) {
254 it = fmt::format_to(it, "(rate limiting dropped {} similar messages) ", rl.get_and_reset_dropped_messages());
255 }
20effc67
TL
256#if FMT_VERSION >= 80000
257 return fmt::format_to(it, fmt::runtime(fmt.format), std::forward<Args>(args)...);
258#else
259 return fmt::format_to(it, fmt.format, std::forward<Args>(args)...);
260#endif
f67539c2
TL
261 });
262 do_log(level, writer);
263 } catch (...) {
20effc67 264 failed_to_log(std::current_exception(), std::move(fmt));
f67539c2
TL
265 }
266 }
267 }
268
269 /// \cond internal
270 /// logs to desired level if enabled, otherwise we ignore the log line
271 ///
272 /// \param writer a function which writes directly to the underlying log buffer
20effc67 273 /// \param fmt - optional logger::format_info passed down the call chain.
f67539c2
TL
274 ///
275 /// This is a low level method for use cases where it is very important to
276 /// avoid any allocations. The \arg writer will be passed a
277 /// internal::log_buf::inserter_iterator that allows it to write into the log
278 /// buffer directly, avoiding the use of any intermediary buffers.
20effc67 279 void log(log_level level, log_writer& writer, format_info fmt = {}) noexcept {
11fdf7f2
TL
280 if (is_enabled(level)) {
281 try {
f67539c2 282 do_log(level, writer);
11fdf7f2 283 } catch (...) {
20effc67 284 failed_to_log(std::current_exception(), std::move(fmt));
11fdf7f2
TL
285 }
286 }
287 }
f67539c2
TL
288 /// logs to desired level if enabled, otherwise we ignore the log line
289 ///
290 /// \param writer a function which writes directly to the underlying log buffer
20effc67 291 /// \param fmt - optional logger::format_info passed down the call chain.
f67539c2
TL
292 ///
293 /// This is a low level method for use cases where it is very important to
294 /// avoid any allocations. The \arg writer will be passed a
295 /// internal::log_buf::inserter_iterator that allows it to write into the log
296 /// buffer directly, avoiding the use of any intermediary buffers.
297 /// This is rate-limited version, see \ref rate_limit.
20effc67 298 void log(log_level level, rate_limit& rl, log_writer& writer, format_info fmt = {}) noexcept {
f67539c2
TL
299 if (is_enabled(level) && rl.check()) {
300 try {
301 lambda_log_writer writer_wrapper([&] (internal::log_buf::inserter_iterator it) {
302 if (rl.has_dropped_messages()) {
303 it = fmt::format_to(it, "(rate limiting dropped {} similar messages) ", rl.get_and_reset_dropped_messages());
304 }
305 return writer(it);
306 });
307 do_log(level, writer_wrapper);
308 } catch (...) {
20effc67 309 failed_to_log(std::current_exception(), std::move(fmt));
f67539c2
TL
310 }
311 }
312 }
313 /// \endcond
11fdf7f2
TL
314
315 /// Log with error tag:
316 /// ERROR %Y-%m-%d %T,%03d [shard 0] - "your msg" \n
317 ///
20effc67
TL
318 /// \param fmt - {fmt} style format string (implictly converted to struct logger::format_info)
319 /// or a logger::format_info passed down the call chain.
11fdf7f2
TL
320 /// \param args - args to print string
321 ///
322 template <typename... Args>
20effc67
TL
323 void error(format_info fmt, Args&&... args) noexcept {
324 log(log_level::error, std::move(fmt), std::forward<Args>(args)...);
11fdf7f2
TL
325 }
326 /// Log with warning tag:
327 /// WARN %Y-%m-%d %T,%03d [shard 0] - "your msg" \n
328 ///
20effc67
TL
329 /// \param fmt - {fmt} style format string (implictly converted to struct logger::format_info)
330 /// or a logger::format_info passed down the call chain.
11fdf7f2
TL
331 /// \param args - args to print string
332 ///
333 template <typename... Args>
20effc67
TL
334 void warn(format_info fmt, Args&&... args) noexcept {
335 log(log_level::warn, std::move(fmt), std::forward<Args>(args)...);
11fdf7f2
TL
336 }
337 /// Log with info tag:
338 /// INFO %Y-%m-%d %T,%03d [shard 0] - "your msg" \n
339 ///
20effc67
TL
340 /// \param fmt - {fmt} style format string (implictly converted to struct logger::format_info)
341 /// or a logger::format_info passed down the call chain.
11fdf7f2
TL
342 /// \param args - args to print string
343 ///
344 template <typename... Args>
20effc67
TL
345 void info(format_info fmt, Args&&... args) noexcept {
346 log(log_level::info, std::move(fmt), std::forward<Args>(args)...);
11fdf7f2
TL
347 }
348 /// Log with info tag on shard zero only:
349 /// INFO %Y-%m-%d %T,%03d [shard 0] - "your msg" \n
350 ///
20effc67
TL
351 /// \param fmt - {fmt} style format string (implictly converted to struct logger::format_info)
352 /// or a logger::format_info passed down the call chain.
11fdf7f2
TL
353 /// \param args - args to print string
354 ///
355 template <typename... Args>
20effc67 356 void info0(format_info fmt, Args&&... args) noexcept {
11fdf7f2 357 if (is_shard_zero()) {
20effc67 358 log(log_level::info, std::move(fmt), std::forward<Args>(args)...);
11fdf7f2
TL
359 }
360 }
f67539c2 361 /// Log with debug tag:
11fdf7f2
TL
362 /// DEBUG %Y-%m-%d %T,%03d [shard 0] - "your msg" \n
363 ///
20effc67
TL
364 /// \param fmt - {fmt} style format string (implictly converted to struct logger::format_info)
365 /// or a logger::format_info passed down the call chain.
11fdf7f2
TL
366 /// \param args - args to print string
367 ///
368 template <typename... Args>
20effc67
TL
369 void debug(format_info fmt, Args&&... args) noexcept {
370 log(log_level::debug, std::move(fmt), std::forward<Args>(args)...);
11fdf7f2
TL
371 }
372 /// Log with trace tag:
373 /// TRACE %Y-%m-%d %T,%03d [shard 0] - "your msg" \n
374 ///
20effc67
TL
375 /// \param fmt - {fmt} style format string (implictly converted to struct logger::format_info)
376 /// or a logger::format_info passed down the call chain.
11fdf7f2
TL
377 /// \param args - args to print string
378 ///
379 template <typename... Args>
20effc67
TL
380 void trace(format_info fmt, Args&&... args) noexcept {
381 log(log_level::trace, std::move(fmt), std::forward<Args>(args)...);
11fdf7f2
TL
382 }
383
384 /// \return name of the logger. Usually one logger per module
385 ///
f67539c2 386 const sstring& name() const noexcept {
11fdf7f2
TL
387 return _name;
388 }
389
390 /// \return current log level for this logger
391 ///
f67539c2 392 log_level level() const noexcept {
11fdf7f2
TL
393 return _level.load(std::memory_order_relaxed);
394 }
395
396 /// \param level - set the log level
397 ///
f67539c2 398 void set_level(log_level level) noexcept {
11fdf7f2
TL
399 _level.store(level, std::memory_order_relaxed);
400 }
401
9f95a23c 402 /// Set output stream, default is std::cerr
f67539c2 403 static void set_ostream(std::ostream& out) noexcept;
9f95a23c
TL
404
405 /// Also output to ostream. default is true
f67539c2 406 static void set_ostream_enabled(bool enabled) noexcept;
9f95a23c 407
11fdf7f2 408 /// Also output to stdout. default is true
9f95a23c 409 [[deprecated("Use set_ostream_enabled instead")]]
f67539c2 410 static void set_stdout_enabled(bool enabled) noexcept;
11fdf7f2
TL
411
412 /// Also output to syslog. default is false
413 ///
414 /// NOTE: syslog() can block, which will stall the reactor thread.
415 /// this should be rare (will have to fill the pipe buffer
416 /// before syslogd can clear it) but can happen.
f67539c2 417 static void set_syslog_enabled(bool enabled) noexcept;
1e59de90
TL
418
419 /// Set the width of shard id field in log messages
420 ///
421 /// \c this_shard_id() is printed as a part of the prefix in logging
422 /// messages, like "[shard 42]", where \c 42 is the decimal number of the
423 /// current shard id printed with a minimal width.
424 ///
425 /// \param width the minimal width of the shard id field
426 static void set_shard_field_width(unsigned width) noexcept;
427
428 /// enable/disable the colored tag in ostream
429 ///
430 /// \note this is a noop if fmtlib's version is less than 6.0
431 static void set_with_color(bool enabled) noexcept;
11fdf7f2
TL
432};
433
434/// \brief used to keep a static registry of loggers
435/// since the typical use case is to do:
436/// \code {.cpp}
437/// static seastar::logger("my_module");
438/// \endcode
439/// this class is used to wrap around the static map
440/// that holds pointers to all logs
441///
442class logger_registry {
443 mutable std::mutex _mutex;
444 std::unordered_map<sstring, logger*> _loggers;
445public:
446 /// loops through all registered loggers and sets the log level
447 /// Note: this method locks
448 ///
449 /// \param level - desired level: error,info,...
450 void set_all_loggers_level(log_level level);
451
452 /// Given a name for a logger returns the log_level enum
453 /// Note: this method locks
454 ///
455 /// \return log_level for the given logger name
456 log_level get_logger_level(sstring name) const;
457
458 /// Sets the log level for a given logger
459 /// Note: this method locks
460 ///
461 /// \param name - name of logger
462 /// \param level - desired level of logging
463 void set_logger_level(sstring name, log_level level);
464
465 /// Returns a list of registered loggers
466 /// Note: this method locks
467 ///
468 /// \return all registered loggers
469 std::vector<sstring> get_all_logger_names();
470
471 /// Registers a logger with the static map
472 /// Note: this method locks
473 ///
474 void register_logger(logger* l);
475 /// Unregisters a logger with the static map
476 /// Note: this method locks
477 ///
478 void unregister_logger(logger* l);
479 /// Swaps the logger given the from->name() in the static map
480 /// Note: this method locks
481 ///
482 void moved(logger* from, logger* to);
483};
484
485logger_registry& global_logger_registry();
486
20effc67 487/// \brief Timestamp style.
11fdf7f2
TL
488enum class logger_timestamp_style {
489 none,
490 boot,
491 real,
492};
493
20effc67 494/// \brief Output stream to use for logging.
9f95a23c
TL
495enum class logger_ostream_type {
496 none,
497 stdout,
498 stderr,
499};
500
11fdf7f2
TL
501struct logging_settings final {
502 std::unordered_map<sstring, log_level> logger_levels;
503 log_level default_level;
504 bool stdout_enabled;
505 bool syslog_enabled;
1e59de90 506 bool with_color;
11fdf7f2 507 logger_timestamp_style stdout_timestamp_style = logger_timestamp_style::real;
9f95a23c 508 logger_ostream_type logger_ostream = logger_ostream_type::stderr;
11fdf7f2
TL
509};
510
511/// Shortcut for configuring the logging system all at once.
512///
513void apply_logging_settings(const logging_settings&);
514
515/// \cond internal
516
517extern thread_local uint64_t logging_failures;
518
519sstring pretty_type_name(const std::type_info&);
520
521sstring level_name(log_level level);
522
523template <typename T>
524class logger_for : public logger {
525public:
526 logger_for() : logger(pretty_type_name(typeid(T))) {}
527};
528
11fdf7f2
TL
529/// \endcond
530} // end seastar namespace
531
532// Pretty-printer for exceptions to be logged, e.g., std::current_exception().
533namespace std {
534std::ostream& operator<<(std::ostream&, const std::exception_ptr&);
535std::ostream& operator<<(std::ostream&, const std::exception&);
536std::ostream& operator<<(std::ostream&, const std::system_error&);
537}
538
1e59de90
TL
539#if FMT_VERSION >= 90000
540template <> struct fmt::formatter<std::exception_ptr> : fmt::ostream_formatter {};
541template <> struct fmt::formatter<std::exception> : fmt::ostream_formatter {};
542template <> struct fmt::formatter<std::system_error> : fmt::ostream_formatter {};
543#endif
544
11fdf7f2 545/// @}