Flow 1.0.0
Flow project: Full implementation reference.
log_fwd.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/// @cond
22// -^- Doxygen, please ignore the following.
23#define BOOST_FILESYSTEM_NO_DEPRECATED 1
24/// @endcond
25#include <boost/filesystem.hpp>
26#include <boost/filesystem/fstream.hpp>
27
28/**
29 * Flow module providing logging functionality. While originally intended to
30 * be used from within the flow::net_flow module's implementation, it can be used by general user code as well.
31 * All other Flow modules expose flow::log concepts when logging is relevant, so one way or another the user
32 * is likely to have at least limited contact with flow::log if they use Flow at all. (In particular, classes
33 * and free/`static` functions in various Flow modules often take a Logger pointer as a constructor
34 * or function argument respectively. Hence to use such APIs one must instantiate a concrete Logger, the simplest
35 * choice being probably Simple_ostream_logger.)
36 *
37 * (The general user [in their own, non-Flow-related code] may well
38 * prefer to use another logging system directly instead; or use boost.log.
39 * However, we humbly recommend taking a look at flow::log as a possibility, as it is highly usable, yet fast, yet
40 * small and elegant enough to ensure complete visibility into its low-level behavior, including how that affects
41 * performance. Note also that this system easily integrates with others via log::Logger interface, so one can stack
42 * this on top of perhaps a lower-level logging facility. The performance cost of this is essentially 1-2
43 * `virtual` pointer lookups per log-message call.)
44 *
45 * Log philosophy of Flow is as follows. The following is a list of goals; and for each item, sub-item(s)
46 * explain how this goal is accomplished.
47 *
48 * - Flow is a library or set of libraries -- not an application or set of applications -- so logging has to flexibly
49 * work with any logging system the user code may use.
50 * - The Logger interface provides a way to efficiently log to an arbitrary logging output; the flow::log
51 * user must provide a simple Logger implementation that fills in what Logger::should_log() and
52 * Logger::do_log() mean.
53 * - To ensure maximum simplicity and usability right out of the box,
54 * flow::log provides a Logger implementation, Simple_ostream_logger, which will log to standard `ostream`s
55 * (including `cout` and `cerr`). The user can just use this out of the box and not even have to implement
56 * the (already trivial) Logger interface. (Probably most larger projects would need to go beyond this.)
57 * - The logging system has to provide both error messages and informational or trace messages, and these
58 * should be easily distinguishable once passed on to the user's supplied arbitrary logging output(s).
59 * - There are at least 3 message severities, and they are passed to the Logger implementation, so
60 * that this can be translated as necessary into the user's logging output(s).
61 * - The logging system is not a replacement for error reporting by Flow APIs.
62 * - flow::Error_code and flow::error provide at least error code reporting (in addition to log calls)
63 * where possible and applicable.
64 * - The logging should be possible to entirely enable, entirely disable, or some reasonable point
65 * between these extremes.
66 * - The Logger interface's Logger::should_log() method allows filtering messages by severity (e.g., allow
67 * only WARNING messages) in any desired fashion. There is now also support for setting filtering config
68 * by "component" supplied essentially at the log message call site.
69 * - If a given message is not ultimately logged due its severity, it should induce no performance or resource
70 * penalty (such as that incurred by assembling the message string) beyond the severity check itself.
71 * - The `FLOW_LOG_...()` macros work with the `Logger::should_log()`/`Logger::do_log()` APIs to only
72 * construct the ultimate message string if the resulting message would be logged.
73 * - Logging should be simple without lots of extraneous features.
74 * - A few simple-to-use macros are the main way to log messages; see those first.
75 * - Every logged message just has a severity and component, which are `enum`s, and the text of the message.
76 * File/function/line and thread ID info are auto-obtained and maintained along with the
77 * messages themselves, via the `FLOW_LOG_...()` macros. The user need not worry about such mundane but tricky
78 * details.
79 * - The `FLOW_LOG_...()` macros assume that `get_logger()` (in the context where the macro is expanded)
80 * returns a `Logger*` implementation, which is then used for the logging call(s) associated with the
81 * message -- typically first the Logger::should_log() check and then possibly the Logger::do_log()
82 * message output. Deriving from the supplied Log_context utility class is an easy way to make this
83 * `get_logger()` available all over the Flow modules' implementations (and should indeed be a technique for the
84 * user's own logging code, if indeed they decided to use flow::log themselves).
85 *
86 * @internal
87 *
88 * log_fwd.hpp is separate from log.hpp due to C++ circular dependency nonsense. See util_fwd.hpp for some
89 * discussion on this same concept.
90 *
91 * @todo There are third-party logging systems including boost.log. The to-do is to
92 * investigate using boost.log, partially or entirely replacing the manually implemented system.
93 * (To be honest I am rather partial to the simple but effective system already implemented, in particular
94 * the performance-focused trickery.) Boost is invaluable
95 * in many ways, and Flow modules use it extremely extensively, but my intuition says that in this case it may be
96 * irrelevant. Meanwhile, boost.log can, of course, be used within user's Logger interface implementation,
97 * but that's not what this to-do is about; it's about actually placing it all over Flow's implementation and
98 * APIs *in place of* Logger, log::Sev, etc. (Also, after a 5-minute glance at boost.log,
99 * I am noticing that some of its ideas I seem to have independently and
100 * unknowingly replicated here. It is, however, simple stuff, not rocket science.) My intuition is I hesitate to
101 * force-marry the Flow user to boost.log; the very simple Logger (abstract) interface is seemingly a gentler
102 * thing to force the Flow user into, at least relatively speaking.
103 *
104 * @todo Lacking feature: message IDs. This is discussed a bit more in the log::Msg_metadata doc header.
105 *
106 * @todo Lacking feature: `printf()`-style logging call sites, in contrast to the currently supported `ostream`
107 * fragment call sites. So, like, `FLOW_LOG_WARNING_FMT("Hello, Mr. %s!", my_name.c_str())` could be used
108 * analogously to `FLOW_LOG_WARNING("Hello, Mr " << my_name << "!!")` (with `std::string my_name`).
109 * In terms of implementation, there is more to this than meets the eye perhaps; as `ostream`s potentially
110 * store (formatting) state between totally separate invocations of `ostream<<`, whereas `printf()` style
111 * functions normally do not. Internally, this likely allows for specially optimized logging code.
112 *
113 * @todo Lacking feature: boost.format-style logging call sites. This is conceptually similar to -- and possibly even
114 * entirely subsuming in backwards-compatible fashion -- the to-do just above for `printf()`-style logging.
115 * Likely both to-dos should be designed/implemented in one shot. Moreover, it is possible (and would be
116 * absolutely delightful if true) that boost.format's `format` class can be used entirely transparently on top
117 * of the `ostream`-focused existing flow::log API (such as FLOW_LOG_WARNING() and buddies)!
118 *
119 * @todo Lacking feature: log message rate-limiting. This could actually mean a few things. One is being able
120 * to rate-limit the messages produced a given log call site per unit time; this might be the minimum for this to-do.
121 * This could be done by a dirty macro hack; or it could be done in more civilized fashion (while minding perf)
122 * by configuring it by message ID (but message IDs are optional and not implemented as of this writing anyway).
123 * Another rate-limiting thing is general controls on frequency of logging; though arguably that is more applicable
124 * to individual `Logger` implementations rather than as a mandatory-general mechanism.
125 *
126 * @todo Lacking feature: compiler hints for optimizing away log filter checks. This is inspired by a certain
127 * other proprietary logging API in which we noticed attempts to give hints to the compiler as to how likely or
128 * unlikely the verbosity check is to return a true or false value, seemingly so that the compiler might
129 * optimize out the check and hence the entire log statement in some conditions; or *always* log others and
130 * skip the check in the optimized code. I omit any details here, but that's the general idea. I don't know
131 * how effective this is given that verbosities are configurable dynamically potentially; but look into it.
132 */
133namespace flow::log
134{
135
136// Types.
137
138// Find doc headers near the bodies of these compound types.
139
140class Async_file_logger;
141class Buffer_logger;
142class Component;
143class Config;
144class Logger;
145class Log_context;
146struct Msg_metadata;
147class Ostream_log_msg_writer;
148class Simple_ostream_logger;
149
150
151/* (The @namespace and @brief thingies shouldn't be needed, but some Doxygen bug necessitated them.
152 * See flow::util::bind_ns for explanation... same thing here.) */
153
154/**
155 * @namespace flow::log::fs
156 * @brief Short-hand for `namespace boost::filesystem`.
157 */
158namespace fs = boost::filesystem;
159
160/**
161 * Enumeration containing one of several message severity levels, ordered from highest to
162 * lowest. Generally speaking, volume/verbosity is inversely proportional to severity, though
163 * this is not enforced somehow.
164 *
165 * As the underlying type is `size_t`, and the values are guaranteed to be 0, 1, ... going from lowest
166 * to highest verbosity (highest to lowest severity), you may directly use log::Sev values to index into arrays that
167 * arrange one-to-one values in the same order.
168 *
169 * The supplied `ostream<<` operator, together with this `enum`, is suitable for util::istream_to_enum().
170 * `ostream>>` operator is built on the latter and is also supplied. Hence I/O of `Sev` is available out of the box;
171 * this enables parsing in boost.program_options among other things.
172 *
173 * ### Formal semantics of log::Sev ###
174 * From the point of view of all Flow code, the *formal* semantics of log::Sev are as follows:
175 * - Sev::S_NONE has value 0 and is a start-sentinel.
176 * - Non-sentinel severities follow, starting at 1 with no gaps.
177 * - Ordinal comparison is meaningful; lower is termed as more severe/less verbose; higher is therefore
178 * termed as less severe/more verbose. These are, formally, only terminology.
179 * - Sev::S_END_SENTINEL is the end-sentinel.
180 * - Sev::S_WARNING is and always shall be considered the *least severe* "abnormal" condition. Any additional
181 * "abnormal" conditions shall always have more-severe (numerically lower) values.
182 * As of this writing, within flow::log Flow module, this has only one practical meaning (but that could change):
183 * Simple_ostream_logger directs WARNING-and-more-severe messages to its configured *error* stream (`os_for_err`);
184 * while all the rest are directed to its configured other (regular) stream (`os`). (For example these might
185 * be set to `std::cerr` and `std::cout` respectively; or both to `std::cout`.)
186 *
187 * ### Informal semantics of log::Sev ###
188 * The following are not enforced by any code logic in the flow::log Flow module. However we suggest users
189 * understand and consider these, as a haphazard approach to severity selection for a given log call site can
190 * cause significant operational problems in a high-volume production environment (at least).
191 *
192 * Generally, there are 2 types of log call sites: ones inside Flow; and ones outside (user code). The former
193 * (inside Flow) are deliberately minimalistic, so as to use as few severities as possible and thus make
194 * severity selection maximally simple and, importantly, unambiguous.
195 *
196 * However, logging code outside Flow may require more sophisticated in-use severity sets. Informally we *recommend*
197 * keeping it as simple as inside-Flow's scheme... if it is sufficient. If it is not sufficient, the other
198 * severities may also be used.
199 *
200 * The log::Sev values used inside Flow (the *minimalistic* set) are as follows. (Brief meanings accompany them;
201 * see their individual doc headers for more detail.) From most to least severe:
202 * - Sev::S_FATAL: the program will abort shortly due to the condition that is being logged. Usually an
203 * `assert(false)` and/or `std::abort()` follows. If you've disabled abort-on-assertion-trip (`NDEBUG` is defined),
204 * and there is no `std::abort()` or equivalent, then the program may continue, but subsequent behavior is
205 * undefined.
206 * - Sev::S_WARNING: abnormal condition is being logged, and its aggregate volume is not high enough to be classified
207 * as TRACE instead to avoid perf impact. (Other than possibly FATAL -- used in extreme cases -- no
208 * other abnormal-condition `Sev` are in-use inside Flow nor will they be.)
209 * - Sev::S_INFO: non-abnormal condition is being logged, and it's not so frequent in practice that enabling
210 * this log level shall adversely impact performance.
211 * - Sev::S_TRACE: like INFO, but enabling this log level *can* adversely impact performance. However, entire
212 * dumps of potentially large (in aggregate) data (such as packet contents) being processed are not included.
213 * - Sev::S_DATA: like TRACE, but entire contents of potentially large (in aggregate) data (such as packet contents)
214 * being processed are included in the message, which may lead to particularly large log output (if enabled).
215 *
216 * As of this writing the following log::Sev values are available for use (by user/non-Flow code) beyond the above
217 * *minimalistic* set. Potential subjective meanings are included, but user code can use whatever conventions that
218 * suit them best.
219 * - Sev::S_ERROR: A non-FATAL abnormal condition subjectively more severe than WARNING.
220 * - Sev::S_DEBUG: A non-abnormal condition with, perhaps, non-perf-affecting verbosity (a-la INFO) but subjectively
221 * of less interest to a human glancing over a large-ish log snippet (a-la TRACE).
222 */
223enum class Sev : size_t
224{
225 /**
226 * Sentinel log level that must not be specified for any actual message (at risk of undefined behavior such
227 * as assert failure); but can be used for sentinel purposes such as specifying a log filter wherein
228 * no messages (not even Sev::S_WARNING ones) are shown.
229 */
230 S_NONE = 0,
231
232 /**
233 * Message indicates a "fatally bad" condition, such that the program shall imminently abort, typically due
234 * to immediately-following `assert(false)` and possibly `std::abort()`; or if aborts are disabled (such as
235 * via defining `NDEBUG`), and there is no explicit `std::abort()` or equivalent, then further
236 * program behavior is undefined.
237 *
238 * @note This severity *is* part of the *minimalistic* (in-use within Flow) severity set as discussed
239 * in log::Sev doc header, "Informal semantics" section.
240 */
241 S_FATAL,
242
243 /**
244 * Message indicates a "bad" condition with "worse" impact than that of Sev::S_WARNING.
245 *
246 * @note If it's "bad" but frequent enough for TRACE, we strongly recommend to make it TRACE -- not ERROR.
247 *
248 * @note This severity *is NOT* part of the *minimalistic* (in-use within Flow) severity set as discussed
249 * in log::Sev doc header, "Informal semantics" section. Informally we recommend projects
250 * do not use it unless it is holistically necessary.
251 */
252 S_ERROR,
253
254 /**
255 * Message indicates a "bad" condition that is not frequent enough to be of severity Sev::S_TRACE.
256 * These typically should occur with less frequency than INFO messages; however, it's not a hard rule.
257 *
258 * @note If it's "bad" but frequent enough for TRACE, it's TRACE by definition -- not WARNING.
259 *
260 * @note This severity *is* part of the *minimalistic* (in-use within Flow) severity set as discussed
261 * in log::Sev doc header, "Informal semantics" section.
262 */
263 S_WARNING,
264
265 /**
266 * Message indicates a not-"bad" condition that is not frequent enough to be of severity Sev::S_TRACE.
267 *
268 * @note That is, it's identical to WARNING *except* doesn't represent a "bad" situation.
269 *
270 * @note This severity *is* part of the *minimalistic* (in-use within Flow) severity set as discussed
271 * in log::Sev doc header, "Informal semantics" section.
272 */
273 S_INFO,
274
275 /**
276 * Message indicates a condition with, perhaps, no significant perf impact if enabled (like Sev::S_INFO)
277 * but of subjectively less interest to a human reader than INFO (hence, like Sev::S_TRACE in that respect).
278 *
279 * @note This severity *is NOT* part of the *minimalistic* (in-use within Flow) severity set as discussed
280 * in log::Sev doc header, "Informal semantics" section. Informally we recommend projects
281 * do not use it unless it is holistically necessary.
282 */
283 S_DEBUG,
284
285 /**
286 * Message indicates any condition that may occur with great frequency (thus verbose if logged).
287 * The line between Sev::S_INFO or Sev::S_WARNING and TRACE is as follows: The former is *not* allowed to classify
288 * messages such that in realistic scenarios they would degrade performance, from processor cycles or log file I/O.
289 *
290 * @see Sev::S_DATA for an even-more-verbose severity, when it's frequent and potentially large in size in aggregate.
291 *
292 * @warning One MUST be able to set max severity level to INFO and confidently count that logging will not
293 * affect performance.
294 *
295 * @note This severity *is* part of the *minimalistic* (in-use within Flow) severity set as discussed
296 * in log::Sev doc header, "Informal semantics" section.
297 */
298 S_TRACE,
299
300 /**
301 * Message satisfies Sev::S_TRACE description AND contains variable-length structure (like packet, file) dumps.
302 * If these are allowed to be logged, resulting log file might be roughly similar to (or even larger than)
303 * the data being transmitted by the logging code. Packet dumps are obvious examples.
304 *
305 * Note that just because it's a variable-length structure dump doesn't mean it's for Sev::S_DATA severity.
306 * If it's not frequent, it's fine for it to even be INFO. E.g., if I decide to dump every
307 * RST packet, it's probably okay as Sev::S_INFO, since RSTs are practically rare;
308 * that need not be Sev::S_DATA. On the other hand, if I dump every DATA packet
309 * as anything except Sev::S_DATA severity, then I should be arrested and convicted.
310 *
311 * When this level is disabled, consider logging a TRACE message instead but summarize the contents you'd
312 * dump; e.g., log a hash and/or a size and/or the few first bytes instead of the entire thing. Something is
313 * often better than nothing (but nothing is still safer than the whole thing).
314 *
315 * @note This severity *is* part of the *minimalistic* (in-use within Flow) severity set as discussed
316 * in log::Sev doc header, "Informal semantics" section.
317 */
318 S_DATA,
319
320 /// Not an actual value but rather stores the highest numerical payload, useful for validity checks.
322}; // enum class Sev
323
324// Free functions.
325
326/**
327 * Deserializes a log::Sev from a standard input stream. Reads up to but not including the next
328 * non-alphanumeric-or-underscore character; the resulting string is then mapped to a log::Sev. If none is
329 * recognized, Sev::S_NONE is the result. The recognized values are:
330 * - "0", "1", ...: Corresponds to the `int` conversion of that log::Sev (e.g., 0 being NONE).
331 * - Case-insensitive encoding of the non-S_-prefix part of the actual log::Sev member; e.g.,
332 * "warning" (or "Warning" or "WARNING" or...) for `S_WARNING`.
333 * This enables a few key things to work, including parsing from config file/command line via and conversion from
334 * `string` via `boost::lexical_cast`.
335 *
336 * @param is
337 * Stream from which to deserialize.
338 * @param val
339 * Value to set.
340 * @return `is`.
341 */
342std::istream& operator>>(std::istream& is, Sev& val);
343// @todo - `@relatesalso Sev` makes Doxygen complain; maybe it doesn't work with `enum class`es like Sev.
344
345/**
346 * Serializes a log::Sev to a standard output stream. The output string is compatible with the reverse
347 * `istream>>` operator.
348 *
349 * @param os
350 * Stream to which to serialize.
351 * @param val
352 * Value to serialize.
353 * @return `os`.
354 */
355std::ostream& operator<<(std::ostream& os, Sev val);
356// @todo - `@relatesalso Sev` makes Doxygen complain; maybe it doesn't work with `enum class`es like Sev.
357
358/**
359 * Log_context ADL-friendly swap: Equivalent to `val1.swap(val2)`.
360 * @param val1
361 * Object.
362 * @param val2
363 * Object.
364 */
365void swap(Log_context& val1, Log_context& val2);
366
367/**
368 * Sets certain `chrono`-related formatting on the given Logger in the current thread that results in a consistent,
369 * desirable output of `duration`s and certain types of `time_point`s. The effect is that of
370 * util::beautify_chrono_ostream().
371 *
372 * @see flow::async in which new threads are set to use this formatting automatically. However you'll want to
373 * do this explicitly for the startup thread.
374 *
375 * @param logger_ptr
376 * The Logger to affect in this thread. Null is allowed; results in no-op.
377 */
378void beautify_chrono_logger_this_thread(Logger* logger_ptr);
379
380/**
381 * Estimate of memory footprint of the given value, including memory allocated on its behalf -- but
382 * excluding its shallow `sizeof`! -- in bytes.
383 *
384 * @param val
385 * Value.
386 * @return See above.
387 */
388size_t deep_size(const Msg_metadata& val);
389
390} // namespace flow::log
We may add some ADL-based overloads into this namespace outside flow.
Definition: cfg_fwd.hpp:267
Flow module providing logging functionality.
std::ostream & operator<<(std::ostream &os, Sev val)
Serializes a log::Sev to a standard output stream.
Definition: log.cpp:249
void swap(Log_context &val1, Log_context &val2)
Log_context ADL-friendly swap: Equivalent to val1.swap(val2).
Definition: log.cpp:242
size_t deep_size(const Msg_metadata &val)
Estimate of memory footprint of the given value, including memory allocated on its behalf – but exclu...
Definition: log.cpp:288
std::istream & operator>>(std::istream &is, Sev &val)
Deserializes a log::Sev from a standard input stream.
Definition: log.cpp:269
Sev
Enumeration containing one of several message severity levels, ordered from highest to lowest.
Definition: log_fwd.hpp:224
@ S_DATA
Message satisfies Sev::S_TRACE description AND contains variable-length structure (like packet,...
@ S_TRACE
Message indicates any condition that may occur with great frequency (thus verbose if logged).
@ S_WARNING
Message indicates a "bad" condition that is not frequent enough to be of severity Sev::S_TRACE.
@ S_NONE
Sentinel log level that must not be specified for any actual message (at risk of undefined behavior s...
@ S_DEBUG
Message indicates a condition with, perhaps, no significant perf impact if enabled (like Sev::S_INFO)...
@ S_ERROR
Message indicates a "bad" condition with "worse" impact than that of Sev::S_WARNING.
@ S_INFO
Message indicates a not-"bad" condition that is not frequent enough to be of severity Sev::S_TRACE.
@ S_FATAL
Message indicates a "fatally bad" condition, such that the program shall imminently abort,...
void beautify_chrono_logger_this_thread(Logger *logger_ptr)
Sets certain chrono-related formatting on the given Logger in the current thread that results in a co...
Definition: log.cpp:278
@ S_END_SENTINEL
CAUTION – see flow::Flow_log_component doc header for directions to find actual members of this enum ...