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