Seastar
High performance C++ framework for concurrent servers
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
log.hh
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>
24#include <seastar/util/backtrace.hh>
25#include <seastar/util/log-impl.hh>
26#include <seastar/core/lowres_clock.hh>
27#include <seastar/util/std-compat.hh>
28#include <seastar/util/modules.hh>
29
30#ifndef SEASTAR_MODULE
31#include <concepts>
32#include <unordered_map>
33#include <exception>
34#include <iosfwd>
35#include <atomic>
36#include <mutex>
37#include <type_traits>
38#include <fmt/core.h>
39#include <fmt/format.h>
40#endif
41
44
45namespace seastar {
46
47SEASTAR_MODULE_EXPORT_BEGIN
48
54enum class log_level {
55 error,
56 warn,
57 info,
58 debug,
59 trace,
60};
61
62std::ostream& operator<<(std::ostream& out, log_level level);
63std::istream& operator>>(std::istream& in, log_level& level);
64
65SEASTAR_MODULE_EXPORT_END
66}
67
68template <>
69struct fmt::formatter<seastar::log_level> {
70 constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
71 auto format(seastar::log_level level, fmt::format_context& ctx) const -> decltype(ctx.out());
72};
73
74namespace seastar {
75SEASTAR_MODULE_EXPORT_BEGIN
76class logger;
77class logger_registry;
78
90class logger {
91 sstring _name;
92 std::atomic<log_level> _level = { log_level::info };
93 static std::ostream* _out;
94 static std::atomic<bool> _ostream;
95 static std::atomic<bool> _syslog;
96 static unsigned _shard_field_width;
97#ifdef SEASTAR_BUILD_SHARED_LIBS
98 static thread_local bool silent;
99#else
100 static inline thread_local bool silent = false;
101#endif
102
103public:
104 class log_writer {
105 public:
106 virtual ~log_writer() = default;
107 virtual internal::log_buf::inserter_iterator operator()(internal::log_buf::inserter_iterator) = 0;
108 };
109 template <typename Func>
110 requires requires (Func fn, internal::log_buf::inserter_iterator it) {
111 it = fn(it);
112 }
113 class lambda_log_writer : public log_writer {
114 Func _func;
115 public:
116 lambda_log_writer(Func&& func) : _func(std::forward<Func>(func)) { }
117 virtual ~lambda_log_writer() override = default;
118 virtual internal::log_buf::inserter_iterator operator()(internal::log_buf::inserter_iterator it) override { return _func(it); }
119 };
120
123#ifdef SEASTAR_LOGGER_COMPILE_TIME_FMT
124 template<typename... Args>
125 struct format_info {
128 template<
129 typename S,
130 std::enable_if_t<std::is_convertible_v<const S&, std::string_view>, int> = 0>
131 FMT_CONSTEVAL inline format_info(const S& format,
132 compat::source_location loc = compat::source_location::current()) noexcept
133 : format(format)
134 , loc(loc)
135 {}
140 inline format_info(fmt::format_string<Args...> s,
141 compat::source_location loc = compat::source_location::current()) noexcept
142 : format(s)
143 , loc(loc)
144 {}
145#if FMT_VERSION >= 100000
146 using runtime_format_string_t = fmt::runtime_format_string<char>;
147#else
148 using runtime_format_string_t = fmt::basic_runtime<char>;
149#endif
150 inline format_info(runtime_format_string_t s,
151 compat::source_location loc = compat::source_location::current()) noexcept
152 : format(s)
153 , loc(loc)
154 {}
156 FMT_CONSTEVAL format_info() noexcept
157 : format_info("")
158 {}
159 fmt::format_string<Args...> format;
160 compat::source_location loc;
161 };
162#ifdef __cpp_lib_type_identity
163 template <typename T>
164 using type_identity_t = typename std::type_identity<T>::type;
165#else
166 template <typename T> struct type_identity { using type = T; };
167 template <typename T> using type_identity_t = typename type_identity<T>::type;
168#endif
169 template <typename... Args>
170 using format_info_t = format_info<type_identity_t<Args>...>;
171#else // SEASTAR_LOGGER_COMPILE_TIME_FMT
174 struct format_info {
177 format_info(const char* format, compat::source_location loc = compat::source_location::current()) noexcept
178 : format(format)
179 , loc(loc)
180 {}
183 format_info(std::string_view format, compat::source_location loc = compat::source_location::current()) noexcept
184 : format(format)
185 , loc(loc)
186 {}
188 format_info(compat::source_location loc = compat::source_location::current()) noexcept
189 : format()
190 , loc(loc)
191 {}
192 std::string_view format;
193 compat::source_location loc;
194 };
195 // to reduce the number of #ifdefs, let's be compatible with the templated
196 // format_info
197 template <typename...>
198 using format_info_t = format_info;
199#endif // SEASTAR_LOGGER_COMPILE_TIME_FMT
200
201private:
202
203 // We can't use an std::function<> as it potentially allocates.
204 void do_log(log_level level, log_writer& writer);
205 void failed_to_log(std::exception_ptr ex,
206 fmt::string_view fmt,
207 compat::source_location loc) noexcept;
208
209 class silencer {
210 public:
211 silencer() noexcept {
212 silent = true;
213 }
214
215 ~silencer() {
216 silent = false;
217 }
218 };
219
220public:
242 class rate_limit {
243 friend class logger;
244
245 using clock = lowres_clock;
246
247 private:
248 clock::duration _interval;
249 clock::time_point _next;
250 uint64_t _dropped_messages = 0;
251
252 private:
253 bool check();
254 bool has_dropped_messages() const { return bool(_dropped_messages); }
255 uint64_t get_and_reset_dropped_messages() {
256 return std::exchange(_dropped_messages, 0);
257 }
258
259 public:
260 explicit rate_limit(std::chrono::milliseconds interval);
261 };
262
263public:
264 explicit logger(sstring name);
265 logger(logger&& x);
266 ~logger();
267
268 bool is_shard_zero() noexcept;
269
274 bool is_enabled(log_level level) const noexcept {
275 return __builtin_expect(level <= _level.load(std::memory_order_relaxed), false) && !silent;
276 }
277
284 template <typename... Args>
285 void log(log_level level, format_info_t<Args...> fmt, Args&&... args) noexcept {
286 if (is_enabled(level)) {
287 try {
288 lambda_log_writer writer([&] (internal::log_buf::inserter_iterator it) {
289#ifdef SEASTAR_LOGGER_COMPILE_TIME_FMT
290 return fmt::format_to(it, fmt.format, std::forward<Args>(args)...);
291#else
292 return fmt::format_to(it, fmt::runtime(fmt.format), std::forward<Args>(args)...);
293#endif
294 });
295 do_log(level, writer);
296 } catch (...) {
297 failed_to_log(std::current_exception(), fmt::string_view(fmt.format), fmt.loc);
298 }
299 }
300 }
301
316 template <typename... Args>
317 void log(log_level level, rate_limit& rl, format_info_t<Args...> fmt, Args&&... args) noexcept {
318 if (is_enabled(level) && rl.check()) {
319 try {
320 lambda_log_writer writer([&] (internal::log_buf::inserter_iterator it) {
321 if (rl.has_dropped_messages()) {
322 it = fmt::format_to(it, "(rate limiting dropped {} similar messages) ", rl.get_and_reset_dropped_messages());
323 }
324 return fmt::format_to(it, fmt::runtime(fmt.format), std::forward<Args>(args)...);
325 });
326 do_log(level, writer);
327 } catch (...) {
328 failed_to_log(std::current_exception(), fmt::string_view(fmt.format), fmt.loc);
329 }
330 }
331 }
332
343 void log(log_level level, rate_limit& rl, log_writer& writer, format_info_t<> fmt = {}) noexcept {
344 if (is_enabled(level) && rl.check()) {
345 try {
346 lambda_log_writer writer_wrapper([&] (internal::log_buf::inserter_iterator it) {
347 if (rl.has_dropped_messages()) {
348 it = fmt::format_to(it, "(rate limiting dropped {} similar messages) ", rl.get_and_reset_dropped_messages());
349 }
350 return writer(it);
351 });
352 do_log(level, writer_wrapper);
353 } catch (...) {
354 failed_to_log(std::current_exception(), "", fmt.loc);
355 }
356 }
357 }
359
367 template <typename... Args>
368 void error(format_info_t<Args...> fmt, Args&&... args) noexcept {
369 log(log_level::error, std::move(fmt), std::forward<Args>(args)...);
370 }
378 template <typename... Args>
379 void warn(format_info_t<Args...> fmt, Args&&... args) noexcept {
380 log(log_level::warn, std::move(fmt), std::forward<Args>(args)...);
381 }
389 template <typename... Args>
390 void info(format_info_t<Args...> fmt, Args&&... args) noexcept {
391 log(log_level::info, std::move(fmt), std::forward<Args>(args)...);
392 }
400 template <typename... Args>
401 void info0(format_info_t<Args...> fmt, Args&&... args) noexcept {
402 if (is_shard_zero()) {
403 log(log_level::info, std::move(fmt), std::forward<Args>(args)...);
404 }
405 }
413 template <typename... Args>
414 void debug(format_info_t<Args...> fmt, Args&&... args) noexcept {
415 log(log_level::debug, std::move(fmt), std::forward<Args>(args)...);
416 }
424 template <typename... Args>
425 void trace(format_info_t<Args...> fmt, Args&&... args) noexcept {
426 log(log_level::trace, std::move(fmt), std::forward<Args>(args)...);
427 }
428
431 const sstring& name() const noexcept {
432 return _name;
433 }
434
437 log_level level() const noexcept {
438 return _level.load(std::memory_order_relaxed);
439 }
440
443 void set_level(log_level level) noexcept {
444 _level.store(level, std::memory_order_relaxed);
445 }
446
448 static void set_ostream(std::ostream& out) noexcept;
449
451 static void set_ostream_enabled(bool enabled) noexcept;
452
458 static void set_syslog_enabled(bool enabled) noexcept;
459
467 static void set_shard_field_width(unsigned width) noexcept;
468
472 static void set_with_color(bool enabled) noexcept;
473};
474
483class logger_registry {
484 mutable std::mutex _mutex;
485 std::unordered_map<sstring, logger*> _loggers;
486public:
491 void set_all_loggers_level(log_level level);
492
497 log_level get_logger_level(sstring name) const;
498
504 void set_logger_level(sstring name, log_level level);
505
510 std::vector<sstring> get_all_logger_names();
511
515 void register_logger(logger* l);
519 void unregister_logger(logger* l);
523 void moved(logger* from, logger* to);
524};
525
526logger_registry& global_logger_registry();
527
529enum class logger_timestamp_style {
530 none,
531 boot,
532 real,
533};
534
536enum class logger_ostream_type {
537 none,
538 cout = 1,
539 cerr = 2,
540};
541
542struct logging_settings final {
543 std::unordered_map<sstring, log_level> logger_levels;
544 log_level default_level;
545 bool stdout_enabled;
546 bool syslog_enabled;
547 bool with_color;
548 logger_timestamp_style stdout_timestamp_style = logger_timestamp_style::real;
549 logger_ostream_type logger_ostream = logger_ostream_type::cerr;
550};
551
554void apply_logging_settings(const logging_settings&);
555
556SEASTAR_MODULE_EXPORT_END
557
559
560extern thread_local uint64_t logging_failures;
561
562sstring pretty_type_name(const std::type_info&);
563
564sstring level_name(log_level level);
565
566template <typename T>
567class logger_for : public logger {
568public:
569 logger_for() : logger(pretty_type_name(typeid(T))) {}
570};
571
573} // end seastar namespace
574
575// Pretty-printer for exceptions to be logged, e.g., std::current_exception().
576namespace std {
577std::ostream& operator<<(std::ostream&, const std::exception_ptr&);
578std::ostream& operator<<(std::ostream&, const std::exception&);
579std::ostream& operator<<(std::ostream&, const std::system_error&);
580}
581
582#if FMT_VERSION >= 90000
583template <> struct fmt::formatter<std::exception_ptr> : fmt::ostream_formatter {};
584template <> struct fmt::formatter<std::exception> : fmt::ostream_formatter {};
585template <> struct fmt::formatter<std::system_error> : fmt::ostream_formatter {};
586#endif
587
log_level
log level used with
Definition: log.hh:54
@ none
Dump diagnostic error report for none of the allocation failures.
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
sstring format(fmt::format_string< A... > fmt, A &&... a)
Definition: format.hh:42
STL namespace.
ostream & operator<<(ostream &os, const seastar::lazy_eval< Func > &lf)
Definition: lazy.hh:131