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 <boost/lexical_cast.hpp>
39#include <fmt/core.h>
40#include <fmt/format.h>
41#endif
42
45
46namespace seastar {
47
48SEASTAR_MODULE_EXPORT_BEGIN
49
55enum class log_level {
56 error,
57 warn,
58 info,
59 debug,
60 trace,
61};
62
63std::ostream& operator<<(std::ostream& out, log_level level);
64std::istream& operator>>(std::istream& in, log_level& level);
65
66SEASTAR_MODULE_EXPORT_END
67}
68
69template <>
70struct fmt::formatter<seastar::log_level> {
71 constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
72 auto format(seastar::log_level level, fmt::format_context& ctx) const -> decltype(ctx.out());
73};
74
75// Boost doesn't auto-deduce the existence of the streaming operators for some reason
76
77namespace boost {
78template<>
79seastar::log_level lexical_cast(const std::string& source);
80
81}
82
83namespace seastar {
84SEASTAR_MODULE_EXPORT_BEGIN
85class logger;
86class logger_registry;
87
99class logger {
100 sstring _name;
101 std::atomic<log_level> _level = { log_level::info };
102 static std::ostream* _out;
103 static std::atomic<bool> _ostream;
104 static std::atomic<bool> _syslog;
105 static unsigned _shard_field_width;
106#ifdef SEASTAR_BUILD_SHARED_LIBS
107 static thread_local bool silent;
108#else
109 static inline thread_local bool silent = false;
110#endif
111
112public:
113 class log_writer {
114 public:
115 virtual ~log_writer() = default;
116 virtual internal::log_buf::inserter_iterator operator()(internal::log_buf::inserter_iterator) = 0;
117 };
118 template <typename Func>
119 requires requires (Func fn, internal::log_buf::inserter_iterator it) {
120 it = fn(it);
121 }
122 class lambda_log_writer : public log_writer {
123 Func _func;
124 public:
125 lambda_log_writer(Func&& func) : _func(std::forward<Func>(func)) { }
126 virtual ~lambda_log_writer() override = default;
127 virtual internal::log_buf::inserter_iterator operator()(internal::log_buf::inserter_iterator it) override { return _func(it); }
128 };
129
132#ifdef SEASTAR_LOGGER_COMPILE_TIME_FMT
133 template<typename... Args>
134 struct format_info {
137 template<
138 typename S,
139 std::enable_if_t<std::is_convertible_v<const S&, std::string_view>, int> = 0>
140 FMT_CONSTEVAL inline format_info(const S& format,
141 compat::source_location loc = compat::source_location::current()) noexcept
142 : format(format)
143 , loc(loc)
144 {}
149 inline format_info(fmt::format_string<Args...> s,
150 compat::source_location loc = compat::source_location::current()) noexcept
151 : format(s)
152 , loc(loc)
153 {}
154#if FMT_VERSION >= 100000
155 using runtime_format_string_t = fmt::runtime_format_string<char>;
156#else
157 using runtime_format_string_t = fmt::basic_runtime<char>;
158#endif
159 inline format_info(runtime_format_string_t s,
160 compat::source_location loc = compat::source_location::current()) noexcept
161 : format(s)
162 , loc(loc)
163 {}
165 FMT_CONSTEVAL format_info() noexcept
166 : format_info("")
167 {}
168 fmt::format_string<Args...> format;
169 compat::source_location loc;
170 };
171#ifdef __cpp_lib_type_identity
172 template <typename T>
173 using type_identity_t = typename std::type_identity<T>::type;
174#else
175 template <typename T> struct type_identity { using type = T; };
176 template <typename T> using type_identity_t = typename type_identity<T>::type;
177#endif
178 template <typename... Args>
179 using format_info_t = format_info<type_identity_t<Args>...>;
180#else // SEASTAR_LOGGER_COMPILE_TIME_FMT
183 struct format_info {
186 format_info(const char* format, compat::source_location loc = compat::source_location::current()) noexcept
187 : format(format)
188 , loc(loc)
189 {}
192 format_info(std::string_view format, compat::source_location loc = compat::source_location::current()) noexcept
193 : format(format)
194 , loc(loc)
195 {}
197 format_info(compat::source_location loc = compat::source_location::current()) noexcept
198 : format()
199 , loc(loc)
200 {}
201 std::string_view format;
202 compat::source_location loc;
203 };
204 // to reduce the number of #ifdefs, let's be compatible with the templated
205 // format_info
206 template <typename...>
207 using format_info_t = format_info;
208#endif // SEASTAR_LOGGER_COMPILE_TIME_FMT
209
210private:
211
212 // We can't use an std::function<> as it potentially allocates.
213 void do_log(log_level level, log_writer& writer);
214 void failed_to_log(std::exception_ptr ex,
215 fmt::string_view fmt,
216 compat::source_location loc) noexcept;
217
218 class silencer {
219 public:
220 silencer() noexcept {
221 silent = true;
222 }
223
224 ~silencer() {
225 silent = false;
226 }
227 };
228
229public:
251 class rate_limit {
252 friend class logger;
253
254 using clock = lowres_clock;
255
256 private:
257 clock::duration _interval;
258 clock::time_point _next;
259 uint64_t _dropped_messages = 0;
260
261 private:
262 bool check();
263 bool has_dropped_messages() const { return bool(_dropped_messages); }
264 uint64_t get_and_reset_dropped_messages() {
265 return std::exchange(_dropped_messages, 0);
266 }
267
268 public:
269 explicit rate_limit(std::chrono::milliseconds interval);
270 };
271
272public:
273 explicit logger(sstring name);
274 logger(logger&& x);
275 ~logger();
276
277 bool is_shard_zero() noexcept;
278
283 bool is_enabled(log_level level) const noexcept {
284 return __builtin_expect(level <= _level.load(std::memory_order_relaxed), false) && !silent;
285 }
286
293 template <typename... Args>
294 void log(log_level level, format_info_t<Args...> fmt, Args&&... args) noexcept {
295 if (is_enabled(level)) {
296 try {
297 lambda_log_writer writer([&] (internal::log_buf::inserter_iterator it) {
298#ifdef SEASTAR_LOGGER_COMPILE_TIME_FMT
299 return fmt::format_to(it, fmt.format, std::forward<Args>(args)...);
300#else
301 return fmt::format_to(it, fmt::runtime(fmt.format), std::forward<Args>(args)...);
302#endif
303 });
304 do_log(level, writer);
305 } catch (...) {
306 failed_to_log(std::current_exception(), fmt::string_view(fmt.format), fmt.loc);
307 }
308 }
309 }
310
325 template <typename... Args>
326 void log(log_level level, rate_limit& rl, format_info_t<Args...> fmt, Args&&... args) noexcept {
327 if (is_enabled(level) && rl.check()) {
328 try {
329 lambda_log_writer writer([&] (internal::log_buf::inserter_iterator it) {
330 if (rl.has_dropped_messages()) {
331 it = fmt::format_to(it, "(rate limiting dropped {} similar messages) ", rl.get_and_reset_dropped_messages());
332 }
333 return fmt::format_to(it, fmt::runtime(fmt.format), std::forward<Args>(args)...);
334 });
335 do_log(level, writer);
336 } catch (...) {
337 failed_to_log(std::current_exception(), fmt::string_view(fmt.format), fmt.loc);
338 }
339 }
340 }
341
352 void log(log_level level, rate_limit& rl, log_writer& writer, format_info_t<> fmt = {}) noexcept {
353 if (is_enabled(level) && rl.check()) {
354 try {
355 lambda_log_writer writer_wrapper([&] (internal::log_buf::inserter_iterator it) {
356 if (rl.has_dropped_messages()) {
357 it = fmt::format_to(it, "(rate limiting dropped {} similar messages) ", rl.get_and_reset_dropped_messages());
358 }
359 return writer(it);
360 });
361 do_log(level, writer_wrapper);
362 } catch (...) {
363 failed_to_log(std::current_exception(), "", fmt.loc);
364 }
365 }
366 }
368
376 template <typename... Args>
377 void error(format_info_t<Args...> fmt, Args&&... args) noexcept {
378 log(log_level::error, std::move(fmt), std::forward<Args>(args)...);
379 }
387 template <typename... Args>
388 void warn(format_info_t<Args...> fmt, Args&&... args) noexcept {
389 log(log_level::warn, std::move(fmt), std::forward<Args>(args)...);
390 }
398 template <typename... Args>
399 void info(format_info_t<Args...> fmt, Args&&... args) noexcept {
400 log(log_level::info, std::move(fmt), std::forward<Args>(args)...);
401 }
409 template <typename... Args>
410 void info0(format_info_t<Args...> fmt, Args&&... args) noexcept {
411 if (is_shard_zero()) {
412 log(log_level::info, std::move(fmt), std::forward<Args>(args)...);
413 }
414 }
422 template <typename... Args>
423 void debug(format_info_t<Args...> fmt, Args&&... args) noexcept {
424 log(log_level::debug, std::move(fmt), std::forward<Args>(args)...);
425 }
433 template <typename... Args>
434 void trace(format_info_t<Args...> fmt, Args&&... args) noexcept {
435 log(log_level::trace, std::move(fmt), std::forward<Args>(args)...);
436 }
437
440 const sstring& name() const noexcept {
441 return _name;
442 }
443
446 log_level level() const noexcept {
447 return _level.load(std::memory_order_relaxed);
448 }
449
452 void set_level(log_level level) noexcept {
453 _level.store(level, std::memory_order_relaxed);
454 }
455
457 static void set_ostream(std::ostream& out) noexcept;
458
460 static void set_ostream_enabled(bool enabled) noexcept;
461
463 [[deprecated("Use set_ostream_enabled instead")]]
464 static void set_stdout_enabled(bool enabled) noexcept;
465
471 static void set_syslog_enabled(bool enabled) noexcept;
472
480 static void set_shard_field_width(unsigned width) noexcept;
481
485 static void set_with_color(bool enabled) noexcept;
486};
487
496class logger_registry {
497 mutable std::mutex _mutex;
498 std::unordered_map<sstring, logger*> _loggers;
499public:
504 void set_all_loggers_level(log_level level);
505
510 log_level get_logger_level(sstring name) const;
511
517 void set_logger_level(sstring name, log_level level);
518
523 std::vector<sstring> get_all_logger_names();
524
528 void register_logger(logger* l);
532 void unregister_logger(logger* l);
536 void moved(logger* from, logger* to);
537};
538
539logger_registry& global_logger_registry();
540
542enum class logger_timestamp_style {
543 none,
544 boot,
545 real,
546};
547
549enum class logger_ostream_type {
550 none,
551#ifdef SEASTAR_LOGGER_TYPE_STDOUT
552 stdout __attribute__ ((deprecated ("use cout instead"))) = 1,
553 stderr __attribute__ ((deprecated ("use cerr instead"))) = 2,
554#endif
555 cout = 1,
556 cerr = 2,
557};
558
559struct logging_settings final {
560 std::unordered_map<sstring, log_level> logger_levels;
561 log_level default_level;
562 bool stdout_enabled;
563 bool syslog_enabled;
564 bool with_color;
565 logger_timestamp_style stdout_timestamp_style = logger_timestamp_style::real;
566 logger_ostream_type logger_ostream = logger_ostream_type::cerr;
567};
568
571void apply_logging_settings(const logging_settings&);
572
573SEASTAR_MODULE_EXPORT_END
574
576
577extern thread_local uint64_t logging_failures;
578
579sstring pretty_type_name(const std::type_info&);
580
581sstring level_name(log_level level);
582
583template <typename T>
584class logger_for : public logger {
585public:
586 logger_for() : logger(pretty_type_name(typeid(T))) {}
587};
588
590} // end seastar namespace
591
592// Pretty-printer for exceptions to be logged, e.g., std::current_exception().
593namespace std {
594std::ostream& operator<<(std::ostream&, const std::exception_ptr&);
595std::ostream& operator<<(std::ostream&, const std::exception&);
596std::ostream& operator<<(std::ostream&, const std::system_error&);
597}
598
599#if FMT_VERSION >= 90000
600template <> struct fmt::formatter<std::exception_ptr> : fmt::ostream_formatter {};
601template <> struct fmt::formatter<std::exception> : fmt::ostream_formatter {};
602template <> struct fmt::formatter<std::system_error> : fmt::ostream_formatter {};
603#endif
604
log_level
log level used with
Definition: log.hh:55
@ 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