]>
Commit | Line | Data |
---|---|---|
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> | |
24 | #include <unordered_map> | |
25 | #include <exception> | |
26 | #include <iosfwd> | |
27 | #include <atomic> | |
28 | #include <mutex> | |
29 | #include <boost/lexical_cast.hpp> | |
30 | ||
31 | ||
32 | /// \addtogroup logging | |
33 | /// @{ | |
34 | ||
35 | namespace seastar { | |
36 | ||
37 | /// \brief log level used with \see {logger} | |
38 | /// used with the logger.do_log method. | |
39 | /// Levels are in increasing order. That is if you want to see debug(3) logs you | |
40 | /// will also see error(0), warn(1), info(2). | |
41 | /// | |
42 | enum class log_level { | |
43 | error, | |
44 | warn, | |
45 | info, | |
46 | debug, | |
47 | trace, | |
48 | }; | |
49 | ||
50 | std::ostream& operator<<(std::ostream& out, log_level level); | |
51 | std::istream& operator>>(std::istream& in, log_level& level); | |
52 | } | |
53 | ||
54 | // Boost doesn't auto-deduce the existence of the streaming operators for some reason | |
55 | ||
56 | namespace boost { | |
57 | template<> | |
58 | seastar::log_level lexical_cast(const std::string& source); | |
59 | ||
60 | } | |
61 | ||
62 | namespace seastar { | |
63 | ||
64 | class logger; | |
65 | class logger_registry; | |
66 | ||
67 | /// \brief Logger class for stdout or syslog. | |
68 | /// | |
69 | /// Java style api for logging. | |
70 | /// \code {.cpp} | |
71 | /// static seastar::logger logger("lsa-api"); | |
72 | /// logger.info("Triggering compaction"); | |
73 | /// \endcode | |
74 | /// The output format is: (depending on level) | |
75 | /// DEBUG %Y-%m-%d %T,%03d [shard 0] - "your msg" \n | |
76 | /// | |
77 | class logger { | |
78 | sstring _name; | |
79 | std::atomic<log_level> _level = { log_level::info }; | |
80 | static std::atomic<bool> _stdout; | |
81 | static std::atomic<bool> _syslog; | |
82 | private: | |
83 | struct stringer { | |
84 | void (*append)(std::ostream& os, const void* object); | |
85 | const void* object; | |
86 | }; | |
87 | template <typename Arg> | |
88 | stringer stringer_for(const Arg& arg) { | |
89 | return stringer{ | |
90 | [] (std::ostream& os, const void* object) { | |
91 | os << *static_cast<const std::remove_reference_t<Arg>*>(object); | |
92 | }, | |
93 | &arg | |
94 | }; | |
95 | }; | |
96 | template <typename... Args> | |
97 | void do_log(log_level level, const char* fmt, Args&&... args); | |
98 | void really_do_log(log_level level, const char* fmt, const stringer* stringers, size_t n); | |
99 | void failed_to_log(std::exception_ptr ex); | |
100 | public: | |
101 | explicit logger(sstring name); | |
102 | logger(logger&& x); | |
103 | ~logger(); | |
104 | ||
105 | bool is_shard_zero(); | |
106 | ||
107 | /// Test if desired log level is enabled | |
108 | /// | |
109 | /// \param level - enum level value (info|error...) | |
110 | /// \return true if the log level has been enabled. | |
111 | bool is_enabled(log_level level) const { | |
112 | return __builtin_expect(level <= _level.load(std::memory_order_relaxed), false); | |
113 | } | |
114 | ||
115 | /// logs to desired level if enabled, otherwise we ignore the log line | |
116 | /// | |
117 | /// \param fmt - printf style format | |
118 | /// \param args - args to print string | |
119 | /// | |
120 | template <typename... Args> | |
121 | void log(log_level level, const char* fmt, const Args&... args) { | |
122 | if (is_enabled(level)) { | |
123 | try { | |
124 | do_log(level, fmt, args...); | |
125 | } catch (...) { | |
126 | failed_to_log(std::current_exception()); | |
127 | } | |
128 | } | |
129 | } | |
130 | ||
131 | /// Log with error tag: | |
132 | /// ERROR %Y-%m-%d %T,%03d [shard 0] - "your msg" \n | |
133 | /// | |
134 | /// \param fmt - printf style format | |
135 | /// \param args - args to print string | |
136 | /// | |
137 | template <typename... Args> | |
138 | void error(const char* fmt, Args&&... args) { | |
139 | log(log_level::error, fmt, std::forward<Args>(args)...); | |
140 | } | |
141 | /// Log with warning tag: | |
142 | /// WARN %Y-%m-%d %T,%03d [shard 0] - "your msg" \n | |
143 | /// | |
144 | /// \param fmt - printf style format | |
145 | /// \param args - args to print string | |
146 | /// | |
147 | template <typename... Args> | |
148 | void warn(const char* fmt, Args&&... args) { | |
149 | log(log_level::warn, fmt, std::forward<Args>(args)...); | |
150 | } | |
151 | /// Log with info tag: | |
152 | /// INFO %Y-%m-%d %T,%03d [shard 0] - "your msg" \n | |
153 | /// | |
154 | /// \param fmt - printf style format | |
155 | /// \param args - args to print string | |
156 | /// | |
157 | template <typename... Args> | |
158 | void info(const char* fmt, Args&&... args) { | |
159 | log(log_level::info, fmt, std::forward<Args>(args)...); | |
160 | } | |
161 | /// Log with info tag on shard zero only: | |
162 | /// INFO %Y-%m-%d %T,%03d [shard 0] - "your msg" \n | |
163 | /// | |
164 | /// \param fmt - printf style format | |
165 | /// \param args - args to print string | |
166 | /// | |
167 | template <typename... Args> | |
168 | void info0(const char* fmt, Args&&... args) { | |
169 | if (is_shard_zero()) { | |
170 | log(log_level::info, fmt, std::forward<Args>(args)...); | |
171 | } | |
172 | } | |
173 | /// Log with info tag: | |
174 | /// DEBUG %Y-%m-%d %T,%03d [shard 0] - "your msg" \n | |
175 | /// | |
176 | /// \param fmt - printf style format | |
177 | /// \param args - args to print string | |
178 | /// | |
179 | template <typename... Args> | |
180 | void debug(const char* fmt, Args&&... args) { | |
181 | log(log_level::debug, fmt, std::forward<Args>(args)...); | |
182 | } | |
183 | /// Log with trace tag: | |
184 | /// TRACE %Y-%m-%d %T,%03d [shard 0] - "your msg" \n | |
185 | /// | |
186 | /// \param fmt - printf style format | |
187 | /// \param args - args to print string | |
188 | /// | |
189 | template <typename... Args> | |
190 | void trace(const char* fmt, Args&&... args) { | |
191 | log(log_level::trace, fmt, std::forward<Args>(args)...); | |
192 | } | |
193 | ||
194 | /// \return name of the logger. Usually one logger per module | |
195 | /// | |
196 | const sstring& name() const { | |
197 | return _name; | |
198 | } | |
199 | ||
200 | /// \return current log level for this logger | |
201 | /// | |
202 | log_level level() const { | |
203 | return _level.load(std::memory_order_relaxed); | |
204 | } | |
205 | ||
206 | /// \param level - set the log level | |
207 | /// | |
208 | void set_level(log_level level) { | |
209 | _level.store(level, std::memory_order_relaxed); | |
210 | } | |
211 | ||
212 | /// Also output to stdout. default is true | |
213 | static void set_stdout_enabled(bool enabled); | |
214 | ||
215 | /// Also output to syslog. default is false | |
216 | /// | |
217 | /// NOTE: syslog() can block, which will stall the reactor thread. | |
218 | /// this should be rare (will have to fill the pipe buffer | |
219 | /// before syslogd can clear it) but can happen. | |
220 | static void set_syslog_enabled(bool enabled); | |
221 | }; | |
222 | ||
223 | /// \brief used to keep a static registry of loggers | |
224 | /// since the typical use case is to do: | |
225 | /// \code {.cpp} | |
226 | /// static seastar::logger("my_module"); | |
227 | /// \endcode | |
228 | /// this class is used to wrap around the static map | |
229 | /// that holds pointers to all logs | |
230 | /// | |
231 | class logger_registry { | |
232 | mutable std::mutex _mutex; | |
233 | std::unordered_map<sstring, logger*> _loggers; | |
234 | public: | |
235 | /// loops through all registered loggers and sets the log level | |
236 | /// Note: this method locks | |
237 | /// | |
238 | /// \param level - desired level: error,info,... | |
239 | void set_all_loggers_level(log_level level); | |
240 | ||
241 | /// Given a name for a logger returns the log_level enum | |
242 | /// Note: this method locks | |
243 | /// | |
244 | /// \return log_level for the given logger name | |
245 | log_level get_logger_level(sstring name) const; | |
246 | ||
247 | /// Sets the log level for a given logger | |
248 | /// Note: this method locks | |
249 | /// | |
250 | /// \param name - name of logger | |
251 | /// \param level - desired level of logging | |
252 | void set_logger_level(sstring name, log_level level); | |
253 | ||
254 | /// Returns a list of registered loggers | |
255 | /// Note: this method locks | |
256 | /// | |
257 | /// \return all registered loggers | |
258 | std::vector<sstring> get_all_logger_names(); | |
259 | ||
260 | /// Registers a logger with the static map | |
261 | /// Note: this method locks | |
262 | /// | |
263 | void register_logger(logger* l); | |
264 | /// Unregisters a logger with the static map | |
265 | /// Note: this method locks | |
266 | /// | |
267 | void unregister_logger(logger* l); | |
268 | /// Swaps the logger given the from->name() in the static map | |
269 | /// Note: this method locks | |
270 | /// | |
271 | void moved(logger* from, logger* to); | |
272 | }; | |
273 | ||
274 | logger_registry& global_logger_registry(); | |
275 | ||
276 | enum class logger_timestamp_style { | |
277 | none, | |
278 | boot, | |
279 | real, | |
280 | }; | |
281 | ||
282 | struct logging_settings final { | |
283 | std::unordered_map<sstring, log_level> logger_levels; | |
284 | log_level default_level; | |
285 | bool stdout_enabled; | |
286 | bool syslog_enabled; | |
287 | logger_timestamp_style stdout_timestamp_style = logger_timestamp_style::real; | |
288 | }; | |
289 | ||
290 | /// Shortcut for configuring the logging system all at once. | |
291 | /// | |
292 | void apply_logging_settings(const logging_settings&); | |
293 | ||
294 | /// \cond internal | |
295 | ||
296 | extern thread_local uint64_t logging_failures; | |
297 | ||
298 | sstring pretty_type_name(const std::type_info&); | |
299 | ||
300 | sstring level_name(log_level level); | |
301 | ||
302 | template <typename T> | |
303 | class logger_for : public logger { | |
304 | public: | |
305 | logger_for() : logger(pretty_type_name(typeid(T))) {} | |
306 | }; | |
307 | ||
308 | template <typename... Args> | |
309 | void | |
310 | logger::do_log(log_level level, const char* fmt, Args&&... args) { | |
311 | [&](auto... stringers) { | |
312 | stringer s[sizeof...(stringers)] = {stringers...}; | |
313 | this->really_do_log(level, fmt, s, sizeof...(stringers)); | |
314 | } (stringer_for<Args>(std::forward<Args>(args))...); | |
315 | } | |
316 | ||
317 | /// \endcond | |
318 | } // end seastar namespace | |
319 | ||
320 | // Pretty-printer for exceptions to be logged, e.g., std::current_exception(). | |
321 | namespace std { | |
322 | std::ostream& operator<<(std::ostream&, const std::exception_ptr&); | |
323 | std::ostream& operator<<(std::ostream&, const std::exception&); | |
324 | std::ostream& operator<<(std::ostream&, const std::system_error&); | |
325 | } | |
326 | ||
327 | /// @} |