Flow 2.0.0
Flow project: Full implementation reference.
log.hpp
Go to the documentation of this file.
1/* Flow
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
19#pragma once
20
21#include "flow/log/log_fwd.hpp"
24#include "flow/util/util.hpp"
27#include <chrono>
28#include <string>
29#include <typeinfo>
30
31// Macros. These (conceptually) belong to the flow::log namespace (hence the prefix for each macro).
32
33/**
34 * Logs a WARNING message into flow::log::Logger `*get_logger()` with flow::log::Component `get_log_component()`, if
35 * such logging is enabled by that `Logger`. Supplies context information
36 * to be potentially logged with message, like current time, source file/line/function,
37 * and thread ID/nickname info, in addition to the message, component, and severity.
38 * The severity checked against (and potentially logged in its own right) is flow::log::Sev:S_WARNING.
39 *
40 * More precisely, checks whether logging warnings is currently enabled in the `Logger*` returned by
41 * `get_logger()` in the macro invocation's context; if not does nothing; if so constructs and logs the
42 * message as a warning via FLOW_LOG_WITHOUT_CHECKING(). Also, if `get_logger()` is null, then the effect is
43 * the same as a `get_logger()->should_log()` returning `false` (meaning it is a no-op).
44 *
45 * `get_logger()` must exist, and if not null then `get_logger()` (returning
46 * `Logger*`) and `get_log_component()` (returning `const Component&`)
47 * must return a valid pointer and reference to `Logger` and `Component`, respectively.
48 * Most of the time these are available due to most logging code being in classes deriving from flow::log::Log_context
49 * which supplies those methods and takes (at construction) the values to return subsequently.
50 * In situations where this is impossible (such as `static` members methods or in free functions) or insufficient (such
51 * as when one wants to use a different component vs. the one returned by flow::log::Log_context::get_log_component()),
52 * use FLOW_LOG_SET_CONTEXT().
53 *
54 * ### Final log output subleties ###
55 * We assume for this discussion that the notion of order of final output (to device/file/network/whatever) exists,
56 * and that the `get_logger()` indeed safely outputs message M1 entirely before M2, or vice versa, for every
57 * pair of messages (in this context by message we mean message+metadata pair) ever passed to `do_log()`. With that
58 * assumption in effect (IF indeed it is):
59 * - In a given thread T, and assuming the same `get_logger()`, if `FLOW_LOG_*(M1);`
60 * is called before `FLOW_LOG_*(M2);`, and both are successfully output by logger, then the final output order must
61 * have M1 precede M2, not vice versa.
62 * - In addition, assuming monotonically increasing time stamps, as time flows (which is the case most of the time
63 * with exceptions due to computer-clock adjustments and similar), M1's time stamp will be earlier (ignoring
64 * rounding) than M2's.
65 * - If, instead, M1 is logged in thread T1, while M2 is logged in thread T2:
66 * - If the 2 `FLOW_LOG_*()` calls are chronologically disjoint, then again the final output must also have
67 * M1 precede M2 if M1 went first; and vice versa.
68 * - Time stamps (under a well behaved clock again), again, will match this order.
69 * - If the 2 `FLOW_LOG_*()` calls chronologically overlap, then either final output order is possible.
70 * - Moreover if M1 precedes M2 in the output, formally time stamps might be in the opposite order
71 * (even assuming, again, a well behaved clock). (Informally, given how this is implemented internally,
72 * this is unlikely to be observed in practice, barring a wide absolute discrepancy between
73 * how long it takes to evaluate `ARG_stream_fragment` in M1 vs. M2.)
74 *
75 * The time stamp is obtained as soon as practically possible within the body of this macro. Hence it reflects the time
76 * at the moment just after the pre-logging statement finished, just before the log call site executes. *After* this
77 * is when `ARG_stream_fragment` is evaluated -- assuming the filter check passes -- and only after that might the
78 * final output get queued (or possibly synchronously output, depending on nature of `get_logger()`). This is why it's
79 * technically possible that (even in the absence of system time going backwards) 2 messages from 2 different threads
80 * might appear in the final output with slightly out-of-order time stamps. Informally, this is unlikely, because
81 * the `ARG_stream_fragment` evaluation would be on the order of a bunch of instructions that would complete in less
82 * time than the microsecond resolution of time stamp output, in most cases, or maybe a handful of microseconds.
83 * Anecdotally, I (ygoldfel) don't recall one instance of seeing this (out-of-order time stamps due to the
84 * concurrency race implied by the above mechanism).
85 * Nevertheless I mention it here for completeness, as well as to explain how it works.
86 *
87 * ### Severity selection ###
88 * Before selecting a severity for your log call site, please consider the discussion in the flow::log::Sev
89 * doc header.
90 *
91 * @param ARG_stream_fragment
92 * Same as in FLOW_LOG_WITHOUT_CHECKING().
93 *
94 * @todo We can avoid using macros for this and similar APIs by requiring the user to use commas instead of the usual
95 * `<<` and by implementing this as a variadic function template (C++11 feature).
96 *
97 * Thus, while it'd be no longer possible to write
98 *
99 * ~~~
100 * FLOW_LOG_WARNING("Result: [" << std::setfill('0') << num << "].");
101 * ~~~
102 *
103 * one would instead write
104 *
105 * ~~~
106 * flow::log::warning("Result: [", std::setfill('0'), num, "].");
107 * ~~~
108 *
109 * which is fairly close and still reasonably readable. However, one would need to be mindful of
110 * performance; hopefully the optimizer would still inline everything
111 * instead of adding a number of function calls compared to this macro implementation whose side benefit is
112 * guaranteed inlining. Generally, though, macros are bad and should be eliminated where possible; just don't mess
113 * up speed in something as common as logging. In addition, if it's NOT inlined, the number of functions generated
114 * by the template's many instantiations would be rather large, though I suppose that's not necessarily *worse* than
115 * full-on inlining thereof -- just *different*. (Still, consider how many different configurations would pop up
116 * as a result of all the different log messages!) Also keep in mind that *fully* inlining all of this would require
117 * the build engine to be capable of link-time optimization (FLTO), and the build script to enable FLTO.
118 * *Unfortunately* it would be impossible for the non-macro to refer to a `get_logger()`
119 * in the call's context, so it would be necessary for this to be passed in as an argument,
120 * significantly lowering the ease of use of the API. That said, flow::log::Logger itself could simply
121 * implement all these APIs as class methods instead of their being free functions.
122 * Then one could even (when desired) write such things as
123 *
124 * ~~~
125 * Logger some_logger{...}; // Some Logger that is not available through get_logger() as would be more typical.
126 * some_logger.warning("Error detected: [", err_num, "].");
127 * ~~~
128 *
129 * and therefore FLOW_LOG_SET_CONTEXT() (another "tricky" macro) could be eliminated due to lack of necessity.
130 * Finally, flow::log::Log_context (in the current design, to be derived from by all logging classes) would be used
131 * like this:
132 *
133 * ~~~
134 * get_logger()->warning("Error detected: [", err_num, "].");
135 * ~~~
136 *
137 * It might also be advisable, for code brevity in such commonly referenced APIs, to add trivial forwarding methods
138 * to flow::log::Log_context. This is slightly questionable, as it's quite a bit of boiler-plate (needed every time one
139 * might change this overall API) just to remove a few characters from each log call. The above call would become:
140 *
141 * ~~~
142 * log_warning("Error detected: [", err_num, "]."); // Invoke super-class Log_context's Logger::warning() method.
143 * ~~~
144 *
145 * which is a little more compact. That can also be accomplished by having flow::log::Log_context implement
146 * flow::log::Logger itself. As a last note, `__LINE__` (etc.) can only be made useful via a macro, so one would still
147 * be required to wrap around any API suggested above in a simple macro -- but this would be far superior (in this
148 * particular dimension of avoiding macro insanity) to the level of macro-ness required at the moment. All in all,
149 * while I do hate macros, the present design seems reasonably strong, so the above rejiggering ideas don't feel
150 * like no-brainers.
151 */
152#define FLOW_LOG_WARNING(ARG_stream_fragment) \
153 FLOW_LOG_WITH_CHECKING(::flow::log::Sev::S_WARNING, ARG_stream_fragment)
154
155/**
156 * Logs a FATAL message into flow::log::Logger `*get_logger()` with
157 * flow::log::Component `get_log_component()`, if such logging
158 * is enabled by the flow::log::Logger. Analogous to FLOW_LOG_WARNING() but for the flow::log::Sev::S_FATAL severity.
159 *
160 * ### Severity selection ###
161 * Before selecting a severity for your log call site, please consider the discussion in the flow::log::Sev
162 * doc header.
163 *
164 * @param ARG_stream_fragment
165 * Same as in FLOW_LOG_WARNING().
166 */
167#define FLOW_LOG_FATAL(ARG_stream_fragment) \
168 FLOW_LOG_WITH_CHECKING(::flow::log::Sev::S_FATAL, ARG_stream_fragment)
169
170/**
171 * Logs an ERROR message into flow::log::Logger `*get_logger()` with
172 * flow::log::Component `get_log_component()`, if such logging
173 * is enabled by the flow::log::Logger. Analogous to FLOW_LOG_WARNING() but for the flow::log::Sev::S_ERROR severity.
174 *
175 * ### Severity selection ###
176 * Before selecting a severity for your log call site, please consider the discussion in the flow::log::Sev
177 * doc header.
178 *
179 * @param ARG_stream_fragment
180 * Same as in FLOW_LOG_WARNING().
181 */
182#define FLOW_LOG_ERROR(ARG_stream_fragment) \
183 FLOW_LOG_WITH_CHECKING(::flow::log::Sev::S_ERROR, ARG_stream_fragment)
184
185/**
186 * Logs an INFO message into flow::log::Logger `*get_logger()` with
187 * flow::log::Component `get_log_component()`, if such logging
188 * is enabled by the flow::log::Logger. Analogous to FLOW_LOG_WARNING() but for the flow::log::Sev::S_INFO severity.
189 *
190 * ### Severity selection ###
191 * Before selecting a severity for your log call site, please consider the discussion in the flow::log::Sev
192 * doc header.
193 *
194 * @param ARG_stream_fragment
195 * Same as in FLOW_LOG_WARNING().
196 */
197#define FLOW_LOG_INFO(ARG_stream_fragment) \
198 FLOW_LOG_WITH_CHECKING(::flow::log::Sev::S_INFO, ARG_stream_fragment)
199
200/**
201 * Logs a DEBUG message into flow::log::Logger `*get_logger()` with
202 * flow::log::Component `get_log_component()`, if such logging
203 * is enabled by the flow::log::Logger. Analogous to FLOW_LOG_WARNING() but for the flow::log::Sev::S_DEBUG severity.
204 *
205 * ### Severity selection ###
206 * Before selecting a severity for your log call site, please consider the discussion in the flow::log::Sev
207 * doc header.
208 *
209 * @param ARG_stream_fragment
210 * Same as in FLOW_LOG_WARNING().
211 */
212#define FLOW_LOG_DEBUG(ARG_stream_fragment) \
213 FLOW_LOG_WITH_CHECKING(::flow::log::Sev::S_DEBUG, ARG_stream_fragment)
214
215/**
216 * Logs a TRACE message into flow::log::Logger `*get_logger()` with
217 * flow::log::Component `get_log_component()`, if such logging
218 * is enabled by the flow::log::Logger. Analogous to FLOW_LOG_WARNING() but for the flow::log::Sev::S_TRACE severity.
219 *
220 * ### Severity selection ###
221 * Before selecting a severity for your log call site, please consider the discussion in the flow::log::Sev
222 * doc header.
223 *
224 * @param ARG_stream_fragment
225 * Same as in FLOW_LOG_WARNING().
226 */
227#define FLOW_LOG_TRACE(ARG_stream_fragment) \
228 FLOW_LOG_WITH_CHECKING(::flow::log::Sev::S_TRACE, ARG_stream_fragment)
229
230/**
231 * Logs a DATA message into flow::log::Logger `*get_logger()` with
232 * flow::log::Component `get_log_component()`, if such logging
233 * is enabled by the flow::log::Logger. Analogous to FLOW_LOG_WARNING() but for the flow::log::Sev::S_DATA severity.
234 *
235 * ### Severity selection ###
236 * Before selecting a severity for your log call site, please consider the discussion in the flow::log::Sev
237 * doc header.
238 *
239 * @param ARG_stream_fragment
240 * Same as in FLOW_LOG_WARNING().
241 */
242#define FLOW_LOG_DATA(ARG_stream_fragment) \
243 FLOW_LOG_WITH_CHECKING(::flow::log::Sev::S_DATA, ARG_stream_fragment)
244
245/**
246 * Logs a WARNING message into flow::log::Logger `*get_logger()` with
247 * flow::log::Component `get_log_component()` regardless of
248 * whether such logging is enabled by the flow::log::Logger. Analogous to FLOW_LOG_WARNING() but without checking for
249 * whether it is enabled (you should do so yourself; see the following note on this topic).
250 *
251 * @note If `get_logger()` is null, this is a no-op. In practice, though,
252 * this case should have been eliminated as part of heeding the following warning:
253 *
254 * @warning If invoking this directly, API user must manually ensure the severity is enabled in the logger.
255 * Not doing so breaks (unenforced but nevertheless mandatory) rules of logging system.
256 *
257 * ### Severity selection ###
258 * Before selecting a severity for your log call site, please consider the discussion in the flow::log::Sev
259 * doc header.
260 *
261 * @param ARG_stream_fragment
262 * Same as in FLOW_LOG_WARNING().
263 */
264#define FLOW_LOG_WARNING_WITHOUT_CHECKING(ARG_stream_fragment) \
265 FLOW_LOG_WITHOUT_CHECKING(::flow::log::Sev::S_WARNING, ARG_stream_fragment)
266
267/**
268 * Logs a FATAL message into flow::log::Logger `*get_logger()`
269 * with flow::log::Component `get_log_component()` regardless of
270 * whether such logging is enabled by the flow::log::Logger. Analogous to FLOW_LOG_WARNING_WITHOUT_CHECKING()
271 * but for the flow::log::Sev::S_FATAL severity.
272 *
273 * @note Same warnings and notes as for FLOW_LOG_WARNING_WITHOUT_CHECKING().
274 *
275 * ### Severity selection ###
276 * Before selecting a severity for your log call site, please consider the discussion in the flow::log::Sev
277 * doc header.
278 *
279 * @param ARG_stream_fragment
280 * Same as in FLOW_LOG_WARNING().
281 */
282#define FLOW_LOG_FATAL_WITHOUT_CHECKING(ARG_stream_fragment) \
283 FLOW_LOG_WITHOUT_CHECKING(::flow::log::Sev::S_FATAL, ARG_stream_fragment)
284
285/**
286 * Logs an ERROR message into flow::log::Logger `*get_logger()`
287 * with flow::log::Component `get_log_component()` regardless of
288 * whether such logging is enabled by the flow::log::Logger. Analogous to FLOW_LOG_WARNING_WITHOUT_CHECKING()
289 * but for the flow::log::Sev::S_ERROR severity.
290 *
291 * @note Same warnings and notes as for FLOW_LOG_WARNING_WITHOUT_CHECKING().
292 *
293 * ### Severity selection ###
294 * Before selecting a severity for your log call site, please consider the discussion in the flow::log::Sev
295 * doc header.
296 *
297 * @param ARG_stream_fragment
298 * Same as in FLOW_LOG_WARNING().
299 */
300#define FLOW_LOG_ERROR_WITHOUT_CHECKING(ARG_stream_fragment) \
301 FLOW_LOG_WITHOUT_CHECKING(::flow::log::Sev::S_ERROR, ARG_stream_fragment)
302
303/**
304 * Logs an INFO message into flow::log::Logger `*get_logger()`
305 * with flow::log::Component `get_log_component()` regardless of
306 * whether such logging is enabled by the flow::log::Logger. Analogous to FLOW_LOG_WARNING_WITHOUT_CHECKING()
307 * but for the flow::log::Sev::S_INFO severity.
308 *
309 * @note Same warnings and notes as for FLOW_LOG_WARNING_WITHOUT_CHECKING().
310 *
311 * ### Severity selection ###
312 * Before selecting a severity for your log call site, please consider the discussion in the flow::log::Sev
313 * doc header.
314 *
315 * @param ARG_stream_fragment
316 * Same as in FLOW_LOG_WARNING().
317 */
318#define FLOW_LOG_INFO_WITHOUT_CHECKING(ARG_stream_fragment) \
319 FLOW_LOG_WITHOUT_CHECKING(::flow::log::Sev::S_INFO, ARG_stream_fragment)
320
321/**
322 * Logs a DEBUG message into flow::log::Logger `*get_logger()`
323 * with flow::log::Component `get_log_component()` regardless of
324 * whether such logging is enabled by the flow::log::Logger. Analogous to FLOW_LOG_WARNING_WITHOUT_CHECKING()
325 * but for the flow::log::Sev::S_DEBUG severity.
326 *
327 * @note Same warnings and notes as for FLOW_LOG_WARNING_WITHOUT_CHECKING().
328 *
329 * ### Severity selection ###
330 * Before selecting a severity for your log call site, please consider the discussion in the flow::log::Sev
331 * doc header.
332 *
333 * @param ARG_stream_fragment
334 * Same as in FLOW_LOG_WARNING().
335 */
336#define FLOW_LOG_DEBUG_WITHOUT_CHECKING(ARG_stream_fragment) \
337 FLOW_LOG_WITHOUT_CHECKING(::flow::log::Sev::S_DEBUG, ARG_stream_fragment)
338
339/**
340 * Logs a TRACE message into flow::log::Logger `*get_logger()`
341 * with flow::log::Component `get_log_component()` regardless of
342 * whether such logging is enabled by the flow::log::Logger. Analogous to FLOW_LOG_WARNING_WITHOUT_CHECKING()
343 * but for the flow::log::Sev::S_TRACE severity.
344 *
345 * @note Same warnings and notes as for FLOW_LOG_WARNING_WITHOUT_CHECKING().
346 *
347 * ### Severity selection ###
348 * Before selecting a severity for your log call site, please consider the discussion in the flow::log::Sev
349 * doc header.
350 *
351 * @param ARG_stream_fragment
352 * Same as in FLOW_LOG_WARNING().
353 */
354#define FLOW_LOG_TRACE_WITHOUT_CHECKING(ARG_stream_fragment) \
355 FLOW_LOG_WITHOUT_CHECKING(::flow::log::Sev::S_TRACE, ARG_stream_fragment)
356
357/**
358 * Logs a DATA message into flow::log::Logger `*get_logger()`
359 * with flow::log::Component `get_log_component()` regardless of
360 * whether such logging is enabled by the flow::log::Logger. Analogous to
361 * FLOW_LOG_WARNING_WITHOUT_CHECKING() but for the flow::log::Sev::S_DATA severity.
362 *
363 * @note Same warnings and notes as for FLOW_LOG_WARNING_WITHOUT_CHECKING().
364 *
365 * ### Severity selection ###
366 * Before selecting a severity for your log call site, please consider the discussion in the flow::log::Sev
367 * doc header.
368 *
369 * @param ARG_stream_fragment
370 * Same as in FLOW_LOG_WARNING().
371 */
372#define FLOW_LOG_DATA_WITHOUT_CHECKING(ARG_stream_fragment) \
373 FLOW_LOG_WITHOUT_CHECKING(::flow::log::Sev::S_DATA, ARG_stream_fragment)
374
375/**
376 * For the rest of the block within which this macro is instantiated, causes all `FLOW_LOG_...()`
377 * invocations to log to `ARG_logger_ptr` with component `flow::log::Component{ARG_component_payload}`, instead of the
378 * normal `get_logger()` and `get_log_component()`, if there even such things are available in the block. This is
379 * useful, for example, in `static` methods, where there is no `get_logger()` or `get_log_component()` function defined,
380 * but a flow::log::Logger and component payload are available (for example) via parameters. It's also useful if one
381 * wants to log to a different `Logger*` for some reason and/or (perhaps more likely) a different component.
382 * Note that this creates or changes the meaning of the identifiers `get_logger` and `get_log_component` for the rest of
383 * the block; in fact you may call `get_logger()` or `get_log_component()` directly for whatever nefarious purposes you
384 * require, though it is suspected to be rare compared to just using `FLOW_LOG_...()` normally.
385 *
386 * Example:
387 * ~~~
388 * // Suppose flow_node is a flow::Node object, which derives from Log_context.
389 * FLOW_LOG_SET_CONTEXT(flow_node.get_logger(), My_project_components::S_COOL_MODULE);
390 * // Logs with component S_COOL_MODULE to logger set above.
391 * FLOW_LOG_WARNING("Something horrible happened: [" << error_str << "].");
392 * ~~~
393 *
394 * @note It will not compile if used 2+ times in the same block at the same nesting level.
395 * (The same applies to mixing with FLOW_LOG_SET_COMPONENT() or FLOW_LOG_SET_LOGGER().)
396 * However, one can create sub-blocks to work around this (likely very infrequent) situation.
397 *
398 * @param ARG_logger_ptr
399 * `ARG_logger_ptr` will be used as the `Logger*` in subsequent `FLOW_LOG_...()`
400 * invocations in this block.
401 * @param ARG_component_payload
402 * `Component{ARG_component_payload}`, a light-weight holder of a copy of `ARG_component_payload`, will be used
403 * as the `const Component&` in subsequent `FLOW_LOG_...()` invocations in this block.
404 */
405#define FLOW_LOG_SET_CONTEXT(ARG_logger_ptr, ARG_component_payload) \
406 FLOW_LOG_SET_LOGGER(ARG_logger_ptr); \
407 FLOW_LOG_SET_COMPONENT(ARG_component_payload);
408
409/**
410 * Equivalent to FLOW_LOG_SET_CONTEXT() but sets the `get_logger` only.
411 *
412 * @param ARG_logger_ptr
413 * See FLOW_LOG_SET_CONTEXT().
414 */
415#define FLOW_LOG_SET_LOGGER(ARG_logger_ptr) \
416 [[maybe_unused]] \
417 const auto get_logger \
418 = [logger_ptr_copy = static_cast<::flow::log::Logger*>(ARG_logger_ptr)] \
419 () -> ::flow::log::Logger* { return logger_ptr_copy; }
420
421/**
422 * Equivalent to FLOW_LOG_SET_CONTEXT() but sets the `get_log_component` only.
423 *
424 * @internal
425 * ### Impl/perf discussion ###
426 * If a free or `static` member function or lambda is frequently called, and it needs to log, it will make perf
427 * aspects of this macro (and thus of FLOW_LOG_SET_CONTEXT()) significant to overall perf. Hence let's unpack
428 * what computation occurs.
429 * - When this macro (and thus FLOW_LOG_SET_CONTEXT()) is called (usually near the top of free/whatever function):
430 * It creates a lambda with one capture, a flow::log::Component, constructed via its 1-arg ctor form.
431 * That lambda object is, in fact, identical to a `struct` instance with a single `Component` member thus
432 * constructed. As of this writing that constuctor: saves the pointer `&(typeid(T))`, where `T` is the particular
433 * `enum class` type of `ARG_component_payload`; and saves the integer payload of the `enum` value
434 * `ARG_component_payload`. A pointer and an integer are light-weight to copy, indeed. Additionally, in practice,
435 * both values are probably known at compile time; hence the compiler might further inline the "copy."
436 * Hence we conclude the copy operation is fast.
437 * - The quasi-`struct`'s two members are stored directly on the stack. There is no heap involved. Hence we
438 * conclude there is no hidden cost in messing with the heap by either using a lambda or whatever is stored
439 * inside the lambda.
440 * - When `get_log_component()` is called (at every log call site in the free/whatever function, probably):
441 * It calls the aforementioned lambda's compiler-generated `operator()()`. This just returns a pointer to the
442 * stack-stored `struct`. Compilers tend to auto-inline (short) lambda `operator()()` calls, so there's probably
443 * not even the overhead of a function call. Hence we conclude this is pretty fast.
444 *
445 * Looks fine perf-wise (at least in theory).
446 * @endinternal
447 *
448 * @param ARG_component_payload
449 * See FLOW_LOG_SET_CONTEXT().
450 */
451#define FLOW_LOG_SET_COMPONENT(ARG_component_payload) \
452 [[maybe_unused]] \
453 const auto get_log_component = [component = ::flow::log::Component{ARG_component_payload}] \
454 () -> const ::flow::log::Component & \
455 { \
456 return component; \
457 }
458
459/**
460 * Logs a message of the specified severity into flow::log::Logger `*get_logger()` with flow::log::Component
461 * `get_log_component()` if such logging is enabled by said flow::log::Logger. The behavior is identical
462 * to that by FLOW_LOG_WARNING() and similar, but one specifies the severity as an argument instead of it being
463 * hard-coded into the macro name itself.
464 *
465 * @note It is important that ARG_stream_fragment is actually evaluated only if Logger::should_log() is
466 * true. Otherwise resources are wasted on constructing a message string that never gets
467 * logged. That's a (the?) reason Logger:should_log() and Logger::do_log() are mutually decoupled in
468 * that interface.
469 *
470 * @param ARG_sev
471 * Severity (type log::Sev).
472 * @param ARG_stream_fragment
473 * Same as in FLOW_LOG_WARNING().
474 *
475 * ### Severity selection ###
476 * Before selecting a severity for your log call site, please consider the discussion in the flow::log::Sev
477 * doc header.
478 *
479 * @internal
480 * @todo In FLOW_LOG_WITH_CHECKING(), save `get_logger()` and `get_log_component()` return values in such a way as to be
481 * reused by the FLOW_LOG_WITHOUT_CHECKING() invoked by the former macro if `should_log() == true`. As it stands, they
482 * are called again inside the latter macro. In the most-common case, wherein Log_context is used for those
483 * two expressions, this should get inline-optimized to be maximally fast anyway. With `FLOW_LOG_SET_*()`, though,
484 * it might be a bit slower than that. Technically, one can make their own `get_logger` and `get_log_component`
485 * identifiers that might do something slower still -- though it's unlikely (and as of this writing unprecedented).
486 * So I would not call this pressing, but on the other hand... just do it! The implementation code will be somewhat
487 * hairier though.
488 */
489#define FLOW_LOG_WITH_CHECKING(ARG_sev, ARG_stream_fragment) \
490 FLOW_UTIL_SEMICOLON_SAFE \
491 ( \
492 ::flow::log::Logger const * const FLOW_LOG_W_CHK_logger = get_logger(); \
493 if (FLOW_LOG_W_CHK_logger && FLOW_LOG_W_CHK_logger->should_log(ARG_sev, get_log_component())) \
494 { \
495 FLOW_LOG_WITHOUT_CHECKING(ARG_sev, ARG_stream_fragment); \
496 } \
497 )
498
499/**
500 * Identical to FLOW_LOG_WITH_CHECKING() but foregoes the filter (Logger::should_log()) check. No-op if
501 * `get_logger()` returns null. Internally, all other log-call-site macros ultimately build on top of this one
502 * except FLOW_LOG_DO_LOG().
503 *
504 * Context information obtained and possibly logged is file/function/line, thread nickname/ID, time stamp, etc.
505 * The message is given as the `<<`-using `ostream` fragment
506 * in `ARG_stream_fragment` argument. Example (note the 2nd argument containing a stream output fragment):
507 *
508 * ~~~
509 * FLOW_LOG_WITHOUT_CHECKING(flow::log::Sev::S_WARNING,
510 * "Failed [" << n_times << "] times; bailing out!");
511 * ~~~
512 *
513 * @note This macro is the lowest-level API that user should invoke to actually log, except FLOW_LOG_DO_LOG().
514 * Usually she'd invoke something higher-level like FLOW_LOG_WARNING(), etc.; but at times more control
515 * is desirable for performance or other reasons -- but even then one should not call anything below this
516 * level without an extremely excellent reason. See FLOW_LOG_DO_LOG() for discussion of the latter.
517 *
518 * @warning If invoking this directly, API user must manually ensure the severity is enabled in the `Logger`.
519 * Not doing so breaks (unenforced but nevertheless mandatory) rules of logging system.
520 *
521 * ### Severity selection ###
522 * Before selecting a severity for your log call site, please consider the discussion in the flow::log::Sev
523 * doc header.
524 *
525 * @param ARG_sev
526 * Severity (type flow::log::Sev).
527 * @param ARG_stream_fragment
528 * Fragment of code as if writing to a standard `ostream`.
529 * A terminating newline will be auto-appended to this eventually and therefore should generally not
530 * be included by the invoker. (Such a terminating newline would manifest as a blank line, likely.)
531 */
532#define FLOW_LOG_WITHOUT_CHECKING(ARG_sev, ARG_stream_fragment) \
533 FLOW_UTIL_SEMICOLON_SAFE \
534 ( \
535 using ::flow::util::Thread_id; \
536 using ::flow::log::Logger; \
537 using ::flow::log::Component; \
538 using ::flow::util::String_view; \
539 using ::flow::util::get_last_path_segment; \
540 using ::std::chrono::system_clock; \
541 using ::std::string; \
542 Logger* const FLOW_LOG_WO_CHK_logger = get_logger(); \
543 if (!FLOW_LOG_WO_CHK_logger) /* Usually a preceding filter/should_log() check would've eliminated this but.... */ \
544 { \
545 break; \
546 } \
547 /* else */ \
548 /* Important: This is from the time-of-day/calendar clock, which is not steady, monotonic, etc.; *but* it is */ \
549 /* convertible to a UTC time with cosmic meaning to humands; that is invaluable. */ \
550 auto const& FLOW_LOG_WO_CHK_time_stamp = system_clock::now(); \
551 /* See Msg_metadata::m_msg_src_file doc. */ \
552 /* @todo The __FILE/FUNCTION__ stuff was far more inlined, but gcc 5.4 hit an internal compiler error (bug)! */ \
553 /* This verbose step-by-step way of doing it jiggled the bug away. Put it back when bug goes away maybe. */ \
554 /* Note either way it's optimized to the same thing, since the following 7 expressions all have constant */ \
555 /* values at compile time as desired in m_msg_src_file doc header; and even formally guaranteed via constexpr. */ \
556 /* Warning: Something about this is brittle, in that if one makes a false move the compiler bug comes back. */ \
557 /* That's even when doing it this multi-step way. Leaving this comment in as moral support for maintainers. */ \
558 constexpr char const * FLOW_LOG_WO_CHK_file_ptr = __FILE__; \
559 constexpr size_t FLOW_LOG_WO_CHK_file_sz = sizeof(__FILE__) - 1; \
560 constexpr char const * FLOW_LOG_WO_CHK_func_ptr = __FUNCTION__; \
561 constexpr size_t FLOW_LOG_WO_CHK_func_sz = sizeof(__FUNCTION__) - 1; \
562 /* Minor: Using {} instead of () here leads to some macro trouble; not worth the pain to fix it. */ \
563 constexpr String_view FLOW_LOG_WO_CHK_full_file_str(FLOW_LOG_WO_CHK_file_ptr, FLOW_LOG_WO_CHK_file_sz); \
564 /* Yes -- get_last_path_segment() is constexpr and will thus "execute" at compile time! */ \
565 constexpr String_view FLOW_LOG_WO_CHK_file_str = get_last_path_segment(FLOW_LOG_WO_CHK_full_file_str); \
566 /* Minor: Using {} instead of () here leads to some macro trouble; not worth the pain to fix it. */ \
567 constexpr String_view FLOW_LOG_WO_CHK_func_str(FLOW_LOG_WO_CHK_func_ptr, FLOW_LOG_WO_CHK_func_sz); \
568 const Component& FLOW_LOG_WO_CHK_component = get_log_component(); \
569 string FLOW_LOG_WO_CHK_call_thread_nickname; \
570 Thread_id FLOW_LOG_WO_CHK_call_thread_id; \
571 Logger::set_thread_info(&FLOW_LOG_WO_CHK_call_thread_nickname, &FLOW_LOG_WO_CHK_call_thread_id); \
572 FLOW_LOG_DO_LOG(FLOW_LOG_WO_CHK_logger, FLOW_LOG_WO_CHK_component, ARG_sev, FLOW_LOG_WO_CHK_file_str, __LINE__, \
573 FLOW_LOG_WO_CHK_func_str, FLOW_LOG_WO_CHK_time_stamp, FLOW_LOG_WO_CHK_call_thread_nickname, \
574 FLOW_LOG_WO_CHK_call_thread_id, ARG_stream_fragment); \
575 /* FLOW_LOG_WO_CHK_call_thread_nickname is now hosed. */ \
576 ) /* FLOW_UTIL_SEMICOLON_SAFE() */
577
578/**
579 * Lowest-level logging API accessible to the user, this is identical to FLOW_LOG_WITHOUT_CHECKING() but expects all
580 * pieces of metadata in addition to the message and log::Sev, plus the flow::log::Logger, to be supplied as macro
581 * arguments.
582 *
583 * Internally, all other log-call-site macros ultimately build on top of this one.
584 *
585 * @note From public user's point of view: It's flow::log::Logger::should_log() that allows the message argument to be
586 * built using `ostream<<` semantics instead of having to instantiate an intermediate flow::util::String_ostream
587 * (which has performance overhead and is more verbose). Why not just use a higher-level macro -- at least
588 * as high-level as FLOW_LOG_WITHOUT_CHECKING() -- instead? Answer: In some cases there is a source of metadata,
589 * like file and line number, that comes from a different source than (e.g.) `__FILE__` and `__LINE__` at the log
590 * call site; e.g., when logging from another log API through flow::log.
591 *
592 * @warning If invoking this directly, API user must manually ensure the severity/component is enabled in the `Logger`.
593 * Not doing so breaks (unenforced but nevertheless mandatory) rules of logging system.
594 * @warning If invoking this directly, API user must manually ensure `ARG_logger_ptr` is not null. Otherwise behavior
595 * is undefined.
596 *
597 * @param ARG_logger_ptr
598 * A `Logger*` through which to log; not null.
599 * @param ARG_component
600 * See Msg_metadata (reference copied into it).
601 * @param ARG_sev
602 * See Msg_metadata (`enum` value copied into it).
603 * @param ARG_file_view
604 * See Msg_metadata (`String_view` copied into it). Reminder: Underlying memory may need to remain valid
605 * asynchronously (read: indefinitely); usually it's a literal in static storage.
606 * @param ARG_line
607 * See Msg_metadata (integer copied into it).
608 * @param ARG_func_view
609 * See Msg_metadata (`String_view` copied into it). Reminder: Same as for `ARG_file_view`.
610 * @param ARG_time_stamp
611 * See Msg_metadata (scalar copied into it).
612 * @param ARG_call_thread_nickname_str_moved
613 * See Msg_metadata (this `std::string` is *moved* into it and thus made empty).
614 * @param ARG_call_thread_id
615 * See Msg_metadata (scalar copied into it).
616 * @param ARG_stream_fragment
617 * See FLOW_LOG_WITHOUT_CHECKING().
618 *
619 * @internal
620 *
621 * ### Implementation discussion ###
622 *
623 * The implementation here must be as performant as humanly possible. Every single logged message (albeit
624 * only assuming severity [or any other filter] checks have passed, meaning a message is IN FACT logged) will
625 * execute this code.
626 *
627 * In this implementation, one keeps reusing a thread-local `string`, cleared each time this is invoked and then
628 * written to. (The clearing doesn't deallocate anything; it only updates an internal length integer to 0!)
629 * If Logger::logs_asynchronously() is `false`, then the Logger synchronously outputs the message and has no need
630 * to make some intemediate copy of either the message or the metadata (time stamp, etc.). However, if
631 * it is `true` (as for heavy-duty logger Async_file_logger), then Logger must make a copy of the aforementioned
632 * thread-local message string, so that it can be asynchronously logged later (probably by some other worker thread
633 * used by the Logger), and deallocated. This implementation allows for both work-flows; also see below to-do.
634 *
635 * @todo An alternative copy-free implementation of the asynchronous FLOW_LOG_DO_LOG() work-flow
636 * is possible. The basic idea of using a thread-local non-reallocating work string is just fine in the
637 * non-`logs_asynchronously()` (i.e., synchronous) Logger flow. In the asynchronous flow, however, it involves
638 * an added message copy. Instead -- as done in certain major server software author is familiar with -- one
639 * could (perhaps in the async flow only) allocate a new string in each FLOW_LOG_DO_LOG() (in rare cases
640 * reallocating, even repeatedly, if more space is needed); then pass that pointer around, until it is asynchronously
641 * written out by Logger impl; then deallocate it. Thus, a copy is eliminated in the async workflow. A complicating
642 * factor is that the current system maintains a format state across separate log call sites in a given thread; this
643 * change would (if naively implemented at least) eliminate that feature -- but this could be considered acceptable.
644 * (Still, do realize that, for example, in today's feature set one can set the `chrono` I/O formatting to show short
645 * unit names like `2ms` instead of `2 milliseconds`; and one need do it only once; but with this change one would
646 * need to do it in every log call site. That would be, I can attest, rather annoying. Additionally, make sure the
647 * behavior is consistent in the sync and async work-flows.) A related wrinkle is that if we add special support for
648 * `printf()`-style log call sites (a to-do in flow::log doc header as of this writing), then in that case since
649 * there is no expectation of such format statefulness in the first place, in that flow that particular concern isn't
650 * a concern. (Sub-to-do: If one did this, an extra-optimized further idea is to avoid the repeated allocs/deallocs
651 * by maintaining a pool of already-allocated buffers to be reused opportunistically.) Bottom line: I claim the
652 * existing thing is pretty good; the extra copy is IMO unlikely to affect real performance, because (1) it's only
653 * one copy in the context of quite a bit of similar copying and other ops going on while writing out the string; and
654 * (2) if the message is so verbose as to affect performance *at all*, then it will probably affect it regardless of
655 * the extra copy (in other words, its verbosity must be increased, or the filter verbosity must be decreased --
656 * avoiding this exta internal copy feels in my [ygoldfel] personal opinion like rearranging deck chairs on the
657 * Titanic). So, this to-do should probably be to-done at some point, but it doesn't feel urgent. And since there are
658 * quite a few subtleties involved, as shown above, it's natural to resist doing it until truly necessary.
659 *
660 * @todo Time stamp subtlety: It might not be crazy to log not just the time stamp of entry to this macro but also
661 * some indication how long it took to evaluate the rest and finally output it to the ultimate device/whatever.
662 * It could be an optional differential or something, like "timestamp+diff," with diff in microseconds or something.
663 *
664 * @todo Time stamp source: The current implementation uses the system clock to generate time stamps (a/k/a POSIX time),
665 * but consider optionally or mandatorily using the high-resolution clock instead (or additionally?).
666 * This has pros and cons; all or most time stamps elsewhere use the system clock also, so this allows for easy
667 * cross-referencing against other systems and logs. There's also the question of how to express an absolute time,
668 * as usually the high-resolution clock starts at system startup -- not as humanly useful as a "calendar" (UTC) time,
669 * which -- while useful humanly -- is *not* steady/monotonic/etc. There is no reasonable conversion between
670 * `Fine_clock::now()` and a calendar time (boost.chrono docs say this unequivocally which is a confirmation).
671 * The pros include: (1) higher precision and resolution; (2) that time always flows forward and at a uniform rate
672 * without possibility of time going back or forward due to human/otherwise clock sets or rare events like daylight
673 * savings and leap seconds; or (3) to summarize, something more suited as a quick-and-dirty way to measure how long
674 * things take in the program, like an always-on, log-integrated version of perf::Checkpointing_timer.
675 * As of this writing all out-of-the-box log::Logger implementations and log::Config allow the output of human-readable
676 * as well as sec.usec-from-Epoch time stamps. One approach might be to replace the latter *only* with the high-rez
677 * clock's time stamps, perhaps optionally, while leaving the human-readable one alone. Note: There is an important
678 * test to perform here, which is the time cost of obtaining either time stamp. E.g., the high-rez time stamp might
679 * be much slower -- or maybe the opposite! To test this, (1) add the POSIX-time clock into the perf::Clock_type
680 * `enum`, with all associated (fairly straightforward) changes in `flow::perf`; and (2) test the perf characteristics
681 * of this new clock. Certain code exists outside of Flow itself that already automatically tests all `Clock_type`s,
682 * so it would quickly give the answer. (Secondary to-do: Be less vague about where this program resides, in this
683 * comment. I, ygoldfel, have the answer at any rate and am only omitting it here for boring process reasons.)
684 */
685#define FLOW_LOG_DO_LOG(ARG_logger_ptr, \
686 ARG_component, ARG_sev, ARG_file_view, ARG_line, ARG_func_view, \
687 ARG_time_stamp, ARG_call_thread_nickname_str_moved, \
688 ARG_call_thread_id, ARG_stream_fragment) \
689 FLOW_UTIL_SEMICOLON_SAFE \
690 ( \
691 using ::flow::log::Thread_local_string_appender; \
692 using ::flow::log::this_thread_sync_msg_metadata_ptr; \
693 using ::flow::log::Logger; \
694 using ::flow::log::Msg_metadata; \
695 using ::flow::util::String_view; \
696 using ::std::flush; \
697 Logger* const FLOW_LOG_DO_LOG_logger = ARG_logger_ptr; /* Note: As advertised, it must NOT be null. */ \
698 /* If first use this thread/logger combo, create necessary structures for writing stream fragment to a string. */ \
699 /* If subsequent use with that combo, reuse already created structures to save cycles. */ \
700 /* We could've just created string { locally } and used util::ostream_op_to_string(), which would */ \
701 /* have been easier, but doing it this way should be faster, as it's just: a thread-local lookup, */ \
702 /* a string clearing which should amount to a single length assignment internally, and finally a */ \
703 /* write to an existing stream adapter, which would have been necessary regardless. The alternative */ \
704 /* would involve creating the string and the adapter machinery { locally } first -- every time. */ \
705 /* Update: Also, this way we get the continuous but distinct ostream state as documented in class Logger doc. */ \
706 /* Update 2: See to-do in FLOW_LOG_WITHOUT_CHECKING() doc header. */ \
707 auto& FLOW_LOG_DO_LOG_appender \
708 = *(Thread_local_string_appender::get_this_thread_string_appender(*FLOW_LOG_DO_LOG_logger)); \
709 auto& FLOW_LOG_DO_LOG_os = *(FLOW_LOG_DO_LOG_appender.fresh_appender_ostream()); \
710 FLOW_LOG_DO_LOG_os << ARG_stream_fragment << flush; \
711 /* They gave us all the pieces of Msg_metadata, so we just put the object together. */ \
712 Msg_metadata* FLOW_LOG_DO_LOG_msg_metadata_ptr; \
713 if (FLOW_LOG_DO_LOG_logger->logs_asynchronously()) \
714 { \
715 /* () used to avoid nested-macro-comma trouble. */ \
716 (FLOW_LOG_DO_LOG_msg_metadata_ptr \
717 = new Msg_metadata{ ARG_component, ARG_sev, ARG_file_view, ARG_line, ARG_func_view, \
718 ARG_time_stamp, std::move(ARG_call_thread_nickname_str_moved), ARG_call_thread_id }); \
719 /* FLOW_LOG_DO_LOG_msg_metadata_ptr will be async-deleted by Logger (see below). */ \
720 } \
721 else \
722 { \
723 FLOW_LOG_DO_LOG_msg_metadata_ptr = this_thread_sync_msg_metadata_ptr.get(); \
724 if (!FLOW_LOG_DO_LOG_msg_metadata_ptr) \
725 { /* This happens once per thread at most. */ \
726 this_thread_sync_msg_metadata_ptr.reset(FLOW_LOG_DO_LOG_msg_metadata_ptr = new Msg_metadata); \
727 } \
728 /* () used to avoid nested-macro-comma trouble. */ \
729 ((*FLOW_LOG_DO_LOG_msg_metadata_ptr) \
730 = { ARG_component, ARG_sev, ARG_file_view, ARG_line, ARG_func_view, \
731 ARG_time_stamp, std::move(ARG_call_thread_nickname_str_moved), ARG_call_thread_id }); \
732 /* *FLOW_LOG_DO_LOG_msg_metadata_ptr will be overwritten next time in this thread, and again and again. */ \
733 } \
734 /* Time to log it finally and (if applicable) clean up. */ \
735 /* Asynchronous path (see above `if`): */ \
736 /* target_contents() returns const reference that we pass through without copying. */ \
737 /* However, FLOW_LOG_DO_LOG_logger with true logs_asynchronously() knows it needs to make a copy of this to */ \
738 /* log that later, asynchronously. */ \
739 /* Also it specifically knows *it* must `delete` FLOW_LOG_DO_LOG_msg_metadata_ptr when done with it */ \
740 /* subsequently. */ \
741 /* Synchronous path: */ \
742 /* This path is simpler. Logger won't make a copy of the message, to which we still pass a ref; */ \
743 /* and it specifically knows it must NOT `delete` FLOW_LOG_DO_LOG_msg_metadata_ptr. */ \
744 /* However, for an alleged perf bump (@todo verify!) we use a */ \
745 /* thread-local Msg_metadata to avoid making this thing on the stack and then destroying almost immediately. */ \
746 FLOW_LOG_DO_LOG_logger->do_log(FLOW_LOG_DO_LOG_msg_metadata_ptr, \
747 String_view{FLOW_LOG_DO_LOG_appender.target_contents()}); \
748 ) /* FLOW_UTIL_SEMICOLON_SAFE() */
749
750namespace flow::log
751{
752// Types.
753
754/**
755 * A light-weight class, each object storing a *component* payload encoding an `enum` value from `enum` type of
756 * user's choice, and a light-weight ID of that `enum` type itself.
757 * A Component is supplied by the user, at every logging call site, along with the message.
758 * Log_context (or FLOW_LOG_SET_CONTEXT() in relatively rare scenarios) makes this easier, so typically user need not
759 * literally type out a component at every logging call site, meaning there is a "context" that already stores it
760 * and need not be re-specified.
761 *
762 * A Component can be either empty, as when default-constructed, indicated by empty() returning `true`; or non-empty,
763 * in which case it actually stores something interesting. In the latter case, construct it with the templated
764 * one-arg constructor. The template arg `Payload` must, always, be user's own `enum class`. In order to have this
765 * `Payload` interpreted correctly, one can (and usually should) use the log::Config facility which is used by
766 * out-of-the-box Logger implementations including Simple_ostream_logger (useful for console output) and
767 * Async_file_logger (for heavy-duty file logging, including rotation support). However, this is technically optional:
768 * one can implement their own Logger which might not use the Config facility and thus deal with the meaning of
769 * Component in some completely different way. I'd start with an existing Logger implementation however; and if writing
770 * one's own Logger, then still have it use log::Config, unless that system is somehow insufficient or inappropriate.
771 *
772 * Tip: Arguably the best way to see how to use all this together, just see flow::Flow_log_component and how Flow's own
773 * code (which, itself, logs!) uses the log system. This is discussed in more detail in the class Config doc header.
774 * Lastly, some clarifying discussion may be found (as of this writing) in the Component::type_info() doc header.
775 *
776 * ### Thread safety, mutability ###
777 * A Component is immutable as of this writing, except one can trivially overwrite a Component via an assignment
778 * operator. The latter write operation is not thread-safe w.r.t. a given `*this`, though in practice this is unlikely
779 * to ever matter.
780 *
781 * @internal
782 *
783 * ### Implementation discussion ###
784 * Again -- a Component conceptually must store only 2 things:
785 * - Encoding of an `enum` value.
786 * - Some ID of the `enum class` type from which this value originates.
787 *
788 * Why these 2 values?
789 * - When a Logger logs, it will likely want to print some string representation. If it's just printing an integer,
790 * then it needs that integer. If it's printing a string, it'll probably need to look up that string; for which
791 * it needs the ID of the per-logging-module component table, and of course the row within that table; as
792 * component 2 for module A is completely not the same component at all as component 2 for module B (and if it is,
793 * then it is a coincidence presumably).
794 * - When a Logger decides *whether* component C should indeed log message M (not even constructed yet at that point,
795 * for perf) at log::Sev S, it must look up whether the logging of component C messages at severity S is indeed
796 * enabled at present. Some kind of data structure is required; for example a map (conceptually) from C to
797 * max severity S', to compare S vs. S'. log::Config (technically optional, though all out-of-the-box `Logger`s
798 * do use it), in particular, has such a structure.
799 *
800 * The operations required to implement, then, are:
801 * - Construct a Component from an `enum class` value of arbitrary type (one such type per logging module).
802 * - Access the type ID of the latter type.
803 * - Access the `enum` value itself.
804 *
805 * The first version of Component did this by simply storing (and thus being, in terms of data, equal to) `boost::any`.
806 * The constructor would simply load the `enum` value of type T as the `any` payload. The accessors would
807 * `any_cast<T>()` and then trivially access the value itself and/or its `typeid()`.
808 *
809 * This was delightful in terms of code simplicity. However, due to the extreme prevalence of Logger::should_log()
810 * checks in code -- at *every* log call site, including high-verbosity ones that do *not* typically result in
811 * messages being logged, such as for Sev::S_TRACE -- certain perf aspects of `boost::any` involved non-trivial perf
812 * costs. Namely:
813 * - `any` constructor performs a `new` when storing the arbitrarily-typed value, even though in our case it's
814 * always just an `enum`. Copying an `any` similarly will need to `new`.
815 * - The `any` accessors must do `virtual` lookups, to resolve to the particular `enum class` type.
816 *
817 * Hence the current solution optimized those issues away by making use of the fact we know the stored thing is
818 * *always* an `enum class`. Hence:
819 * - Its value can be stored as an integer.
820 * - Its type can be stored directly; and since the type is an `enum`, which is non-polymorphic, the `typeid()`
821 * operation does not even need to do the `virtual` resolution. In fact its result is known at compile time.
822 * (This can even lead to further optimizations by the compiler.)
823 *
824 * Some discussion for perspective:
825 * - Using `any` was elegant in terms of code simplicity; it is analogous to runtime dynamic typing of languages
826 * like Python.
827 * - It is just that it was too slow given the frequency of the accessor operations (and, to a lesser but still
828 * potentially significant degree -- as with FLOW_LOG_SET_CONTEXT() -- for construction/copying).
829 * - Therefore it was replaced by a less-pithy hand-optimized solution that doesn't use Python-style dynamic typing
830 * but instead encodes the 2 bits of info directly by using the fact it's always an `enum`.
831 * - This does not mean `boost::any` is just too slow, because most operations are not done
832 * as frequently as should-log checks. It *does* mean it may be too slow in production in this *particular*
833 * context.
834 * - It has been alleged that `std::any` (appearing first in C++17) in some or all gcc versions optimized-away these
835 * perf problems by storing certain values directly inside it, when those values were small enough to "fit."
836 * (Our `enum`s presumably would fit that bill, being glorified `int`s.) This is believable: Many `std::string`
837 * impls will directly store strings of sufficiently small length; e.g., <=15 bytes would fit into the same area
838 * as the pointers and length data members required for arbitrarily-long string that do require heap allocation.
839 * Probably the same can be done for `any`. So keep that in mind if using `any` functionality in other contexts.
840 */
842{
843public:
844 // Types.
845
846 /// The type `Payload` must be `enum class Payload : enum_raw_t`: an `enum` type encoded via this integer type.
847 using enum_raw_t = unsigned int;
848
849 // Constructors/destructor.
850
851 /**
852 * Constructs a Component that stores no payload; meaning an unspecified-component Component that returns
853 * `empty() == true`. Every Logger and all other systems must accept a message with such a null Component.
854 * However, one can configure a given Logger to ensure such messages not be logged (filtered out via `should_log()`).
855 * Point is, any log call site that supplied a null Component must still work, meaning not cause undefined behavior.
856 */
857 Component();
858
859 /**
860 * Constructs a Component with the given payload of arbitrary type, so long as that type is an
861 * `enum class : Component::enum_raw_t`. (#enum_raw_t is an unsigned integer type.)
862 * The resulting Component will return `empty() == false`.
863 *
864 * @tparam Payload
865 * The type of the component value stored inside `*this`. This is required to be a type satisfying
866 * the requirements in the doc header for #enum_raw_t.
867 * @param payload
868 * The payload value copied into `*this` and whenever a Component itself is copied (or moved).
869 */
870 template<typename Payload>
871 Component(Payload payload);
872
873 /**
874 * Copies the source Component into `*this`. This involves a single payload copy (and not even that if
875 * `src.empty()`).
876 *
877 * @param src
878 * Object to copy.
879 */
880 Component(const Component& src);
881
882 /**
883 * Constructs `*this` equal to `src_moved`. In this implementation it is equivalent to the copy constructor.
884 *
885 * @param src_moved
886 * Object to move.
887 */
888 Component(Component&& src_moved);
889
890 // Methods.
891
892 /**
893 * Overwrites `*this` with a copy of `src`.
894 * @param src
895 * Object to copy.
896 * @return `*this`.
897 */
899
900 /**
901 * Equivalent to `operator=(Component<Payload>(new_payload))` modulo possibly minor perf differences.
902 *
903 * @param new_payload
904 * See non-default constructor.
905 * @return `*this`.
906 */
907 template<typename Payload>
908 Component& operator=(Payload new_payload);
909
910 /**
911 * Makes `*this` equal to `src_moved`. In this implementation it is equivalent to copy assignment.
912 * @param src_moved
913 * Object to move.
914 * @return `*this`.
915 */
917
918 /**
919 * Returns `true` if `*this` is as if default-constructed (a null Component); `false` if as if
920 * constructed via the 1-arg constructor (a non-null Component).
921 *
922 * @return See above.
923 */
924 bool empty() const;
925
926 /**
927 * Returns reference to immutable payload stored in `*this`; undefined behavior if `empty() == true`.
928 *
929 * @tparam Payload
930 * See one-arg ctor doc header.
931 * @return See above.
932 */
933 template<typename Payload>
934 Payload payload() const;
935
936 /**
937 * Returns `typeid(Payload)`, where `Payload` was the template param used when calling the
938 * originating one-arg constructor or equivalent; undefined behavior if `empty() == true`.
939 *
940 * flow::log user that relies fully on an out-of-the-box Logger, or on a custom Logger that
941 * nevertheless fully uses the log::Config facility, is unlikely to need this information.
942 * (It is used internally by log::Config.) However, a custom Logger that decided to use
943 * an alternative Component mechanism (as opposed to how log::Config and reliant `Logger`s do)
944 * can use this payload_type() method to distinguish between potential source `enum`s that were
945 * passed to the Logger at each given log call site.
946 *
947 * For example, consider that Flow itself, for its own logging, uses the flow::Flow_log_component
948 * `enum` at all log call sites. Imagine you, the user, have decided to generate your own
949 * flow::log messages and always use your own `enum class cool_project::Cool_log_component` at your
950 * own logging call sites. Then, a typical component specification will look like this respectively:
951 *
952 * ~~~
953 * class flow::net_flow::Node : public Log_context
954 * {
955 * Node(...) :
956 * Log_context(..., Flow_log_component::S_NET_FLOW) // This class will usually use the NET_FLOW component.
957 * ...
958 *
959 * class cool_project::Cool_class_about_widgets : public Log_context
960 * {
961 * Cool_class_about_widgets(...) :
962 * Log_context(..., Cool_log_component::S_WIDGETRY) // Ditto for your own code. Use your own enum.
963 * ~~~
964 *
965 * Now, `this->get_log_component().payload_type()` will equal that of `Flow_log_component` and `Cool_log_component`,
966 * respectively, within those 2 classes. In particular, a custom Logger implementation can use `payload_type()`
967 * to interpret the `Component`s of values from the 2
968 * entirely disparate `enum`s in different ways. More in particular, the out-of-the-box `Logger`s use `Config`
969 * to do all of that without your having to worry about it (but your own Logger would potentially have to worry
970 * about it particularly if not using log::Config... though we suggest that you should, barring excellent
971 * design counter-reasons).
972 *
973 * @return See above.
974 */
975 const std::type_info& payload_type() const;
976
977 /**
978 * Returns the numeric value of the `enum` payload stored by this Component, originating in the one-arg constructor;
979 * undefined behavior if `empty() == true`.
980 *
981 * @return See above. Specifically that's: `static_cast<enum_raw_t>(payload)`, where `payload` was passed to
982 * originating 1-arg ctor.
983 */
985
986private:
987 // Data.
988
989 /**
990 * The `typeid()` of the `Payload` passed to the 1-arg constructor; if 0-arg ctor was used (empty() is `true`) then
991 * it is null.
992 *
993 * ### Rationale ###
994 * Why is it a pointer and not something else? Answer:
995 * It cannot be a direct member: `type_info` is not copyable. It cannot be a non-`const` reference for the
996 * same reason. It cannot be a `const` reference, because Component is mutable via assignment.
997 * Could use `reference_wrapper<const type_info>`, but ultimately that's the same as storing a pointer.
998 *
999 * In any case by definition `type_info` is not copyable, and indeed the desire to store a "copy" is wrong-headed.
1000 *
1001 * ### Discussion of performance ###
1002 * Careful: This does not mean that (for `type_info`s `A` and `B`)
1003 * `A == B` only if `&A == &B`: two `type_info`s may be equal -- refer to the
1004 * same type -- but be distinct objects. This occurs essentially only if the `A = typeid(...)` op
1005 * was done on another side of a shared-object boundary versus the `B = typeid(...)` op.
1006 *
1007 * (If they're *not* on opposing sides that way, then in fact `A == B` (if and) only if `&A == &B`. For example, if
1008 * you've statically-linked all relevant code, then the relevant `type_info`s will be 1-1 with their addresses
1009 * after all.)
1010 *
1011 * That is why `type_index` (cted from a `type_info`) is useful for (essentially) storing `type_info`s in
1012 * containers. Problem is: syntactically it's great; but it can give a false sense of performance:
1013 * internally hashing, equality, and ordering ops cannot simply always assume `type_info*`s are 1-1 with distinct
1014 * `type_info`s and hence usually STLs implement the hashing in terms of `type_info::name()` -- a string, namely
1015 * usually one containing the entire fully qualified type name (e.g.: some mangled version of
1016 * `"flow::Log_component"`). Utilities in detail/component_cfg.hpp work with this topic. For more:
1017 * start with Config::Component_payload_type_to_cfg_map doc header.
1018 */
1019 std::type_info const * m_payload_type_or_null;
1020
1021 /**
1022 * The internally stored integer representation of the `enum` value passed to the 1-arg constructor; meaningless
1023 * and ignored if 0-arg ctor was used (empty() is `true`).
1024 */
1026}; // class Component
1027
1028/**
1029 * Simple data store containing all of the information generated at every logging call site by flow::log, except
1030 * the message itself, which is passed to Logger::do_log() assuming Logger::should_log() had returned `true`.
1031 * User only need to worry about this when dealing with the internals of a Logger implementation. Copying is to be
1032 * avoided, as there are some non-trivial data stored here; though it is not too bad.
1033 *
1034 * @warning If changing the insides of Msg_metadata, ensure free function `deep_copy(const Msg_metadata&)`
1035 * remains accurate.
1036 *
1037 * @todo Add support in Msg_metadata for a message ID which could more straightforwardly help the human log reader
1038 * to map a log line to the originating log call site in source code. One approach, then, might be to output
1039 * that message ID when available; else output #m_msg_src_file, #m_msg_src_line, #m_msg_src_function; or maybe
1040 * both.
1041 */
1043{
1044 // Data.
1045
1046 /// Component of message, as of this writing coming from either Log_context constructor or FLOW_LOG_SET_CONTEXT().
1048
1049 /**
1050 * Severity of message, typically determined by choice of macro (e.g., FLOW_LOG_WARNING() vs. FLOW_LOG_INFO()) at the
1051 * call site; though it can also be supplied manually via `FLOW_LOG_WITH[OUT]_CHECKING()` macro arg.
1052 */
1054
1055 /**
1056 * Pointer/length into static-storage string that would have come from built-in `__FILE__` macro which is
1057 * auto-invoked by all `FLOW_LOG_*()` logging call sites. Formally this should be the pointer/length representing
1058 * the substring of `__FILE__` that you wish to be logged in its entirely, no more and no less. For example
1059 * the pointer might be to the first character, the lengh equal to `strlen()`; or more practically it might be
1060 * one past the right-most dir separator character (and the length decremented accordingly).
1061 *
1062 * To be explicit: The underlying memory must never be deallocated, in that it should never have been allocated on the
1063 * heap but in static storage.
1064 *
1065 * ### Perf notes ###
1066 * We store a util::String_view, not a `const char*`, which means not only a pointer but a length is stored here
1067 * internally. That's fine (we discuss trade-offs just below); it should barely affect the perf of copying
1068 * Msg_metadata. However the length must be initialized. To initialize it in the most optimized way, recognize
1069 * that it will come from `__FILE__` which is really a string literal substituted by preprocessor; therefore
1070 * the length can be obtained at compile time via `sizeof()`. Hence use the 2-arg `String_view` ctor which takes
1071 * the pointer *and* the length instead of figuring the latter out via `strlen()` which is linear-time.
1072 * Update: The util::String_view in use as of this writing declares the 1-arg ctor as `constexpr` which indicates
1073 * it might be able to do the `strlen()` (or equivalent) at compile-time. Nevertheless, this feels (and I say this
1074 * with some related experience) like something that may or may not actually be implemented by a given compiler.
1075 * So it's probably safer (in terms of portability) to still follow the 2-arg ctor advice above.
1076 *
1077 * If it is desired (as suggested in an example above) to represent a mere substring of that, then as long as
1078 * the computation of the appropriate first character past index 0 satisfies `constexpr` requirements (meaning, it
1079 * will be computed at compile time, not runtime) -- as does the according decrement of the length -- then you're
1080 * still fine. If it is done at runtime, then that's a hit to perf, so avoid it if at all possible.
1081 *
1082 * If indeed you do want ultimately to output a substring of `__FILE__`, then in order to guarantee or at least
1083 * increase the chance of compile-time computation of that substring you should in fact do it as early as possible
1084 * at the log call site as opposed to later/nearer the time of final output to device/whatever by Logger. In other
1085 * words in that case do try to set this value to the substring from the start; don't leave it to the Logger.
1086 *
1087 * ### Perf rationale: Why not use `const char*` instead? ###
1088 * (This is arguably very nitpicky anyway, but these objects are generated and passed around at every single log call
1089 * site, so we should try to save every little bit.) Let's stipulate that the cost of storing (RAM; and copying)
1090 * the length is negligible. The next concern is initializing the length; the above shows that's free in practice.
1091 * Finally, when output to the final device/whatever occurs within Logger impl, there are two possibilities of how
1092 * it might realistically act. One, it might search for the NUL char anyway -- in which case we've changed nothing
1093 * perf-wise -- as it copies for output purposes. Two, it might require the length of the string and hence use
1094 * `String_view::size()` and then perform the copy; in that case we have saved a linear search. (For example, if
1095 * the Logger is printing to an `ostream`, then there exists an `operator<<(ostream, String_view)` that'd be
1096 * automatically used and would of course use the saved length properly.) So, it's a win or at worst a tie.
1097 */
1099
1100 /**
1101 * Copy of integer that would have come from built-in `__LINE__` macro which is auto-invoked by all `FLOW_LOG_*()`
1102 * logging call sites.
1103 */
1104 unsigned int m_msg_src_line;
1105
1106 /// Analogous to #m_msg_src_file but coming from `__FUNCTION__`, not `__FILE__`. See #m_msg_src_file perf notes.
1108
1109 /**
1110 * Time stamp from as close as possible to entry into the log call site (usually `FLOW_LOG_WARNING()` or similar).
1111 *
1112 * `std::chrono` is used instead of boost.chrono for certain internal output reasons in Ostream_log_msg_writer.
1113 */
1114 std::chrono::system_clock::time_point m_called_when;
1115
1116 /**
1117 * Thread nickname, as for Logger::this_thread_set_logged_nickname(), of the thread from which the
1118 * log call was invoked; or an empty string if the thread had no such nickname set at the time.
1119 *
1120 * @see #m_call_thread_id
1121 *
1122 * ### Perf note ###
1123 * Setting this involves an `std::string` copy; the cost of this is worth considering given that this is done
1124 * for every single log call site, if the nickname is indeed set. See performance note in doc header of
1125 * Logger::this_thread_set_logged_nickname() for the recommendation and details. (Long story short, if you keep
1126 * it at N `char`s or fewer, the cost of a non-empty #m_call_thread_nickname becomes equal to that of an
1127 * empty one. N might be 15 in gcc 5.)
1128 */
1130
1131 /**
1132 * Thread ID of the thread from which the log call was invoked; or a default-constructed (no-thread) such
1133 * thread ID if the thread has a nickname, meaning `!m_call_thread_nickname.empty()`. The working assumption is
1134 * that (1) both members are met for direct log output only and no other logic; and (2) the nickname is preferable
1135 * when set, the thread ID being the fallback. (If this sounds meh, consider that it's entirely reasonable to make
1136 * the nickname contain some nice info *and* the original thread ID as well in string form. However, mind
1137 * the length -- the Performance Note in #m_call_thread_nickname doc header.)
1138 */
1140}; // struct Msg_metadata
1141
1142/**
1143 * Interface that the user should implement, passing the implementing Logger into logging classes
1144 * (Flow's own classes like net_flow::Node; and user's own logging classes) at construction (plus free/`static`
1145 * logging functions). The class (or function) will then implicitly
1146 * use that Logger in the logging apparatus (such as `FLOW_LOG_...()` macros) to log messages
1147 * into the user's preferred logging output(s). One can think of the class implementing this interface
1148 * as the glue between Flow logging and either the user's lower-level logging system or some output device
1149 * (like console, files) directly.
1150 *
1151 * The reason should_log() and do_log() are decoupled like this is for a fast
1152 * implementation of the `FLOW_LOG_...()` macros (see FLOW_LOG_WITH_CHECKING() in particular).
1153 *
1154 * There is also a small set of app-wide (`static`) utilities, including the ability to nickname any
1155 * given thread, which causes that nickname (instead of the thread ID) to be logged in subsequent log
1156 * messages.
1157 *
1158 * ### Stream continuity (and threads) ###
1159 * As slightly advanced `ostream` users know, an `ostream` carries state that can be set via explicit method
1160 * calls such as `ostream::setf()` and via "printing" manipulators such as `std::hex` to a given `ostream`.
1161 * Regular values printed to `ostream` will come out differently depending on the current state (e.g., `<< 14` may
1162 * show up as a decimal or a hex integer depending on preceding formatters). If you use these state-affecting features,
1163 * be aware of the following semantics:
1164 *
1165 * - The system works as if there is exactly one `ostream` for every distinct combination (`logger`, T), where:
1166 * - `logger` is a distinct Logger object, as identified by the address in its `Logger::this` pointer.
1167 * - T is a distinct thread such that the API user has previously, at least once, invoked a logging API that works
1168 * with Logger `ostream`s -- from that thread. (The APIs that qualify are essentially various APIs that actually
1169 * log messages or attempt to set formatting for subsequent messages or are used to subsequently do either.)
1170 * - One can think of a given (`logger`, T) combo's `ostream` as being created on-demand the first time a given
1171 * Logger `logger` attempts to do something logging-related from a given thread T. Subsequently, with that combo
1172 * used again and again, the stream is internally looked up with high performance. The on-demand creation is slower
1173 * but relatively rare given the low expected count of threads and `Logger`s.
1174 * - Any stream state change carried by a user API call for a given distinct (as defined above) stream S:
1175 * - *will* persistently affect the subsequent output of any subsequent log message payload on stream S,
1176 * including the same log message following the given state change, and including any subsequent log messages;
1177 * - *will not* affect any stream other than S;
1178 * - *will not* affect any intra-message prefixes or suffixes (such as originating file, function, line that may
1179 * be inserted before every log message automatically) added by the Logger API implementation.
1180 * - There are essentially two ways to affect state of stream S. Either one must be invoked from the thread T
1181 * pertaining to stream S, and the Logger object involved must also be the one pertaining to S.
1182 * - The state change may be part of the sequence of `operator<<()` operands passed to a logging API.
1183 * However, note that for it to take effect, it must not be filtered out by a Sev filter check (basically,
1184 * the containing log message must actually end up in the log, not filtered away).
1185 * - In this case, the state change must be via a manipulator formatter like `std::setfill`, `std::hex`,
1186 * `boost::chrono::seconds`, etc.
1187 * - The state change may be a direct call invoked on a given `ostream S`, where to get access to `S` one would
1188 * call Logger::this_thread_ostream() to receive the pointer `&S', for the current thread and
1189 * the Logger on which the method is executed. Once you have `S`, you may call state manipulators such as
1190 * `S.setf();` or, again, manipulators via `S << std::hex;` and similar.
1191 * - Using this_thread_ostream() for any purpose other than to change stream state (i.e., to output actual
1192 * characters, like `S << "Hello, world!";`) will result in undefined behavior. Do not. Use logging APIs for
1193 * that.
1194 * - Even if the Logger implementation uses `ostream` for ultimate output of characters (and it absolutely does not
1195 * have to do any such thing), that `ostream` is totally orthogonal and irrelevant to the one being discussed here.
1196 * E.g., Simple_ostream_logger may print to `std::cout`, but the formatters we are discussing affect a totally
1197 * different, internal stream, not that `std::cout`. In fact, the internal stream is in memory, not
1198 * file/console/whatever. I point this out only to avoid confusion.
1199 *
1200 * ### Basic suggested strategy for implementing a Logger ###
1201 * The simplicity of the Logger interface, and the lack of mandatory rigidity in how one might configure it
1202 * (particularly w/r/t per-`Component` verbosity and output format), assures the sky is the limit for how one
1203 * implements their own custom Logger. However, in the absence of great reasons not to, we suggest one follows
1204 * the lead of out-of-the-box existing `Logger`s in Flow, which adds a degree of rigidity as to the implementation
1205 * but also seems to provide all the features seemingly commonly desired in practice. Namely, like
1206 * (say) Simple_ostream_logger, Buffer_logger, and Async_file_logger, do this:
1207 * - Take a Config pointer at constructor and save it (do not copy the Config). (There are thread safety
1208 * implications.)
1209 * - Internally use some kind of `ostream`-sub-class member to a target device, if at all possible.
1210 * (boost.asio will let you even write to network this way; but at least console output, file output, and
1211 * memory string output are 100% practical via `ostream`s. Existing `Logger`s provide examples.)
1212 * - Internally use an Ostream_log_msg_writer to write to said `ostream`, the formatting thereof being configurable
1213 * in a uniform way via the saved Config.
1214 * - Forward should_log() logic to the saved Config (Config::output_whether_should_log()), so that verbosity is
1215 * flexibly but uniformly set via Config.
1216 * - It is up to the user, now, to set up the Config appropriately when passing it to your `Logger` sub-class
1217 * constructor. The user would simply follow the documentation for Config, and you need neither re-implement
1218 * nor re-document configurability of your Logger.
1219 *
1220 * Reminder: Config and Ostream_log_msg_writer are optional to use, and one can use one, both, or neither (though
1221 * the latter itself does expect one of the former; of course you can just load up your own new Config, in case
1222 * you don't want to use a user Config taken via your constructor API). However, unless there's something insufficient
1223 * about them, various benefits abound in using both. Furthermore, if something is lacking, consider extending that
1224 * system to the benefit of all/most other `Logger`s as opposed to throwing it away and re-implementing something new
1225 * for just your new Logger.
1226 *
1227 * Having decided on that stuff, you also need to decide whether you will write to the output device/whatever
1228 * synchronously or asynchronously. To wit, the thread safety discussion:
1229 *
1230 * ### Thread safety ###
1231 * The degree of thread safety for either of the 2 main operations is completely up to the sub-class implementer.
1232 * Informally, we suggest here that you think about this topic carefully. In particular, without locking,
1233 * do_log() may run concurrently with itself from multiple threads; depending on the medium to which
1234 * it is writing, this may result in corruption or ugly output or turn out fine, depending on how you define
1235 * "fine." Whether this is a concern or not is up to you. Note, however, that in practice as of this writing
1236 * *at least* one Flow module (flow::net_flow) will definitely potentially execute should_log() and do_log()
1237 * concurrently with themselves from multiple threads on the same Logger. In general that should be expected in all but
1238 * the simplest single-threaded apps.
1239 *
1240 * Implementation suggestions for Logger sub-classes with respect to thread safety: There are 2 likeliest patterns one
1241 * can use.
1242 * -# One can use a mutex lock around actual writing to the target device. There's nothing inherently un-performant
1243 * about this, in an of itself, and the implementation is incredibly simple. For example see
1244 * Simple_ostream_logger. However, there *is* a significant performance danger if the device-writing itself can
1245 * be both synchronous and slow. In particular, a file write (when flushing any buffer such as the internal
1246 * one in `FILE*` or `ofstream`), which is likely after each message, is usually synchronous and can be
1247 * sporadically slow (consider having to spin up a sleeping hard drive for instance). That would not only block
1248 * the log-call-site thread but also any competing logging threads at that time. That is probably fine in many
1249 * non-production scenarios, but in a production heavy-duty server it's not OK.
1250 * - An informal suggestion: It is fine for console output, probably (standard out, standard err). Otherwise,
1251 * particularly with files and synchronous networking, don't. Use the following instead:
1252 * -# One can avoid any such lock; instead the log-call-site thread can save the stuff to log (including the message
1253 * and Msg_metadata passed to do_log()) and pass it to a dedicated thread (or pool thereof, etc.) in charge of
1254 * asynchronously queueing up stuff to write to device and actually writing it in the order received at some
1255 * later time (though typically fairly soon and definitely ASAP in most cases). This is a bit more complex, but
1256 * with flow::async it's really pretty easy still. See the heavy-duty Async_file_logger for example.
1257 *
1258 * Both general patterns are formally supported. To use pattern 1, have your Logger implementation's
1259 * logs_asynchronously() return `false`. To use pattern 2, have it use `true`. The precise implications are
1260 * documented in doc header of do_log().
1261 *
1262 * ### Order of output requirements ###
1263 * We assume for this discussion that the notion of order of final output (to device/file/network/whatever) exists,
1264 * and that the Logger implementation indeed safely outputs message M1 entirely before M2, or vice versa, for every
1265 * pair of messages (in this context by message we mean message+metadata pair) ever passed to do_log(). With that
1266 * assumption in effect (IF indeed it is), the present text *requires* that the following is guaranteed:
1267 * - In a given thread T, for a given Logger L, if `L.do_log(M1);` is called before `L.do_log(M2)`, and both
1268 * are successfully output by L, then the final output order must have M1 precede M2, not vice versa.
1269 * - If, instead, M1 is logged in thread T1, while M2 is logged in thread T2:
1270 * - If the 2 do_log() calls are chronologically disjoint, then again the final output must also have
1271 * M1 precede M2 if M1 went first; and vice versa.
1272 * - If the 2 do_log() calls chronologically overlap, then either final output order is acceptable.
1273 * (Ideally, it *should* reflect the order of entry to do_log() in the 2 threads, but it's formally optional.
1274 * Informally this typically will happen anyway under most basic algorithms imaginable.)
1275 *
1276 * Note that this is only tangentially related to any time stamps one might see in the final output.
1277 *
1278 * See also notes on log output order in FLOW_LOG_WARNING() doc header (which applies to all log call sites).
1279 * The behavior described there is a function of the underlying Logger following the above formal requirements for
1280 * thread-safe Logger implementations.
1281 */
1282class Logger :
1283 public util::Null_interface,
1285 private boost::noncopyable // Though it's an interface, this just seems like a prudent rule. @todo Reconsider?
1286{
1287public:
1288 // Methods.
1289
1290 /**
1291 * Given attributes of a hypothetical message that would be logged, return `true` if that message
1292 * should be logged and `false` otherwise (e.g., if the verbosity of the message is above the
1293 * current configured verbosity threshold for the Component specified).
1294 *
1295 * The convenience macros `FLOW_LOG_...()` combine this with do_log() to make it so that
1296 * a message is only built if `should_log() == true`, and (if so) builds and logs it; otherwise
1297 * almost no computing or storage resources are spent, and the message is neither built nor logged logged.
1298 * The verb "to build message" here more formally means "to execute the `ostream<<` fragment passed to macro by
1299 * writing characters to some internally maintained `ostream` accordingly."
1300 *
1301 * @param sev
1302 * Severity of the message.
1303 * @param component
1304 * Component of the message. Reminder: `component.empty() == true` is allowed; which isn't to say it will
1305 * or won't result in this method returning `true`, but that it will return and not act in undefined fashion.
1306 * @return `true` if it should be logged; `false` if it should not.
1307 */
1308 virtual bool should_log(Sev sev, const Component& component) const = 0;
1309
1310 /**
1311 * Must return `true` if do_log() at least sometimes logs the given message and metadata (e.g., time stamp) after
1312 * do_log() returns; `false` if this never occurs (i.e., it logs synchronously, always). do_log() doc header
1313 * formally describes the implications of this.
1314 *
1315 * This must always return the same value, for a given `*this`.
1316 *
1317 * This method is intended for internal use by the flow::log system; informally it is not expected the user
1318 * will call it. Technically there is no harm in doing so. (It would have been `private` but cannot due to certain
1319 * C++ limitations, and certain contrived ways to do it are just not worth the trouble.)
1320 *
1321 * @internal
1322 * The "certain C++ limitations" are that FLOW_LOG_WITHOUT_CHECKING() must access it, but it is a macro and cannot
1323 * be involved in `friend`ship. A contrived way to resolve it would be to make some `detail/` helper free function
1324 * the `friend`, which would call this method, and invoke that free function from the macro. All that just to move a
1325 * harmless thing into `private` -- I think not worth it, but an argument could be made.
1326 * @endinternal
1327 *
1328 * @see do_log().
1329 * @return See above.
1330 */
1331 virtual bool logs_asynchronously() const = 0;
1332
1333 /**
1334 * Given a message and its severity, logs that message and possibly severity WITHOUT checking whether it should be
1335 * logged (i.e., without performing logic that should_log() performs). The logging is guaranteed to be synchronous
1336 * if `!logs_asynchronously()`; but may be asynchronous otherwise (more on this below).
1337 *
1338 * The convenience macros `FLOW_LOG_...()` combine this with should_log() to make it so that a
1339 * message is only constructed if `should_log() == true`, and if so, constructs and logs it; otherwise
1340 * almost no computing or storage resources are spent, and the message is not logged.
1341 *
1342 * Expectations of what should or should not be included in `msg` are of some importance.
1343 * They are as follows. To summarize, `msg` should include the message (as specified typically as the
1344 * `ARG_stream_fragment` arg to FLOW_LOG_WARNING() and buddies); and `*msg_metadata` should include everything
1345 * else. This design provides maximum flexibility to the Logger::do_log() implementation to structure the final
1346 * output's contents (in the log file, or console, or wherever) as it sees fit, cosmetically and size-wise.
1347 *
1348 * Note on trailing newline(s): `msg` must include any trailing newline(s) that are *required* to be output.
1349 * By convention, do_log() itself will print a message terminator (often in fact a newline) after each message, if
1350 * applicable. Hence typically there is no trailing newline at the end of most `msg`s, and one would include N of
1351 * them if and only if one intentionally desires N trailing blank lines (possible but atypical).
1352 *
1353 * ### Precise meaning of logs_asynchronously() ###
1354 * Let ASYNC = the value logs_asynchronously() returns (note for a given `*this` it must always be the same value by
1355 * that API's contract). Then do_log() must act as follows:
1356 * - If ASYNC is `false`: Do not make a copy of `msg`; output it synchronously. Do not make a copy of `*metadata`.
1357 * Do not `delete metadata`. Output any of its contents synchronously.
1358 * - If ASYNC is `true`: Optionally make a copy of `msg`, unless you are able to output it synchronously, in which
1359 * case there is no need. Do not make a copy of `*metadata`. You *must* `delete metadata` at some point;
1360 * failing to do so *will* leak it. (Note: We are intentionally avoiding using `shared_ptr` or even `unique_ptr`,
1361 * for the perf savings, since logging is ubiquitous.) Output `*metadata` contents either synchronously or
1362 * asynchronously.
1363 *
1364 * The only formal requirement, however, is simply: You must `delete metadata;` at some future point <=iff=>
1365 * ASYNC is `true`. The other requirements just above are informal but of no less import.
1366 *
1367 * @see Msg_metadata which includes stuff not to include in `msg`.
1368 *
1369 * @param metadata
1370 * All information to potentially log in addition to `msg`.
1371 * @param msg
1372 * The message. See details above. Short version: exclude anything from `metadata`; exclude any ever-present
1373 * terminating newline.
1374 */
1375 virtual void do_log(Msg_metadata* metadata, util::String_view msg) = 0;
1376
1377 /**
1378 * Sets or unsets the current thread's logging-worthy string name; optionally sets the OS thread name (such as
1379 * visible in `top` output). The logging-worthy name is always set or unset; the OS name is modified only if
1380 * `also_set_os_name == true` arg is set. `thread_nickname` can thus be set to something more descriptive than
1381 * any default, such as: "native_main" or "worker_port_2231."
1382 *
1383 * - The logging-worthy thread string name is accessed as follows:
1384 * - `FLOW_LOG_*()` macros pass it automatically to the appropriate log::Logger. All out-of-the-box `Logger`s
1385 * will then log either the name set here (if not blank) or the thread ID (if blank). (Custom `Logger`s
1386 * can ignore these data and not log them; but presumably most will at least optionally log them.)
1387 * - It can also be obtained directly via this_thread_logged_name_os_manip(), set_thread_info(), or
1388 * set_thread_info_in_msg_metadata().
1389 * - The OS thread name can be accessed in various ways; including in Linux:
1390 * - `ps H -C $cmd -o pid\ tid\ cmd\ comm # The comm column will be the thread name; set $proc = process name.`
1391 * - `top -H` (thread mode -- or just `top` and press H key for the same effect).
1392 *
1393 * More precisely, there are 3 states for each thread: before this_thread_set_logged_nickname() is called;
1394 * after it is called with blank name; and after it's called non-blank name. The semantics are as follows:
1395 * - Logging-worthy thread string name:
1396 * - Initial: As-if this_thread_set_logged_nickname() was already called with blank name (next bullet).
1397 * - Blank: The thread string name becomes conceptually null; and the thread ID shall be used instead.
1398 * - Non-blank: The thread string name becomes equal to `thread_nickname`.
1399 * - OS thread name:
1400 * - Initial: `ps`, `top`, etc. will show the thread name as equal to the process name.
1401 * - Blank: `ps`, `top`, etc. will show the thread name as the thread ID (truncated to N characters, though
1402 * this is unlikely to be exceeded by real thread IDs).
1403 * - Non-blank: `ps`, `top`, etc. will show the thread name as equal to `thread_nickname` (truncated to N
1404 * characters).
1405 * - Note: Because "Initial" and "Blank" are necessarily not the same, it is recommended to call
1406 * this_thread_set_logged_nickname() around thread creation time even if `thread_nickname` is blank.
1407 * Then `ps`, `top`, etc. output will still be useful and possible to cross-reference with log output, say.
1408 * - Note: N is documented in `man pthread_setname_np` as 15 not counting the NUL terminator.
1409 * Therefore ideally keep the `thread_nickname.size()` to at most N.
1410 *
1411 * ### Performance ###
1412 * Subsequently obtaining the nickname involves a linear string copy;
1413 * the cost of this is worth considering given that this is potentially
1414 * done for every single log call site, if the nickname is indeed set. However, most `string` implementations
1415 * provide an optimization that uses a `union` (or equivalent) technique to store short strings in the same place
1416 * as the data members (pointer, size, capacity) required for long strings; meaning such short `string`s cost no
1417 * more to copy than an *empty* one does. In gcc 5, this is 15 bytes or `char`s, but implementations vary.
1418 *
1419 * Therefore, it is *actively encouraged* that you keep the length of `thread_nickname` low. "Low" depends on the
1420 * compiler, but keeping it somewhere at or under 15 characters is likely good. If you do so, the effective cost
1421 * (at a log call site) will be the same as if `thread_nickname.empty()`, so one could not do better.
1422 * Note, also, that the OS-name (in Linux) max length happens to also be 15 (see N discussion above), so there is
1423 * convenient synergy there.
1424 *
1425 * ### Thready safety ###
1426 * This call is safe w/r/t concurrent execution with itself and this_thread_logged_name_os_manip() in other
1427 * thread(s) for a given `*this`. It is thread-local in nature.
1428 *
1429 * ### Naming rationale ###
1430 * this_thread_set_logged_nickname() is an incomplete name, in that it (optionally) also affects the OS thread name.
1431 * The function was not renamed for compatibility reasons, as the technically incomplete name is in my (ygoldfel)
1432 * opinion still acceptably descriptive.
1433 *
1434 * @param thread_nickname
1435 * New nickname of thread; or "" to request the thread to not be nicknamed (thread ID will be used).
1436 * In the "" case, no particular thread ID format should ever be assumed; it may be OS-dependent;
1437 * but it can be informally assumed to be useful for thread identification purposes (probably unique per thread
1438 * and somewhat readable, etc.). The non-blank value is copied and saved.
1439 * @param logger_ptr
1440 * If non-null, this `Logger` will be used to log an INFO-severity message indicating the
1441 * thread ID and new nickname (conceptually: [INFO] T...nickname...: Thread ...ID... has new nickname.).
1442 * If null, nothing is logged.
1443 * @param also_set_os_name
1444 * If and only `true`, `thread_nickname` (whether blank or not) also affects the current OS thread name.
1445 * Otherwise it is not affected.
1446 *
1447 * @todo this_thread_set_logged_nickname() could take multiple `Logger`s, since it is an app-wide function
1448 * and potentially would want to be reflected in multiple loggers. It could take a pair of iterators
1449 * or a container (in both cases, of template argument type). Arguably, though, this is overkill; as (1) most code
1450 * paths deal with only one `get_logger()`-owning object each; (2) naming of a thread is obvious enough in subsequent
1451 * logs (hence this message only calls attention to that operation plus notes the thread ID before the nickname
1452 * assignment, not necessarily the most critical of information). Certainly this zero-to-one-`Logger` version must
1453 * continue to be available for syntactic-sugary convenience, even if the to-do is performed.
1454 */
1455 static void this_thread_set_logged_nickname(util::String_view thread_nickname = {},
1456 Logger* logger_ptr = nullptr,
1457 bool also_set_os_name = true);
1458
1459 /**
1460 * `ostream` manipulator function that, if output via `operator<<` to an `ostream`, will cause the current
1461 * thread's logging-worthy string name to be output to that stream. See this_thread_set_logged_nickname()
1462 * for details of what this string will actually be.
1463 *
1464 * Recall that an `ostream` manipulator is invoked in the style of `endl` and `flush`; for example: `cout << endl;`.
1465 * It is atypical to call it directly as opposed to via the overloaded "shift" operator.
1466 *
1467 * Note that typically this is not invoked directly by the user but rather used in the `FLOW_LOG_...()` macros'
1468 * implementation guts which is the original use case and hence reason for its existence.
1469 * However, there's no rule against direct uses, and it could prove useful at some point. Any use beyond logging or
1470 * debugging is not recommended however (in particular, do not use to make any algorithmic decisions).
1471 *
1472 * This call is safe w/r/t concurrent execution with itself and this_thread_set_logged_nickname() in other
1473 * thread(s).
1474 *
1475 * @param os
1476 * Stream to which to write thread's name.
1477 * @return `os`.
1478 */
1479 static std::ostream& this_thread_logged_name_os_manip(std::ostream& os);
1480
1481 /**
1482 * Loads `msg_metadata->m_call_thread_nickname` (if set) or else `msg_metadata->m_call_thread_id`, based
1483 * on whether/how this_thread_set_logged_nickname() was last called in the current thread.
1484 * The two members should be set to ther default-constructed values on entry to the present function.
1485 *
1486 * @todo It would be more consistent to rename set_thread_info_in_msg_metadata() to
1487 * this_thread_set_info_in_msg_metadata(), since it operates in thread-local fashion.
1488 * This was a naming convention oversight.
1489 *
1490 * @param msg_metadata
1491 * Non-null pointer to structure to modify. See above.
1492 */
1493 static void set_thread_info_in_msg_metadata(Msg_metadata* msg_metadata);
1494
1495 /**
1496 * Same as set_thread_info_in_msg_metadata() but targets the given two variables as opposed to a
1497 * Msg_metadata.
1498 *
1499 * @todo It would be more consistent to rename set_thread_info() to
1500 * this_thread_set_info(), since it operates in thread-local fashion.
1501 * This was a naming convention oversight.
1502 *
1503 * @param call_thread_nickname
1504 * Non-null pointer to value to modify. See above.
1505 * @param call_thread_id
1506 * Non-null pointer to value to modify. See above.
1507 */
1508 static void set_thread_info(std::string* call_thread_nickname,
1509 util::Thread_id* call_thread_id);
1510
1511 /**
1512 * Returns the stream dedicated to the executing thread and `this` Logger, so that the caller can apply
1513 * state-setting formatters to it. If you write characters to it, or otherwise do anything othen than set
1514 * formatting state, or try to affect buffering behavior, behavior is undefined. Usage example:
1515 *
1516 * ~~~
1517 * get_logger()->this_thread_ostream()->setf(std::fixed | std::right);
1518 * *(get_logger()->this_thread_ostream()) << std::setw(2);
1519 * // *get_logger()'s subsequent messages (such as the following) from the current thread will use above formatting.
1520 * FLOW_LOG_WARNING("Let's print a number with some formatting: " << 0.5);
1521 * ~~~
1522 *
1523 * Note that you could just as well apply the intended formatters via regular log statements.
1524 * However, there are disadvantages to that approach -- but they do not always apply. The disadvantages are listed
1525 * below; but the short version is you should typically use the present method if and only if you are attempting to
1526 * affect subsequent logging at large, not a particular fragment of a particular message.
1527 *
1528 * Formally, the disadvantages of affecting formatting state of the underlying stream via log macros:
1529 * - If the log statement is ignored due to failing a filter check,
1530 * then any formatters therein will also be ignored. (However, you could use a macro that bypasses such a check.
1531 * On the other hand, stylistically one would typically only do that after checking the severity manually
1532 * for performance of combining several log statements with equal severities. Using it *just* to apply formatters
1533 * is stylistically dubious.)
1534 * - If you are trying to affect subsequent logging at large, you'd hypothetically use something like
1535 * `FLOW_LOG_INFO_WITHOUT_CHECKING(...formatter... << ...formatter... << ...);`. This is stylistically dubious,
1536 * because the lack of characters being output means the severity (INFO in this case) is disregarded and is
1537 * a dummy value.
1538 * - While one can pass many formatters as `<<` operator arguments, there are others than do not use that syntax.
1539 * For example, `ostream::setf()` is a method *of* `std::ostream`. Thus the log macros do not help.
1540 *
1541 * Recall that the stream involved is completely orthogonal to any underlying stream that may be ultimately output
1542 * to by Logger for the actual, ultimate output of characters. E.g., if Logger happens to be Simple_ostream_logger
1543 * targeted at `std::cout`, the above snippet would in no way touch `std::cout` formatting. In fact, Logger may
1544 * not even use streams for output; that is an orthogonal implementation detail.
1545 *
1546 * @return Pointer to stream; always the same value for a given thread and different among all distinct threads.
1547 */
1548 std::ostream* this_thread_ostream() const;
1549
1550private:
1551 // Data.
1552
1553 /// Thread-local storage for each thread's logged name (null pointer, which is default, means no nickname stored).
1554 static boost::thread_specific_ptr<std::string> s_this_thread_nickname_ptr;
1555}; // class Logger
1556
1557/**
1558 * Convenience class that simply stores a Logger and/or Component passed into a constructor; and returns this
1559 * Logger and Component via get_logger() and get_log_component() public accessors. It's extremely
1560 * useful (almost mandatory in conventional practice) for classes that want to log, as they can simply
1561 * derive from it (passing in the desired `Logger*` and Component payload (an `enum`
1562 * value) into the Log_context super-class constructor),
1563 * at which point the get_logger() and get_log_component() functions the `FLOW_LOG_...()` macros expect automatically
1564 * become available without any additional code having to be written in the logging class. Here is how:
1565 *
1566 * ~~~
1567 * class I_like_to_have_fun_and_log_about_it :
1568 * public flow::log::Log_context
1569 * {
1570 * public:
1571 * I_like_to_have_fun_and_log_about_it() :
1572 * // Glue FLOW_LOG_*() macros to the following simple logger and component FUN_HAVER.
1573 * Log_context(&m_logger, My_cool_components::S_FUN_HAVER),
1574 * // Initialize stdout logger that logs INFO-or-higher-severity messages.
1575 * m_logger(true, std::cout, std::cout, flow::log::Sev::S_INFO),
1576 * // ... other initializers and super-class constructors, if any ...
1577 * {
1578 * FLOW_LOG_INFO("I can log right from the constructor and throughout *this lifetime!");
1579 * // ... other code ...
1580 * }
1581 *
1582 * private:
1583 * void do_fun_stuff()
1584 * {
1585 * // This macro works, because Log_context super-class defines get_logger() which returns m_logger,
1586 * // and component() returns My_cool_components::S_FUN_HAVER.
1587 * // But we need not ever worry about such details.
1588 * FLOW_LOG_INFO("I am about to do something cool and fun: " << 42 << "!");
1589 * // ... more code ...
1590 * }
1591 *
1592 * // I will use a simple stdout/stderr logger for my logging. It's passed to Log_context in constructor.
1593 * flow::log::Simple_ostream_logger m_logger;
1594 * }; // class I_like_to_have_fun_and_log_about_it
1595 * ~~~
1596 *
1597 * Note that the `operator=()` allows one to change the underlying Logger anytime after
1598 * construction (e.g., `existing_log_context = Log_context{&some_logger, Some_enum::S_SOME_COMPONENT};`).
1599 * That said it is more convenient to use set_logger(); but see the next section, as this may involve more
1600 * subtleties than one might think.
1601 *
1602 * ### Setting the logger / thready safety ###
1603 * set_logger() allows one, including an external user, to change the Logger. However beware two points w/r/t
1604 * thread safety.
1605 * -# It is not safe to use set_logger() or `*this = Log_context{...}` concurrently with any call
1606 * that would log via get_logger(): so anything that, e.g., does `FLOW_LOG_...()`. It only replaces
1607 * a pointer in memory, but there is no mutex or atomic protection; so it is not safe.
1608 * -# Assuming one avoids any issues with the get_logger() pointer safety, please be sure that the Logger
1609 * itself is actually valid/alive.
1610 *
1611 * Informally: Generally it is best to avoid changing the active Logger, after an object is constructed.
1612 * 99% of code in practice does not do so; it is usually far better to affect the Logger via its Config which
1613 * takes massive pains to be both thread-safe and performant at that.
1614 *
1615 * However in practice there is at least one exception to this: when the sub-class's instance is `static` or
1616 * even global and/or a singleton. Then it might operate before and even after `main()`, and even during `main()` there
1617 * may not be a good `Logger` to use yet. One might then make use of set_logger(), e.g., early in `main()` to
1618 * change it from null to a `Logger` and then back late in `main()` (or if not null then a default
1619 * Simple_ostream_logger to `cout` + `cerr`... you get the idea).
1620 *
1621 * That however does not protect against thread-safety problems (point 1 above) necessarily. It depends when one
1622 * does it. If it is necessary to be changing get_logger() return-value while get_logger() is potentially used
1623 * by another thread, then consider using Log_context_mt. There is a bit of a perf trade-off there (see its doc
1624 * header).
1625 *
1626 * ### Implementation notes ###
1627 * The code could be shorter by getting rid of non-copy constuctor in favor of direct member initialization by user;
1628 * and by simply omitting the API for the auto-generated copy constructor and assignment. However, in this case,
1629 * I wanted to clearly document the API; and since there are more than 1 constructors, it seemed better to explicitly
1630 * declare them all instead of replacing some with implicitly required direct initialization (again to make API more
1631 * clearly documented).
1632 *
1633 * ### Thread safety ###
1634 * The only operation of interest w/r/t threads is the aforementioned implicit assignment operator. Thread safety is
1635 * the same as for any `struct` with no locking done therein.
1636 */
1638{
1639public:
1640 // Constructors/destructor.
1641
1642 /**
1643 * Constructs Log_context by storing the given pointer to a Logger and a null Component.
1644 *
1645 * @param logger
1646 * Pointer to store. Rationale for providing the null default: To facilitate sub-class `= default` no-arg
1647 * ctors.
1648 */
1649 explicit Log_context(Logger* logger = nullptr);
1650
1651 /**
1652 * Constructs Log_context by storing the given pointer to a Logger and a new Component storing the
1653 * specified generically typed payload (an `enum` value). For more background on Component see its
1654 * doc header.
1655 *
1656 * @tparam Component_payload
1657 * See Component constructor doc header: `Payload` template arg specifically.
1658 * @param logger
1659 * Pointer to store.
1660 * @param component_payload
1661 * See Component constructor doc header: `payload` arg specifically.
1662 */
1663 template<typename Component_payload>
1664 explicit Log_context(Logger* logger, Component_payload component_payload);
1665
1666 /**
1667 * Copy constructor that stores equal `Logger*` and Component values as the source.
1668 *
1669 * This is `explicit`, even though an unintentional copy (e.g., in a `bind` sans `cref` or `ref`) would just
1670 * internally involve the copying a pointer (as of this writing). The reason is that it's unlikely one wants
1671 * to blithely copy these objects or objects of a sub-type; most likely (at least in scenarios seen so far, as of
1672 * this writing) a `cref` or `ref` is in order instead. (I am open to counter-examples and thus removing this
1673 * `explicit` keyword if convinced by one.)
1674 *
1675 * @param src
1676 * Source object.
1677 */
1678 explicit Log_context(const Log_context& src);
1679
1680 /**
1681 * Move constructor that makes this equal to `src`, while the latter becomes as-if default-constructed.
1682 *
1683 * @param src
1684 * Source object.
1685 */
1686 Log_context(Log_context&& src);
1687
1688 // Methods.
1689
1690 /**
1691 * Assignment operator that behaves similarly to the copy constructor.
1692 *
1693 * @param src
1694 * Source object.
1695 * @return `*this`.
1696 */
1698
1699 /**
1700 * Move assignment operator that behaves similarly to the move constructor.
1701 *
1702 * @param src
1703 * Source object.
1704 * @return `*this`.
1705 */
1707
1708 /**
1709 * Swaps Logger pointers and Component objects held by `*this` and `other`. No-op if `this == &other`.
1710 *
1711 * @param other
1712 * Other object.
1713 */
1714 void swap(Log_context& other);
1715
1716 /**
1717 * Returns the stored Logger pointer, particularly as many `FLOW_LOG_*()` macros expect.
1718 *
1719 * @note It's public at least so that FLOW_LOG_SET_CONTEXT() works in all reasonable contexts.
1720 *
1721 * @return See above.
1722 */
1723 Logger* get_logger() const;
1724
1725 /**
1726 * Sets the value to be returned by the next get_logger() call; returns get_logger() from before the change.
1727 *
1728 * Behavior is undefined if invoked concurrently with itself or get_logger() on the same `*this`.
1729 * If that is unacceptable: see our class doc header for brief discussion / suggestion on alternative to
1730 * Log_context.
1731 *
1732 * @param logger
1733 * As in ctor.
1734 * @return get_logger() pre-change.
1735 */
1736 Logger* set_logger(Logger* logger);
1737
1738 /**
1739 * Returns reference to the stored Component object, particularly as many `FLOW_LOG_*()` macros expect.
1740 *
1741 * @note It's public at least so that FLOW_LOG_SET_CONTEXT() works in all reasonable contexts.
1742 *
1743 * @return See above.
1744 */
1745 const Component& get_log_component() const;
1746
1747private:
1748 // Data.
1749
1750 /// The held Logger pointer. Making the pointer itself non-`const` to allow `operator=()` to work.
1752 /// The held Component object. Making the object non-`const` to allow `operator=()` to work.
1754}; // class Log_context
1755
1756/**
1757 * Identical to Log_context but is safe w/r/t to set_logger(), assignment, and `swap()` done concurrently to
1758 * ops (especially get_logger()) on the same `*this`.
1759 *
1760 * @see Log_context doc header section "Setting the logger / thready safety."
1761 *
1762 * There is a perf trade-off: essentially all operations will lock an internal mutex, proceed, then unlock.
1763 * This will have a small cost when there is no lock contention (no simultaneous logging -- get_logger() calls);
1764 * and a larger cost when there is (when indeed there is simultaneous logging -- therefore get_logger() calls).
1765 * Informally: in our experience, as noted in the aforementioned doc header discussion, set_logger() is mainly
1766 * used when the sub-class is instantiated `static`ally or globally; and usually it is possible to simply avoid
1767 * logging along fast-paths of such classes.
1768 *
1769 * Still: it requires care. If you need to provide/use set_logger(), be mindful of this potential source of
1770 * slow-down.
1771 */
1773 private Log_context
1774{
1775public:
1776 // Constructors/destructor.
1777
1778 /**
1779 * Identical to Log_context API.
1780 * @param logger
1781 * See above.
1782 */
1783 explicit Log_context_mt(Logger* logger = nullptr);
1784
1785 /**
1786 * Identical to Log_context API.
1787 * @tparam Component_payload
1788 * See above.
1789 * @param logger
1790 * See above.
1791 * @param component_payload
1792 * See above.
1793 */
1794 template<typename Component_payload>
1795 explicit Log_context_mt(Logger* logger, Component_payload component_payload);
1796
1797 /**
1798 * Identical to Log_context API.
1799 * @param src
1800 * See above.
1801 */
1802 explicit Log_context_mt(const Log_context_mt& src);
1803
1804 /**
1805 * Identical to Log_context API.
1806 * @param src
1807 * See above.
1808 */
1810
1811 // Methods.
1812
1813 /**
1814 * Identical to Log_context API; but safe against concurrent operations on a `*this`.
1815 *
1816 * @param src
1817 * See above.
1818 * @return See above.
1819 */
1821
1822 /**
1823 * Identical to Log_context API; but safe against concurrent operations on a `*this`.
1824 * @param src
1825 * See above.
1826 * @return See above.
1827 */
1829
1830 /**
1831 * Identical to Log_context API; but safe against concurrent operations on a `*this`.
1832 * @param other
1833 * See above.
1834 */
1835 void swap(Log_context_mt& other);
1836
1837 /**
1838 * Identical to Log_context API; but safe against concurrent operations on a `*this`.
1839 * @return See above.
1840 */
1841 Logger* get_logger() const;
1842
1843 /**
1844 * Identical to Log_context API; but safe against concurrent operations on a `*this`.
1845 * @param logger
1846 * See above.
1847 * @return See above.
1848 */
1849 Logger* set_logger(Logger* logger);
1850
1851 /**
1852 * Identical to Log_context API.
1853 * @return See above.
1854 */
1855 const Component& get_log_component() const;
1856
1857private:
1858 // Data.
1859
1860 /// Protects access to data in `static_cast<Log_context&>(*this)` (especially Log_context::m_logger).
1862}; // class Log_context_mt
1863
1864// Free functions: in *_fwd.hpp.
1865
1866// Template implementations.
1867
1868template<typename Payload>
1869Component::Component(Payload payload)
1870{
1871 // Currently uninitialized. Now:
1873}
1874
1875template<typename Payload>
1876Payload Component::payload() const
1877{
1878 // Zero processor cycles (an `enum` value *is* internally its integer value).
1879 return static_cast<Payload>(m_payload_enum_raw_value);
1880}
1881
1882template<typename Payload>
1883Component& Component::operator=(Payload new_payload)
1884{
1885 static_assert(std::is_enum_v<Payload>, "Payload type must be an enum.");
1886 static_assert(std::is_same_v<typename std::underlying_type_t<Payload>, enum_raw_t>,
1887 "Payload enum underlying type must equal enum_raw_t.");
1888
1889 // Might be optimized-out to storing, essentially, a constant at the call site of this assignment (or ctor).
1890 m_payload_type_or_null = &(typeid(Payload));
1891 // Zero processor cycles (an `enum` value *is* internally its integer value).
1892 m_payload_enum_raw_value = static_cast<enum_raw_t>(new_payload);
1893
1894 return *this;
1895}
1896
1897template<typename Component_payload>
1898Log_context::Log_context(Logger* logger, Component_payload component_payload) :
1899 m_logger(logger),
1900 m_component(component_payload)
1901{
1902 // Nothing.
1903}
1904
1905template<typename Component_payload>
1906Log_context_mt::Log_context_mt(Logger* logger, Component_payload component_payload) :
1907 Log_context(logger, component_payload)
1908{
1909 // Nothing.
1910}
1911
1912} // namespace flow::log
A light-weight class, each object storing a component payload encoding an enum value from enum type o...
Definition: log.hpp:842
Component & operator=(const Component &src)
Overwrites *this with a copy of src.
unsigned int enum_raw_t
The type Payload must be enum class Payload : enum_raw_t: an enum type encoded via this integer type.
Definition: log.hpp:847
Component(Component &&src_moved)
Constructs *this equal to src_moved.
std::type_info const * m_payload_type_or_null
The typeid() of the Payload passed to the 1-arg constructor; if 0-arg ctor was used (empty() is true)...
Definition: log.hpp:1019
const std::type_info & payload_type() const
Returns typeid(Payload), where Payload was the template param used when calling the originating one-a...
Definition: log.cpp:175
bool empty() const
Returns true if *this is as if default-constructed (a null Component); false if as if constructed via...
Definition: log.cpp:170
enum_raw_t m_payload_enum_raw_value
The internally stored integer representation of the enum value passed to the 1-arg constructor; meani...
Definition: log.hpp:1025
Component(const Component &src)
Copies the source Component into *this.
Payload payload() const
Returns reference to immutable payload stored in *this; undefined behavior if empty() == true.
Definition: log.hpp:1876
Component & operator=(Component &&src_moved)
Makes *this equal to src_moved.
enum_raw_t payload_enum_raw_value() const
Returns the numeric value of the enum payload stored by this Component, originating in the one-arg co...
Definition: log.cpp:181
Component()
Constructs a Component that stores no payload; meaning an unspecified-component Component that return...
Definition: log.cpp:158
Identical to Log_context but is safe w/r/t to set_logger(), assignment, and swap() done concurrently ...
Definition: log.hpp:1774
void swap(Log_context_mt &other)
Identical to Log_context API; but safe against concurrent operations on a *this.
Definition: log.cpp:339
Log_context_mt & operator=(const Log_context_mt &src)
Identical to Log_context API; but safe against concurrent operations on a *this.
Definition: log.cpp:271
util::Mutex_non_recursive m_mutex
Protects access to data in static_cast<Log_context&>(*this) (especially Log_context::m_logger).
Definition: log.hpp:1861
Log_context_mt(Logger *logger=nullptr)
Identical to Log_context API.
Definition: log.cpp:246
const Component & get_log_component() const
Identical to Log_context API.
Definition: log.cpp:333
Logger * get_logger() const
Identical to Log_context API; but safe against concurrent operations on a *this.
Definition: log.cpp:321
Logger * set_logger(Logger *logger)
Identical to Log_context API; but safe against concurrent operations on a *this.
Definition: log.cpp:327
Convenience class that simply stores a Logger and/or Component passed into a constructor; and returns...
Definition: log.hpp:1638
Log_context(const Log_context &src)
Copy constructor that stores equal Logger* and Component values as the source.
Log_context & operator=(const Log_context &src)
Assignment operator that behaves similarly to the copy constructor.
const Component & get_log_component() const
Returns reference to the stored Component object, particularly as many FLOW_LOG_*() macros expect.
Definition: log.cpp:226
Log_context(Logger *logger=nullptr)
Constructs Log_context by storing the given pointer to a Logger and a null Component.
Definition: log.cpp:189
Logger * set_logger(Logger *logger)
Sets the value to be returned by the next get_logger() call; returns get_logger() from before the cha...
Definition: log.cpp:220
void swap(Log_context &other)
Swaps Logger pointers and Component objects held by *this and other.
Definition: log.cpp:231
Component m_component
The held Component object. Making the object non-const to allow operator=() to work.
Definition: log.hpp:1753
Logger * m_logger
The held Logger pointer. Making the pointer itself non-const to allow operator=() to work.
Definition: log.hpp:1751
Logger * get_logger() const
Returns the stored Logger pointer, particularly as many FLOW_LOG_*() macros expect.
Definition: log.cpp:215
Interface that the user should implement, passing the implementing Logger into logging classes (Flow'...
Definition: log.hpp:1286
static void set_thread_info_in_msg_metadata(Msg_metadata *msg_metadata)
Loads msg_metadata->m_call_thread_nickname (if set) or else msg_metadata->m_call_thread_id,...
Definition: log.cpp:144
static boost::thread_specific_ptr< std::string > s_this_thread_nickname_ptr
Thread-local storage for each thread's logged name (null pointer, which is default,...
Definition: log.hpp:1554
virtual void do_log(Msg_metadata *metadata, util::String_view msg)=0
Given a message and its severity, logs that message and possibly severity WITHOUT checking whether it...
static std::ostream & this_thread_logged_name_os_manip(std::ostream &os)
ostream manipulator function that, if output via operator<< to an ostream, will cause the current thr...
Definition: log.cpp:108
static void this_thread_set_logged_nickname(util::String_view thread_nickname={}, Logger *logger_ptr=nullptr, bool also_set_os_name=true)
Sets or unsets the current thread's logging-worthy string name; optionally sets the OS thread name (s...
Definition: log.cpp:36
virtual bool should_log(Sev sev, const Component &component) const =0
Given attributes of a hypothetical message that would be logged, return true if that message should b...
std::ostream * this_thread_ostream() const
Returns the stream dedicated to the executing thread and this Logger, so that the caller can apply st...
Definition: log.cpp:150
virtual bool logs_asynchronously() const =0
Must return true if do_log() at least sometimes logs the given message and metadata (e....
static void set_thread_info(std::string *call_thread_nickname, util::Thread_id *call_thread_id)
Same as set_thread_info_in_msg_metadata() but targets the given two variables as opposed to a Msg_met...
Definition: log.cpp:125
An empty interface, consisting of nothing but a default virtual destructor, intended as a boiler-plat...
Definition: util.hpp:46
Each object of this class stores (at construction) and returns (on demand) a numeric ID unique from a...
Flow module providing logging functionality.
Sev
Enumeration containing one of several message severity levels, ordered from highest to lowest.
Definition: log_fwd.hpp:225
Thread::id Thread_id
Short-hand for an OS-provided ID of a util::Thread.
Definition: util_fwd.hpp:102
boost::mutex Mutex_non_recursive
Short-hand for non-reentrant, exclusive mutex. ("Reentrant" = one can lock an already-locked-in-that-...
Definition: util_fwd.hpp:227
Basic_string_view< char > String_view
Commonly used char-based Basic_string_view. See its doc header.
Simple data store containing all of the information generated at every logging call site by flow::log...
Definition: log.hpp:1043
std::chrono::system_clock::time_point m_called_when
Time stamp from as close as possible to entry into the log call site (usually FLOW_LOG_WARNING() or s...
Definition: log.hpp:1114
util::Thread_id m_call_thread_id
Thread ID of the thread from which the log call was invoked; or a default-constructed (no-thread) suc...
Definition: log.hpp:1139
unsigned int m_msg_src_line
Copy of integer that would have come from built-in __LINE__ macro which is auto-invoked by all FLOW_L...
Definition: log.hpp:1104
Sev m_msg_sev
Severity of message, typically determined by choice of macro (e.g., FLOW_LOG_WARNING() vs.
Definition: log.hpp:1053
std::string m_call_thread_nickname
Thread nickname, as for Logger::this_thread_set_logged_nickname(), of the thread from which the log c...
Definition: log.hpp:1129
util::String_view m_msg_src_file
Pointer/length into static-storage string that would have come from built-in __FILE__ macro which is ...
Definition: log.hpp:1098
Component m_msg_component
Component of message, as of this writing coming from either Log_context constructor or FLOW_LOG_SET_C...
Definition: log.hpp:1047
util::String_view m_msg_src_function
Analogous to m_msg_src_file but coming from __FUNCTION__, not __FILE__. See m_msg_src_file perf notes...
Definition: log.hpp:1107