]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/include/seastar/util/log.hh
update source to Ceph Pacific 16.2.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>
11fdf7f2
TL
27#include <unordered_map>
28#include <exception>
29#include <iosfwd>
30#include <atomic>
31#include <mutex>
32#include <boost/lexical_cast.hpp>
f67539c2 33#include <fmt/format.h>
11fdf7f2
TL
34
35
36/// \addtogroup logging
37/// @{
38
39namespace seastar {
40
41/// \brief log level used with \see {logger}
42/// used with the logger.do_log method.
43/// Levels are in increasing order. That is if you want to see debug(3) logs you
44/// will also see error(0), warn(1), info(2).
45///
46enum class log_level {
47 error,
48 warn,
49 info,
50 debug,
51 trace,
52};
53
54std::ostream& operator<<(std::ostream& out, log_level level);
55std::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
60namespace boost {
61template<>
62seastar::log_level lexical_cast(const std::string& source);
63
64}
65
66namespace seastar {
67
68class logger;
69class logger_registry;
70
9f95a23c 71/// \brief Logger class for ostream or syslog.
11fdf7f2
TL
72///
73/// Java style api for logging.
74/// \code {.cpp}
75/// static seastar::logger logger("lsa-api");
76/// logger.info("Triggering compaction");
77/// \endcode
78/// The output format is: (depending on level)
79/// DEBUG %Y-%m-%d %T,%03d [shard 0] - "your msg" \n
80///
f67539c2 81/// It is possible to rate-limit log messages, see \ref logger::rate_limit.
11fdf7f2
TL
82class logger {
83 sstring _name;
84 std::atomic<log_level> _level = { log_level::info };
9f95a23c
TL
85 static std::ostream* _out;
86 static std::atomic<bool> _ostream;
11fdf7f2 87 static std::atomic<bool> _syslog;
f67539c2
TL
88
89public:
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;
11fdf7f2 94 };
f67539c2
TL
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); }
11fdf7f2 105 };
f67539c2
TL
106
107private:
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;
112public:
113 /// Apply a rate limit to log message(s)
114 ///
115 /// Pass this to \ref logger::log() to apply a rate limit to the message.
116 /// The rate limit is applied to all \ref logger::log() calls this rate
117 /// limit is passed to. Example:
118 ///
119 /// void handle_request() {
120 /// static thread_local logger::rate_limit my_rl(std::chrono::seconds(10));
121 /// // ...
122 /// 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");
123 /// // ...
124 /// }
125 ///
126 /// The rate limit ensures that at most one message per interval will be
127 /// logged. If there were messages dropped due to rate-limiting the
128 /// following snippet will be prepended to the first non-dropped log
129 /// messages:
130 ///
131 /// (rate limiting dropped $N similar messages)
132 ///
133 /// Where $N is the number of messages dropped.
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
11fdf7f2
TL
155public:
156 explicit logger(sstring name);
157 logger(logger&& x);
158 ~logger();
159
f67539c2 160 bool is_shard_zero() noexcept;
11fdf7f2
TL
161
162 /// Test if desired log level is enabled
163 ///
164 /// \param level - enum level value (info|error...)
165 /// \return true if the log level has been enabled.
f67539c2 166 bool is_enabled(log_level level) const noexcept {
11fdf7f2
TL
167 return __builtin_expect(level <= _level.load(std::memory_order_relaxed), false);
168 }
169
170 /// logs to desired level if enabled, otherwise we ignore the log line
171 ///
f67539c2
TL
172 /// \param fmt - {fmt} style format
173 /// \param args - args to print string
174 ///
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
189 /// logs with a rate limit to desired level if enabled, otherwise we ignore the log line
190 ///
191 /// If there were messages dropped due to rate-limiting the following snippet
192 /// will be prepended to the first non-dropped log messages:
193 ///
194 /// (rate limiting dropped $N similar messages)
195 ///
196 /// Where $N is the number of messages dropped.
197 ///
198 /// \param rl - the \ref rate_limit to apply to this log
199 /// \param fmt - {fmt} style format
11fdf7f2
TL
200 /// \param args - args to print string
201 ///
202 template <typename... Args>
f67539c2
TL
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
219 /// \cond internal
220 /// logs to desired level if enabled, otherwise we ignore the log line
221 ///
222 /// \param writer a function which writes directly to the underlying log buffer
223 ///
224 /// This is a low level method for use cases where it is very important to
225 /// avoid any allocations. The \arg writer will be passed a
226 /// internal::log_buf::inserter_iterator that allows it to write into the log
227 /// buffer directly, avoiding the use of any intermediary buffers.
228 void log(log_level level, log_writer& writer) noexcept {
11fdf7f2
TL
229 if (is_enabled(level)) {
230 try {
f67539c2 231 do_log(level, writer);
11fdf7f2
TL
232 } catch (...) {
233 failed_to_log(std::current_exception());
234 }
235 }
236 }
f67539c2
TL
237 /// logs to desired level if enabled, otherwise we ignore the log line
238 ///
239 /// \param writer a function which writes directly to the underlying log buffer
240 ///
241 /// This is a low level method for use cases where it is very important to
242 /// avoid any allocations. The \arg writer will be passed a
243 /// internal::log_buf::inserter_iterator that allows it to write into the log
244 /// buffer directly, avoiding the use of any intermediary buffers.
245 /// This is rate-limited version, see \ref rate_limit.
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 }
261 /// \endcond
11fdf7f2
TL
262
263 /// Log with error tag:
264 /// ERROR %Y-%m-%d %T,%03d [shard 0] - "your msg" \n
265 ///
f67539c2 266 /// \param fmt - {fmt} style format
11fdf7f2
TL
267 /// \param args - args to print string
268 ///
269 template <typename... Args>
f67539c2 270 void error(const char* fmt, Args&&... args) noexcept {
11fdf7f2
TL
271 log(log_level::error, fmt, std::forward<Args>(args)...);
272 }
273 /// Log with warning tag:
274 /// WARN %Y-%m-%d %T,%03d [shard 0] - "your msg" \n
275 ///
f67539c2 276 /// \param fmt - {fmt} style format
11fdf7f2
TL
277 /// \param args - args to print string
278 ///
279 template <typename... Args>
f67539c2 280 void warn(const char* fmt, Args&&... args) noexcept {
11fdf7f2
TL
281 log(log_level::warn, fmt, std::forward<Args>(args)...);
282 }
283 /// Log with info tag:
284 /// INFO %Y-%m-%d %T,%03d [shard 0] - "your msg" \n
285 ///
f67539c2 286 /// \param fmt - {fmt} style format
11fdf7f2
TL
287 /// \param args - args to print string
288 ///
289 template <typename... Args>
f67539c2 290 void info(const char* fmt, Args&&... args) noexcept {
11fdf7f2
TL
291 log(log_level::info, fmt, std::forward<Args>(args)...);
292 }
293 /// Log with info tag on shard zero only:
294 /// INFO %Y-%m-%d %T,%03d [shard 0] - "your msg" \n
295 ///
f67539c2 296 /// \param fmt - {fmt} style format
11fdf7f2
TL
297 /// \param args - args to print string
298 ///
299 template <typename... Args>
f67539c2 300 void info0(const char* fmt, Args&&... args) noexcept {
11fdf7f2
TL
301 if (is_shard_zero()) {
302 log(log_level::info, fmt, std::forward<Args>(args)...);
303 }
304 }
f67539c2 305 /// Log with debug tag:
11fdf7f2
TL
306 /// DEBUG %Y-%m-%d %T,%03d [shard 0] - "your msg" \n
307 ///
f67539c2 308 /// \param fmt - {fmt} style format
11fdf7f2
TL
309 /// \param args - args to print string
310 ///
311 template <typename... Args>
f67539c2 312 void debug(const char* fmt, Args&&... args) noexcept {
11fdf7f2
TL
313 log(log_level::debug, fmt, std::forward<Args>(args)...);
314 }
315 /// Log with trace tag:
316 /// TRACE %Y-%m-%d %T,%03d [shard 0] - "your msg" \n
317 ///
f67539c2 318 /// \param fmt - {fmt} style format
11fdf7f2
TL
319 /// \param args - args to print string
320 ///
321 template <typename... Args>
f67539c2 322 void trace(const char* fmt, Args&&... args) noexcept {
11fdf7f2
TL
323 log(log_level::trace, fmt, std::forward<Args>(args)...);
324 }
325
326 /// \return name of the logger. Usually one logger per module
327 ///
f67539c2 328 const sstring& name() const noexcept {
11fdf7f2
TL
329 return _name;
330 }
331
332 /// \return current log level for this logger
333 ///
f67539c2 334 log_level level() const noexcept {
11fdf7f2
TL
335 return _level.load(std::memory_order_relaxed);
336 }
337
338 /// \param level - set the log level
339 ///
f67539c2 340 void set_level(log_level level) noexcept {
11fdf7f2
TL
341 _level.store(level, std::memory_order_relaxed);
342 }
343
9f95a23c 344 /// Set output stream, default is std::cerr
f67539c2 345 static void set_ostream(std::ostream& out) noexcept;
9f95a23c
TL
346
347 /// Also output to ostream. default is true
f67539c2 348 static void set_ostream_enabled(bool enabled) noexcept;
9f95a23c 349
11fdf7f2 350 /// Also output to stdout. default is true
9f95a23c 351 [[deprecated("Use set_ostream_enabled instead")]]
f67539c2 352 static void set_stdout_enabled(bool enabled) noexcept;
11fdf7f2
TL
353
354 /// Also output to syslog. default is false
355 ///
356 /// NOTE: syslog() can block, which will stall the reactor thread.
357 /// this should be rare (will have to fill the pipe buffer
358 /// before syslogd can clear it) but can happen.
f67539c2 359 static void set_syslog_enabled(bool enabled) noexcept;
11fdf7f2
TL
360};
361
362/// \brief used to keep a static registry of loggers
363/// since the typical use case is to do:
364/// \code {.cpp}
365/// static seastar::logger("my_module");
366/// \endcode
367/// this class is used to wrap around the static map
368/// that holds pointers to all logs
369///
370class logger_registry {
371 mutable std::mutex _mutex;
372 std::unordered_map<sstring, logger*> _loggers;
373public:
374 /// loops through all registered loggers and sets the log level
375 /// Note: this method locks
376 ///
377 /// \param level - desired level: error,info,...
378 void set_all_loggers_level(log_level level);
379
380 /// Given a name for a logger returns the log_level enum
381 /// Note: this method locks
382 ///
383 /// \return log_level for the given logger name
384 log_level get_logger_level(sstring name) const;
385
386 /// Sets the log level for a given logger
387 /// Note: this method locks
388 ///
389 /// \param name - name of logger
390 /// \param level - desired level of logging
391 void set_logger_level(sstring name, log_level level);
392
393 /// Returns a list of registered loggers
394 /// Note: this method locks
395 ///
396 /// \return all registered loggers
397 std::vector<sstring> get_all_logger_names();
398
399 /// Registers a logger with the static map
400 /// Note: this method locks
401 ///
402 void register_logger(logger* l);
403 /// Unregisters a logger with the static map
404 /// Note: this method locks
405 ///
406 void unregister_logger(logger* l);
407 /// Swaps the logger given the from->name() in the static map
408 /// Note: this method locks
409 ///
410 void moved(logger* from, logger* to);
411};
412
413logger_registry& global_logger_registry();
414
415enum class logger_timestamp_style {
416 none,
417 boot,
418 real,
419};
420
9f95a23c
TL
421enum class logger_ostream_type {
422 none,
423 stdout,
424 stderr,
425};
426
11fdf7f2
TL
427struct 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;
9f95a23c 433 logger_ostream_type logger_ostream = logger_ostream_type::stderr;
11fdf7f2
TL
434};
435
436/// Shortcut for configuring the logging system all at once.
437///
438void apply_logging_settings(const logging_settings&);
439
440/// \cond internal
441
442extern thread_local uint64_t logging_failures;
443
444sstring pretty_type_name(const std::type_info&);
445
446sstring level_name(log_level level);
447
448template <typename T>
449class logger_for : public logger {
450public:
451 logger_for() : logger(pretty_type_name(typeid(T))) {}
452};
453
11fdf7f2
TL
454/// \endcond
455} // end seastar namespace
456
457// Pretty-printer for exceptions to be logged, e.g., std::current_exception().
458namespace std {
459std::ostream& operator<<(std::ostream&, const std::exception_ptr&);
460std::ostream& operator<<(std::ostream&, const std::exception&);
461std::ostream& operator<<(std::ostream&, const std::system_error&);
462}
463
464/// @}