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 <unordered_map>
28 #include <exception>
29 #include <iosfwd>
30 #include <atomic>
31 #include <mutex>
32 #include <boost/lexical_cast.hpp>
33 #include <fmt/format.h>
34 
35 
38 
39 namespace seastar {
40 
46 enum class log_level {
47  error,
48  warn,
49  info,
50  debug,
51  trace,
52 };
53 
54 std::ostream& operator<<(std::ostream& out, log_level level);
55 std::istream& operator>>(std::istream& in, log_level& level);
56 }
57 
58 // Boost doesn't auto-deduce the existence of the streaming operators for some reason
59 
60 namespace boost {
61 template<>
62 seastar::log_level lexical_cast(const std::string& source);
63 
64 }
65 
66 namespace seastar {
67 
68 class logger;
69 class logger_registry;
70 
82 class logger {
83  sstring _name;
84  std::atomic<log_level> _level = { log_level::info };
85  static std::ostream* _out;
86  static std::atomic<bool> _ostream;
87  static std::atomic<bool> _syslog;
88 
89 public:
90  class log_writer {
91  public:
92  virtual ~log_writer() = default;
93  virtual internal::log_buf::inserter_iterator operator()(internal::log_buf::inserter_iterator) = 0;
94  };
95  template <typename Func>
96  SEASTAR_CONCEPT(requires requires (Func fn, internal::log_buf::inserter_iterator it) {
97  it = fn(it);
98  })
99  class lambda_log_writer : public log_writer {
100  Func _func;
101  public:
102  lambda_log_writer(Func&& func) : _func(std::forward<Func>(func)) { }
103  virtual ~lambda_log_writer() override = default;
104  virtual internal::log_buf::inserter_iterator operator()(internal::log_buf::inserter_iterator it) override { return _func(it); }
105  };
106 
107 private:
108 
109  // We can't use an std::function<> as it potentially allocates.
110  void do_log(log_level level, log_writer& writer);
111  void failed_to_log(std::exception_ptr ex) noexcept;
112 public:
134  class rate_limit {
135  friend class logger;
136 
137  using clock = lowres_clock;
138 
139  private:
140  clock::duration _interval;
141  clock::time_point _next;
142  uint64_t _dropped_messages = 0;
143 
144  private:
145  bool check();
146  bool has_dropped_messages() const { return bool(_dropped_messages); }
147  uint64_t get_and_reset_dropped_messages() {
148  return std::exchange(_dropped_messages, 0);
149  }
150 
151  public:
152  explicit rate_limit(std::chrono::milliseconds interval);
153  };
154 
155 public:
156  explicit logger(sstring name);
157  logger(logger&& x);
158  ~logger();
159 
160  bool is_shard_zero() noexcept;
161 
166  bool is_enabled(log_level level) const noexcept {
167  return __builtin_expect(level <= _level.load(std::memory_order_relaxed), false);
168  }
169 
175  template <typename... Args>
176  void log(log_level level, const char* fmt, Args&&... args) noexcept {
177  if (is_enabled(level)) {
178  try {
179  lambda_log_writer writer([&] (internal::log_buf::inserter_iterator it) {
180  return fmt::format_to(it, fmt, std::forward<Args>(args)...);
181  });
182  do_log(level, writer);
183  } catch (...) {
184  failed_to_log(std::current_exception());
185  }
186  }
187  }
188 
202  template <typename... Args>
203  void log(log_level level, rate_limit& rl, const char* fmt, Args&&... args) noexcept {
204  if (is_enabled(level) && rl.check()) {
205  try {
206  lambda_log_writer writer([&] (internal::log_buf::inserter_iterator it) {
207  if (rl.has_dropped_messages()) {
208  it = fmt::format_to(it, "(rate limiting dropped {} similar messages) ", rl.get_and_reset_dropped_messages());
209  }
210  return fmt::format_to(it, fmt, std::forward<Args>(args)...);
211  });
212  do_log(level, writer);
213  } catch (...) {
214  failed_to_log(std::current_exception());
215  }
216  }
217  }
218 
228  void log(log_level level, log_writer& writer) noexcept {
229  if (is_enabled(level)) {
230  try {
231  do_log(level, writer);
232  } catch (...) {
233  failed_to_log(std::current_exception());
234  }
235  }
236  }
246  void log(log_level level, rate_limit& rl, log_writer& writer) noexcept {
247  if (is_enabled(level) && rl.check()) {
248  try {
249  lambda_log_writer writer_wrapper([&] (internal::log_buf::inserter_iterator it) {
250  if (rl.has_dropped_messages()) {
251  it = fmt::format_to(it, "(rate limiting dropped {} similar messages) ", rl.get_and_reset_dropped_messages());
252  }
253  return writer(it);
254  });
255  do_log(level, writer_wrapper);
256  } catch (...) {
257  failed_to_log(std::current_exception());
258  }
259  }
260  }
262 
269  template <typename... Args>
270  void error(const char* fmt, Args&&... args) noexcept {
271  log(log_level::error, fmt, std::forward<Args>(args)...);
272  }
279  template <typename... Args>
280  void warn(const char* fmt, Args&&... args) noexcept {
281  log(log_level::warn, fmt, std::forward<Args>(args)...);
282  }
289  template <typename... Args>
290  void info(const char* fmt, Args&&... args) noexcept {
291  log(log_level::info, fmt, std::forward<Args>(args)...);
292  }
299  template <typename... Args>
300  void info0(const char* fmt, Args&&... args) noexcept {
301  if (is_shard_zero()) {
302  log(log_level::info, fmt, std::forward<Args>(args)...);
303  }
304  }
311  template <typename... Args>
312  void debug(const char* fmt, Args&&... args) noexcept {
313  log(log_level::debug, fmt, std::forward<Args>(args)...);
314  }
321  template <typename... Args>
322  void trace(const char* fmt, Args&&... args) noexcept {
323  log(log_level::trace, fmt, std::forward<Args>(args)...);
324  }
325 
328  const sstring& name() const noexcept {
329  return _name;
330  }
331 
334  log_level level() const noexcept {
335  return _level.load(std::memory_order_relaxed);
336  }
337 
340  void set_level(log_level level) noexcept {
341  _level.store(level, std::memory_order_relaxed);
342  }
343 
345  static void set_ostream(std::ostream& out) noexcept;
346 
348  static void set_ostream_enabled(bool enabled) noexcept;
349 
351  [[deprecated("Use set_ostream_enabled instead")]]
352  static void set_stdout_enabled(bool enabled) noexcept;
353 
359  static void set_syslog_enabled(bool enabled) noexcept;
360 };
361 
371  mutable std::mutex _mutex;
372  std::unordered_map<sstring, logger*> _loggers;
373 public:
379 
384  log_level get_logger_level(sstring name) const;
385 
391  void set_logger_level(sstring name, log_level level);
392 
397  std::vector<sstring> get_all_logger_names();
398 
410  void moved(logger* from, logger* to);
411 };
412 
413 logger_registry& global_logger_registry();
414 
415 enum class logger_timestamp_style {
416  none,
417  boot,
418  real,
419 };
420 
421 enum class logger_ostream_type {
422  none,
423  stdout,
424  stderr,
425 };
426 
427 struct logging_settings final {
428  std::unordered_map<sstring, log_level> logger_levels;
429  log_level default_level;
430  bool stdout_enabled;
431  bool syslog_enabled;
432  logger_timestamp_style stdout_timestamp_style = logger_timestamp_style::real;
433  logger_ostream_type logger_ostream = logger_ostream_type::stderr;
434 };
435 
439 
441 
442 extern thread_local uint64_t logging_failures;
443 
444 sstring pretty_type_name(const std::type_info&);
445 
446 sstring level_name(log_level level);
447 
448 template <typename T>
449 class logger_for : public logger {
450 public:
451  logger_for() : logger(pretty_type_name(typeid(T))) {}
452 };
453 
455 } // end seastar namespace
456 
457 // Pretty-printer for exceptions to be logged, e.g., std::current_exception().
458 namespace std {
459 std::ostream& operator<<(std::ostream&, const std::exception_ptr&);
460 std::ostream& operator<<(std::ostream&, const std::exception&);
461 std::ostream& operator<<(std::ostream&, const std::system_error&);
462 }
463 
seastar::log_level
log_level
log level used with
Definition: log.hh:46
seastar::logger_registry::get_all_logger_names
std::vector< sstring > get_all_logger_names()
seastar
Seastar API namespace.
Definition: abort_on_ebadf.hh:24
seastar::logger::name
const sstring & name() const noexcept
Definition: log.hh:328
seastar::logger::is_enabled
bool is_enabled(log_level level) const noexcept
Definition: log.hh:166
seastar::logger_registry::moved
void moved(logger *from, logger *to)
seastar::logger::error
void error(const char *fmt, Args &&... args) noexcept
Definition: log.hh:270
seastar::logger::trace
void trace(const char *fmt, Args &&... args) noexcept
Definition: log.hh:322
seastar::logging_settings
Definition: log.hh:427
seastar::logger::set_ostream_enabled
static void set_ostream_enabled(bool enabled) noexcept
Also output to ostream. default is true.
seastar::logger::level
log_level level() const noexcept
Definition: log.hh:334
seastar::logger::set_ostream
static void set_ostream(std::ostream &out) noexcept
Set output stream, default is std::cerr.
seastar::logger
Logger class for ostream or syslog.
Definition: log.hh:82
seastar::logger_registry::register_logger
void register_logger(logger *l)
seastar::logger::warn
void warn(const char *fmt, Args &&... args) noexcept
Definition: log.hh:280
seastar::logger_registry
used to keep a static registry of loggers since the typical use case is to do:
Definition: log.hh:370
seastar::logger::info0
void info0(const char *fmt, Args &&... args) noexcept
Definition: log.hh:300
seastar::logger::set_level
void set_level(log_level level) noexcept
Definition: log.hh:340
seastar::logger::set_syslog_enabled
static void set_syslog_enabled(bool enabled) noexcept
seastar::logger::log
void log(log_level level, const char *fmt, Args &&... args) noexcept
Definition: log.hh:176
seastar::logger_registry::set_logger_level
void set_logger_level(sstring name, log_level level)
seastar::logger::lambda_log_writer
Definition: log.hh:99
seastar::logger::log
void log(log_level level, rate_limit &rl, const char *fmt, Args &&... args) noexcept
Definition: log.hh:203
seastar::logger::set_stdout_enabled
static void set_stdout_enabled(bool enabled) noexcept
Also output to stdout. default is true.
seastar::apply_logging_settings
void apply_logging_settings(const logging_settings &)
seastar::logger::info
void info(const char *fmt, Args &&... args) noexcept
Definition: log.hh:290
seastar::logger::log_writer
Definition: log.hh:90
seastar::logger_registry::set_all_loggers_level
void set_all_loggers_level(log_level level)
seastar::lowres_clock
Low-resolution and efficient steady clock.
Definition: lowres_clock.hh:106
seastar::logger::debug
void debug(const char *fmt, Args &&... args) noexcept
Definition: log.hh:312
seastar::logger::rate_limit
Definition: log.hh:134
seastar::logger_registry::get_logger_level
log_level get_logger_level(sstring name) const
seastar::logger_registry::unregister_logger
void unregister_logger(logger *l)