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