Seastar
High performance C++ framework for concurrent servers
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/concepts.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 <unordered_map>
32 #include <exception>
33 #include <iosfwd>
34 #include <atomic>
35 #include <mutex>
36 #include <boost/lexical_cast.hpp>
37 #include <fmt/format.h>
38 #endif
39 
42 
43 namespace seastar {
44 
45 SEASTAR_MODULE_EXPORT_BEGIN
46 
52 enum class log_level {
53  error,
54  warn,
55  info,
56  debug,
57  trace,
58 };
59 
60 std::ostream& operator<<(std::ostream& out, log_level level);
61 std::istream& operator>>(std::istream& in, log_level& level);
62 
63 SEASTAR_MODULE_EXPORT_END
64 }
65 
66 // Boost doesn't auto-deduce the existence of the streaming operators for some reason
67 
68 namespace boost {
69 template<>
70 seastar::log_level lexical_cast(const std::string& source);
71 
72 }
73 
74 namespace seastar {
75 SEASTAR_MODULE_EXPORT_BEGIN
76 class logger;
77 class logger_registry;
78 
90 class 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 
103 public:
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  SEASTAR_CONCEPT(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  struct format_info {
126  format_info(const char* format, compat::source_location loc = compat::source_location::current()) noexcept
127  : format(format)
128  , loc(loc)
129  {}
132  format_info(std::string_view format, compat::source_location loc = compat::source_location::current()) noexcept
133  : format(format)
134  , loc(loc)
135  {}
137  format_info(compat::source_location loc = compat::source_location::current()) noexcept
138  : format()
139  , loc(loc)
140  {}
141  std::string_view format;
142  compat::source_location loc;
143  };
144 
145 private:
146 
147  // We can't use an std::function<> as it potentially allocates.
148  void do_log(log_level level, log_writer& writer);
149  void failed_to_log(std::exception_ptr ex, format_info fmt) noexcept;
150 
151  class silencer {
152  public:
153  silencer() noexcept {
154  silent = true;
155  }
156 
157  ~silencer() {
158  silent = false;
159  }
160  };
161 
162 public:
184  class rate_limit {
185  friend class logger;
186 
187  using clock = lowres_clock;
188 
189  private:
190  clock::duration _interval;
191  clock::time_point _next;
192  uint64_t _dropped_messages = 0;
193 
194  private:
195  bool check();
196  bool has_dropped_messages() const { return bool(_dropped_messages); }
197  uint64_t get_and_reset_dropped_messages() {
198  return std::exchange(_dropped_messages, 0);
199  }
200 
201  public:
202  explicit rate_limit(std::chrono::milliseconds interval);
203  };
204 
205 public:
206  explicit logger(sstring name);
207  logger(logger&& x);
208  ~logger();
209 
210  bool is_shard_zero() noexcept;
211 
216  bool is_enabled(log_level level) const noexcept {
217  return __builtin_expect(level <= _level.load(std::memory_order_relaxed), false) && !silent;
218  }
219 
226  template <typename... Args>
227  void log(log_level level, format_info fmt, Args&&... args) noexcept {
228  if (is_enabled(level)) {
229  try {
230  lambda_log_writer writer([&] (internal::log_buf::inserter_iterator it) {
231 #if FMT_VERSION >= 80000
232  return fmt::format_to(it, fmt::runtime(fmt.format), std::forward<Args>(args)...);
233 #else
234  return fmt::format_to(it, fmt.format, std::forward<Args>(args)...);
235 #endif
236  });
237  do_log(level, writer);
238  } catch (...) {
239  failed_to_log(std::current_exception(), std::move(fmt));
240  }
241  }
242  }
243 
258  template <typename... Args>
259  void log(log_level level, rate_limit& rl, format_info fmt, Args&&... args) noexcept {
260  if (is_enabled(level) && rl.check()) {
261  try {
262  lambda_log_writer writer([&] (internal::log_buf::inserter_iterator it) {
263  if (rl.has_dropped_messages()) {
264  it = fmt::format_to(it, "(rate limiting dropped {} similar messages) ", rl.get_and_reset_dropped_messages());
265  }
266 #if FMT_VERSION >= 80000
267  return fmt::format_to(it, fmt::runtime(fmt.format), std::forward<Args>(args)...);
268 #else
269  return fmt::format_to(it, fmt.format, std::forward<Args>(args)...);
270 #endif
271  });
272  do_log(level, writer);
273  } catch (...) {
274  failed_to_log(std::current_exception(), std::move(fmt));
275  }
276  }
277  }
278 
289  void log(log_level level, log_writer& writer, format_info fmt = {}) noexcept {
290  if (is_enabled(level)) {
291  try {
292  do_log(level, writer);
293  } catch (...) {
294  failed_to_log(std::current_exception(), std::move(fmt));
295  }
296  }
297  }
308  void log(log_level level, rate_limit& rl, log_writer& writer, format_info fmt = {}) noexcept {
309  if (is_enabled(level) && rl.check()) {
310  try {
311  lambda_log_writer writer_wrapper([&] (internal::log_buf::inserter_iterator it) {
312  if (rl.has_dropped_messages()) {
313  it = fmt::format_to(it, "(rate limiting dropped {} similar messages) ", rl.get_and_reset_dropped_messages());
314  }
315  return writer(it);
316  });
317  do_log(level, writer_wrapper);
318  } catch (...) {
319  failed_to_log(std::current_exception(), std::move(fmt));
320  }
321  }
322  }
324 
332  template <typename... Args>
333  void error(format_info fmt, Args&&... args) noexcept {
334  log(log_level::error, std::move(fmt), std::forward<Args>(args)...);
335  }
343  template <typename... Args>
344  void warn(format_info fmt, Args&&... args) noexcept {
345  log(log_level::warn, std::move(fmt), std::forward<Args>(args)...);
346  }
354  template <typename... Args>
355  void info(format_info fmt, Args&&... args) noexcept {
356  log(log_level::info, std::move(fmt), std::forward<Args>(args)...);
357  }
365  template <typename... Args>
366  void info0(format_info fmt, Args&&... args) noexcept {
367  if (is_shard_zero()) {
368  log(log_level::info, std::move(fmt), std::forward<Args>(args)...);
369  }
370  }
378  template <typename... Args>
379  void debug(format_info fmt, Args&&... args) noexcept {
380  log(log_level::debug, std::move(fmt), std::forward<Args>(args)...);
381  }
389  template <typename... Args>
390  void trace(format_info fmt, Args&&... args) noexcept {
391  log(log_level::trace, std::move(fmt), std::forward<Args>(args)...);
392  }
393 
396  const sstring& name() const noexcept {
397  return _name;
398  }
399 
402  log_level level() const noexcept {
403  return _level.load(std::memory_order_relaxed);
404  }
405 
408  void set_level(log_level level) noexcept {
409  _level.store(level, std::memory_order_relaxed);
410  }
411 
413  static void set_ostream(std::ostream& out) noexcept;
414 
416  static void set_ostream_enabled(bool enabled) noexcept;
417 
419  [[deprecated("Use set_ostream_enabled instead")]]
420  static void set_stdout_enabled(bool enabled) noexcept;
421 
427  static void set_syslog_enabled(bool enabled) noexcept;
428 
436  static void set_shard_field_width(unsigned width) noexcept;
437 
441  static void set_with_color(bool enabled) noexcept;
442 };
443 
453  mutable std::mutex _mutex;
454  std::unordered_map<sstring, logger*> _loggers;
455 public:
461 
466  log_level get_logger_level(sstring name) const;
467 
473  void set_logger_level(sstring name, log_level level);
474 
479  std::vector<sstring> get_all_logger_names();
480 
492  void moved(logger* from, logger* to);
493 };
494 
495 logger_registry& global_logger_registry();
496 
499  none,
500  boot,
501  real,
502 };
503 
506  none,
507 #ifdef SEASTAR_LOGGER_TYPE_STDOUT
508  stdout __attribute__ ((deprecated ("use cout instead"))) = 1,
509  stderr __attribute__ ((deprecated ("use cerr instead"))) = 2,
510 #endif
511  cout = 1,
512  cerr = 2,
513 };
514 
515 struct logging_settings final {
516  std::unordered_map<sstring, log_level> logger_levels;
517  log_level default_level;
518  bool stdout_enabled;
519  bool syslog_enabled;
520  bool with_color;
521  logger_timestamp_style stdout_timestamp_style = logger_timestamp_style::real;
522  logger_ostream_type logger_ostream = logger_ostream_type::cerr;
523 };
524 
528 
529 SEASTAR_MODULE_EXPORT_END
530 
532 
533 extern thread_local uint64_t logging_failures;
534 
535 sstring pretty_type_name(const std::type_info&);
536 
537 sstring level_name(log_level level);
538 
539 template <typename T>
540 class logger_for : public logger {
541 public:
542  logger_for() : logger(pretty_type_name(typeid(T))) {}
543 };
544 
546 } // end seastar namespace
547 
548 // Pretty-printer for exceptions to be logged, e.g., std::current_exception().
549 namespace std {
550 std::ostream& operator<<(std::ostream&, const std::exception_ptr&);
551 std::ostream& operator<<(std::ostream&, const std::exception&);
552 std::ostream& operator<<(std::ostream&, const std::system_error&);
553 }
554 
555 #if FMT_VERSION >= 90000
556 template <> struct fmt::formatter<std::exception_ptr> : fmt::ostream_formatter {};
557 template <> struct fmt::formatter<std::exception> : fmt::ostream_formatter {};
558 template <> struct fmt::formatter<std::system_error> : fmt::ostream_formatter {};
559 #endif
560 
Definition: log.hh:104
used to keep a static registry of loggers since the typical use case is to do:
Definition: log.hh:452
std::vector< sstring > get_all_logger_names()
void set_logger_level(sstring name, log_level level)
void register_logger(logger *l)
void set_all_loggers_level(log_level level)
void moved(logger *from, logger *to)
log_level get_logger_level(sstring name) const
void unregister_logger(logger *l)
Logger class for ostream or syslog.
Definition: log.hh:90
static void set_syslog_enabled(bool enabled) noexcept
void error(format_info fmt, Args &&... args) noexcept
Definition: log.hh:333
void set_level(log_level level) noexcept
Definition: log.hh:408
void debug(format_info fmt, Args &&... args) noexcept
Definition: log.hh:379
void info0(format_info fmt, Args &&... args) noexcept
Definition: log.hh:366
log_level level() const noexcept
Definition: log.hh:402
static void set_shard_field_width(unsigned width) noexcept
void trace(format_info fmt, Args &&... args) noexcept
Definition: log.hh:390
static void set_with_color(bool enabled) noexcept
static void set_ostream(std::ostream &out) noexcept
Set output stream, default is std::cerr.
const sstring & name() const noexcept
Definition: log.hh:396
static void set_stdout_enabled(bool enabled) noexcept
Also output to stdout. default is true.
static void set_ostream_enabled(bool enabled) noexcept
Also output to ostream. default is true.
void info(format_info fmt, Args &&... args) noexcept
Definition: log.hh:355
void warn(format_info fmt, Args &&... args) noexcept
Definition: log.hh:344
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
logger_timestamp_style
Timestamp style.
Definition: log.hh:498
logger_ostream_type
Output stream to use for logging.
Definition: log.hh:505
sstring format(const char *fmt, A &&... a)
Definition: print.hh:142
log_level
log level used with
Definition: log.hh:52
void apply_logging_settings(const logging_settings &)
Definition: log.hh:515