Flow 1.0.0
Flow project: Public API.
Public Types | Public Member Functions | Static Public Member Functions | Public Attributes | Static Public Attributes | List of all members
flow::log::Config Class Reference

Class used to configure the filtering and logging behavior of Loggers; its use in your custom Loggers is optional but encouraged; supports dynamically changing filter settings even while concurrent logging occurs. More...

#include <config.hpp>

Public Types

using component_union_idx_t = Component::enum_raw_t
 Unsigned index into the flat union of component tables maintained by a Config, combining potentially multiple user component enum tables. More...
 
using Component_to_union_idx_func = Function< component_union_idx_t(const Component &)>
 Short-hand for a function that takes a Component (storing a payload of some generic component enum member of the logging user's choice) and returns its corresponding flat union component index.
 

Public Member Functions

 Config (Sev most_verbose_sev_default=S_MOST_VERBOSE_SEV_DEFAULT)
 Constructs a conceptually blank but functional set of Config. More...
 
 Config (const Config &src)
 Copy-constructs *this to be equal to src config object. More...
 
 Config (Config &&)=delete
 For now at least there's no reason for move-construction. More...
 
void operator= (const Config &)=delete
 For now at least there's no reason for copy assignment. More...
 
void operator= (Config &&)=delete
 For now at least there's no reason for move assignment. More...
 
bool output_whether_should_log (Sev sev, const Component &component) const
 A key output of Config, this computes the verbosity-filtering answer to Logger::should_log() based on the given log-call-site severity and component and the verbosity configuration in this Config, including the value at *(this_thread_verbosity_override()), the value from configure_default_verbosity(), and the config from configure_component_verbosity*(). More...
 
bool output_component_to_ostream (std::ostream *os, const Component &component) const
 An output of Config, this writes a string representation of the given component value to the given ostream, if possible. More...
 
template<typename Component_payload >
void init_component_to_union_idx_mapping (component_union_idx_t enum_to_num_offset, size_t enum_sparse_length)
 Registers a generically-typed enum class that represents the full set of the calling module's possible component values that it will supply at subsequent log call sites from that module. More...
 
template<typename Component_payload >
void init_component_names (const boost::unordered_multimap< Component_payload, std::string > &component_names, bool output_components_numerically=false, util::String_view payload_type_prefix_or_empty=util::String_view())
 Registers the string names of each member of the enum class Component_payload earlier registered via init_component_to_union_idx_mapping<Component_payload>(). More...
 
void configure_default_verbosity (Sev most_verbose_sev_default, bool reset)
 Sets the default verbosity to the given value, to be used by subsequent output_whether_should_log() calls whenever one supplies it a component for which no per-component verbosity is configured at that time; optionally wipes out all existing per-component verbosities for a constructor-like reset. More...
 
template<typename Component_payload >
bool configure_component_verbosity (Sev most_verbose_sev, Component_payload component_payload)
 Sets the per-component verbosity for the given component to the given value, to be used by subsequent output_whether_should_log() calls whenever one supplies it the same component value. More...
 
bool configure_component_verbosity_by_name (Sev most_verbose_sev, util::String_view component_name)
 Like configure_component_verbosity(), but the component is to be specified by its registered string name, well suited to interpreting text config files. More...
 

Static Public Member Functions

template<typename Component_payload >
static size_t standard_component_payload_enum_sparse_length ()
 Returns highest numeric value in the given component-payload enum, plus 1, assuming that enum was created using the config_enum_start_hdr.macros.hpp mechanism with all requirements followed by user. More...
 
static Sevthis_thread_verbosity_override ()
 Returns pointer to this thread's mutable verbosity override, for querying or assignment alike. More...
 
static util::Scoped_setter< Sevthis_thread_verbosity_override_auto (Sev most_verbose_sev_or_none)
 Sets *(this_thread_verbosity_override()) = most_verbose_sev_or_none; and returns an object that shall restore it to its current value when it goes out of scope. More...
 

Public Attributes

bool m_use_human_friendly_time_stamps
 Config setting: If true, time stamps will include a (deterministically formatted) date, time, time zone, all in the OS's current time zone; else raw # of seconds passed since POSIX (Unix) Epoch (1970, Jan 1, 00:00, GMT). More...
 

Static Public Attributes

static const Sev S_MOST_VERBOSE_SEV_DEFAULT = Sev::S_INFO
 Recommended default/catch-all most-verbose-severity value if no specific config is given.
 

Detailed Description

Class used to configure the filtering and logging behavior of Loggers; its use in your custom Loggers is optional but encouraged; supports dynamically changing filter settings even while concurrent logging occurs.

If you are reading this to know how to configure an existing Logger, then you don't need further background; just see this API to know how to configure such Loggers in a uniform way.

If you are, instead, reading this when implementing a new custom Logger, then please see implementation recommendations in Logger doc header before continuing here.

Todo:
Class Config doc header is wordy and might be hard to follow; rewrite for clarity/flow/size.

Synopsis: How do I use it to configure a Logger? Just tell me!

Really it's pretty easy to use it, but it's much easier to see how it's done by example rather than a formal-ish description (which is nevertheless below, in the next section of this doc header). To use Config with a Config-supporting Logger in your flow::log-using module M:

What Config controls and how

Let's get into it more formally.

Firstly, Config simply stores simple scalars controlling output behavior. For example, the public member m_use_human_friendly_time_stamps controls the style of time stamps in the final output. It's just a data store for such things.

Secondly, Config knows how to understand the component values supplied at every single log call site in your program. (This also ties in to the next thing we discuss, verbosity config.) See Component doc header. Now, here's how Config understands components. In your program, you will use various flow::log-using libraries or modules – including (but not necessarily limited to!) Flow itself. Each module is likely to feature their own component table, in the form of an enum class. For example, Flow itself has enum class Flow_log_component (see common.hpp).

Thirdly, and crucially, the verbosity filtering (Logger::should_log()) for the client Logger is entirely implemented via the output_whether_should_log() output method; so Logger::should_log() can simply forward to that method. Here is how one configures its behavior in Config. At construction, or in a subsequent configure_default_verbosity() call, one sets the default verbosity, meaning the most-verbose log::Sev that should_log() would let through when no per-component verbosity for the log call site's specified Component is configured. In addition, assuming more fine-grained (per-component) verbosity config is desired, one can call configure_component_verbosity*() to set the most-verbose log::Sev for when should_log() is passed that specific Component payload.

Setting per-component verbosity can be done by its enum value. Or one can use the overload that takes the component's distinct (among all source component enum tables registered before) string name; in which case the aforementioned performant name-to-index mapping is used internally to set the proper union component's verbosity.

(Note that order of calls matters: configure_default_verbosity() wipes out effects of any individual, per-component configure_component_verbosity*() executed prior to it. It is typical (in a given round of applying config) to first call configure_default_verbosity() and then make 0 or more configure_component_verbosity*() calls for various individual components.)

Fourthly, output_component_to_ostream() is a significant helper for your Logger::do_log() when actually printing the ultimate character representation of the user's message and metadata. The metadata (Msg_metadata) includes a Component; that method will output its string representation to the ostream given to it. To do so it will use the aforementioned ability to quickly map the C1:: or C2:: member to the flat union index to that index's distinct name (the same name optionally used to configure verbosity of that component, as explained above).

Or one can opt to print the flat numeric index of the component instead; in which case the reverse name-to-union-idx is not needed.

Thread safety

Formally, Config is thread-safe for all operations when concurrent access is to separate Configs. There are no static data involved. Formally, Config is generally NOT thread-safe when concurrent read and write access is w/r/t to a single Config; this includes read/write of any public data members and read/write in the form const/otherwise method calls. Informally, one could use an outside mutex, including in any Logger implementation that uses *this, but we recommend against this for performance reasons; and see below "exception."

Also formally for a given *this: The logging phase is assumed to begin after all init_*() calls and any initial configure_*() calls; at this point output_whether_should_log() and output_component_to_ostream() may be used at will by any thread; but the pre-logging-phase non-const calls are no longer allowed.

There is an important exception to the assertion that Config *this one must NOT call any write methods once the logging phase has begun. Informally, this exception should make it possible to use Config safely and yet dynamically allow changes to Config without any expensive outside mutex. The exception is as follows:

Assume, as is proper, that you've called all needed init_component_to_union_idx_mapping() and init_component_names() before any concurrent logging and have now started to log – you are in the logging phase. Now assume you want to change verbosity settings during this logging-at-large phase; this is common, for example, when some dynamic config file changes verbosity settings for your program. The following is safe: You may call configure_default_verbosity() and/or configure_component_verbosity*() while expecting the changes to take effect promptly in all threads; namely, output_whether_should_log() will reflect the change there; and output_component_to_ostream() does not care. Perf-wise, little to nothing is sacrified (internally, a lock-free implementation is used).

Corner case: It is also safe to call configure_default_verbosity() and/or configure_component_verbosity*() concurrently with themselves. Naturally, it is a race as to which thread "wins." Moreover, configure_default_verbosity() with reset == true is equivalent to removing the verbosity setting for each individual component; but only each removal itself is atomic, not the overall batch operation; so concurrent execution with another configure_*_verbosity() call may result in an interleaved (but valid) verbosity table. Informally, we recommend against any design that would allow concurrent configure_*() calls on *this; it does not seem wise. It does however result in well-defined behavior as described. The real aim, though, is not this corner case but only the main case of a series of configure_*() calls in thread 1, while logging may be going on in other threads.

Optional: Thread-local verbosity override

In a pinch, it may be desirable – temporarily and in a given thread of execution only – to change the current verbosity config. To do so in a given scope {} simply do this:

{
// In this { scope } temporarily let-through only error messages or more severe.
// Now let's create 3,000 threads each of which would normally log a few "starting thread" startup INFO messages!
m_thread_pool = std::make_unique<flow::async::Cross_thread_task_loop>(get_logger(), "huge_pool", 3000);
m_thread_pool->start();
} // Previous thread-local verbosity is restored (whether there was one, or none) upon exit from {block}.
static util::Scoped_setter< Sev > this_thread_verbosity_override_auto(Sev most_verbose_sev_or_none)
Sets *(this_thread_verbosity_override()) = most_verbose_sev_or_none; and returns an object that shall...
Definition: config.cpp:260
@ S_WARNING
Message indicates a "bad" condition that is not frequent enough to be of severity Sev::S_TRACE.

You may also query the current setting via *(this_thread_verbosity_override()). Direct assignment of a Sev to the latter is allowed, but generally it is both safer and easier to use the RAII pattern via this_thread_verbosity_override_auto() for setting/restoring the override.

The value Sev::S_END_SENTINEL indicates the thread-local verbosity override is disabled; and is the initial (default) state in any thread. However, generally, it is recommended to use the RAII pattern via this_thread_verbosity_override_auto() instead of direct assignment, for safe and easy save/restore.

Note there are no thread-safety concerns with this feature, as it is entirely thread-local.

Member Typedef Documentation

◆ component_union_idx_t

Unsigned index into the flat union of component tables maintained by a Config, combining potentially multiple user component enum tables.

Also suitable for non-negative offsets against such indices.

Constructor & Destructor Documentation

◆ Config() [1/3]

flow::log::Config::Config ( Sev  most_verbose_sev_default = S_MOST_VERBOSE_SEV_DEFAULT)
explicit

Constructs a conceptually blank but functional set of Config.

Namely, no component enums are yet registered (call init_component_to_union_idx_mapping() and init_component_names() to register 0 or more such enum tables). The default verbosity is set to most_verbose_sev_default.

While you can and should register enums after this, if you don't then the object's outputs will act as follows:

Note that – particularly in a pinch and in simple applications – this is perfectly reasonable, simple behavior. One doesn't always need per-component verbosity configuration abilities; and one definitely doesn't always need to print the component name/index in the log output. But if one does need these things, then you can register enums as explained in class doc header.

Parameters
most_verbose_sev_defaultSame as in configure_default_verbosity().

◆ Config() [2/3]

flow::log::Config::Config ( const Config src)
default

Copy-constructs *this to be equal to src config object.

Performance-wise, this will copy internal per-component tables (in addition to a few scalars). These tables are conceptually unions of potentially multiple long enums; so this probably shouldn't be done often, but typically Config is constructed at startup or during rare config change events.

Warning
If this copy construction occurs very soon after a configure_default_verbosity() or configure_component_verbosity*() call in a different thread completes, then it is possible that call's effect won't register in the resulting *this. In the case of configure_default_verbosity() with reset == true the clearing of the per-component verbosities may register only partially in that situation, though *this will still be valid. Since "very soon" cannot be formally defined, it is therefore best to make such a copy in the same thread as the last verbosity-modifying call on src. (On the other hand, even otherwise results in valid behavior, but it may not be quite as deterministic as preferred and clean.)
Parameters
srcSource object.

◆ Config() [3/3]

flow::log::Config::Config ( Config &&  )
delete

For now at least there's no reason for move-construction.

Todo:
Reconsider providing a Config move constructor. I just didn't need to deal with it.

Member Function Documentation

◆ configure_component_verbosity()

template<typename Component_payload >
bool flow::log::Config::configure_component_verbosity ( Sev  most_verbose_sev,
Component_payload  component_payload 
)

Sets the per-component verbosity for the given component to the given value, to be used by subsequent output_whether_should_log() calls whenever one supplies it the same component value.

See also configure_default_verbosity().

This only works (and will return true) if init_component_to_union_idx_mapping<Component_payload>() has been called. Otherwise it returns false (caller may assert() against this result if it is felt justified).

See class doc header section "What Config controls and how" for more discussion.

Parameters
most_verbose_sevThe most-verbose (numerically highest) Sev sev value such that output_whether_should_log() will return true, when Component component is not null and has Component::payload<Component_payload>() return a value equal to component_payload.
component_payloadThe component for which verbosity is being set.
Returns
true on success; false otherwise. See above for more.

◆ configure_component_verbosity_by_name()

bool flow::log::Config::configure_component_verbosity_by_name ( Sev  most_verbose_sev,
util::String_view  component_name 
)

Like configure_component_verbosity(), but the component is to be specified by its registered string name, well suited to interpreting text config files.

The meaning of verbosity is the same as in the other overload.

This only works (and will return true) if init_component_names() has been called in such a way as to successfully associate a component in the flat union table with the name equal (after normalization of both sides) to component_name. If the name is unknown, it returns false.

Name normalization consists of conversion to upper case according to the classic ("C") locale.

See class doc header section "What Config controls and how" for more discussion.

Parameters
most_verbose_sevThe most-verbose (numerically highest) Sev sev value such that output_whether_should_log() will return true, when Component component is not null and has an associated string name equal to component_name (post-normalization of both sides of comparison).
component_nameThe component for which verbosity is being set.
Returns
true on success; false otherwise. See above for more.

◆ configure_default_verbosity()

void flow::log::Config::configure_default_verbosity ( Sev  most_verbose_sev_default,
bool  reset 
)

Sets the default verbosity to the given value, to be used by subsequent output_whether_should_log() calls whenever one supplies it a component for which no per-component verbosity is configured at that time; optionally wipes out all existing per-component verbosities for a constructor-like reset.

This is fairly intuitive; the only aspect one might find non-obvious is reset == true mode. In that mode all per-component verbosities are forgotten, as after construction. An intended use scenario is when reading a hypothetical config file describing new, dynamic overall verbosity settings to replace any existing ones. Such a config file would probably specify the catch-all (default) verbosity; then 0 or more per-component "exception" verbosities. Hence once would call this method with reset == true accordingly to reset everything and set the default; then one would call configure_component_verbosity*() for each "exception."

reset == true is technically slower than otherwise, though it is doubtful one would call us frequently enough for it to matter. The perf cost of !reset is constant time and basically that of a scalar assignment. The perf cost of reset == true is that plus the cost of about N configure_component_verbosity() calls, where N is the highest flat-union-component-table implied by the enum_sparse_length arg to init_component_to_union_idx_mapping() calls to date. In practice doing this when outside config changes is unlikely to be a perf issue.

Thread safety on *this

If called, it must be called after all init_component_to_union_idx_mapping() calls have completed. It is safe to call concurrently with output_whether_should_log(), meaning dynamic config of verbosities is allowed. See formal details in thread safety notes in class Config doc header.

Parameters
most_verbose_sev_defaultThe most-verbose (numerically highest) Sev sev value such that output_whether_should_log() will return true, when Component component is either null or has no per-component verbosity configured at that time.
resetIf false then per-component verbosities are left unchanged; else they are wiped out, meaning only the catch-all setting has subsequent effect in output_whether_should_log().

◆ init_component_names()

template<typename Component_payload >
void flow::log::Config::init_component_names ( const boost::unordered_multimap< Component_payload, std::string > &  component_names,
bool  output_components_numerically = false,
util::String_view  payload_type_prefix_or_empty = util::String_view() 
)

Registers the string names of each member of the enum class Component_payload earlier registered via init_component_to_union_idx_mapping<Component_payload>().

These are used subsequently (as of this writing) to (1) map name to index in one of the configure_component_verbosity*() methods; and (2) to map index to name in output_component_to_ostream().

Behavior undefined if init_component_to_union_idx_mapping<Component_payload>() hasn't yet been called. Behavior undefined if init_component_names<Component_payload>() has already been called. (Informally, something safe might happen, depending, but in general it's a weird/bad idea, so don't.) Behavior undefined if any value in component_names is empty.

The recommended (but not mandatory) way to auto-generate a component_names map (as normally doing so by hand is tedious) is to use config_enum_{start|end}_macros.[hc]pp. As an example, Flow itself does it in common.hpp and common.cpp, defining both flow::Flow_log_component (the enum) and flow::S_FLOW_LOG_COMPONENT_NAME_MAP (the component_names map). Basically, via macro magic it names each component according to the enum member identifier's own name.

component_names meaning

Some subtleties exist in interpreting component_names.

Firstly, each value (string name) in component_names – as well as payload_type_prefix_or_empty – is internally pre-normalized before any other work. Name normalization consists of conversion to upper case according to the classic ("C") locale. output_component_to_ostream() will print in normalized form (if applicable); and configure_component_verbosity_by_name() will normalize the input arg string before lookup.

If empty, payload_type_prefix_or_empty has no effect. Otherwise, its effect is as if it were empty, but as if component_names[X] had payload_type_prefix_or_empty prepended to its actual value at all times. Less formally, it's a constant to prefix every name; then if the program (perhaps around main()) simply manually provides a distinct, cosmetically useful "namespace-like" prefix in each init_component_names() call, then it can 100% guarantee no name clashes, even if accidentally one of module X's component names A happened to equal an unrelated module Y's component name B. For example, A = B = "UTIL" is fairly likely to collide otherwise. It won't be an issue, if they end up being called "X_UTIL" and "Y_UTIL" ultimately, by supplying prefixes "X_" and "Y_" X and Y's init_component_names() calls.

Within component_names if a value (name) is present 2+ times, behavior is undefined. Furthermore if payload_type_prefix_or_empty + X, where X is in component_names, is already stored in *this, behavior is undefined. Either way it's a name collision which should be entirely avoidable using payload_type_prefix_or_empty as shown above.

It is a multi-map, and key K is allowed to be present 2+ times mapping to 2+ distinct names. The reason this is supported is so one can (discouraged though it is – but for historical reasons tends to come up at times) declare an enum that includes a few mutually "aliased" members:

  • Sweet_components::S_COOL_ENGINE <=> "COOL_ENGINE" <=> 5
  • Sweet_components::S_ENGINE_ALIAS1 <=> "ENGINE_ALIAS1" <=> 5
  • Sweet_components::S_ENGINE_ALIAS2 <=> "ENGINE_ALIAS2" <=> 5 In that example, any of S_{COOL_ENGINE|ENGINE_ALIAS{1|2}} maps to the rather long name "COOL_ENGINE,ENGINE_ALIAS1,ENGINE_ALIAS2"; and each of "COOL_ENGINE", "ENGINE_ALIAS1", "ENGINE_ALIAS2" maps backwards to a single entry in the component-to-verbosity table. Hence if I configure verbosity X (using configure_component_verbosity*()) for COOL_ENGINE_ALIAS1, then verbosity X config will equally affect subsequent messages with specified component COOL_ENGINE and ENGINE_ALIAS2 as well.

Detail: When concatenating component output names as just described, the prefix payload_type_prefix_or_empty is prepended only once. So, if the prefix is "SWEET-", then any one of the above 3 enum example members maps to the name "SWEET-COOL_ENGINE,ENGINE_ALIAS1,ENGINE_ALIAS2".

output_components_numerically, if and only if set to true, suppresses the default behavior which is to memorize the string to output (in output_component_to_ostream()) for a given enum value; instead it doesn't memorize this forward mapping. As a result, output_component_to_ostream() will simply output the numerical value of the enum member from the flat union component table. This is a cosmetic output choice some prefer to the long-looking component names.

In addition, even if !output_components_numerically, but a subsequent output_component_to_ostream() call encounters an enum value that you neglected to register via init_component_names() (omitting it in component_names in particular), then it will also be printed numerically as if output_components_numerically.

Finally, even if output_components_numerically == true, the backwards mapping (from string name to component) is still memorized. Therefore one can still set configure_component_verbosity_by_name() by string name. Again, in practice, I have seen this: Config files will refer to component verbosities by component name, not unhelpful-looking number; but output log files still print them as numbers for brevity.

Parameters
component_namesMapping of each possible Component_payload value to its string representation, for both output and per-component config (namely verbosity config) subsequently. Details above. Empty names lead to undefined behavior.
output_components_numericallyIf and only if true, output_component_to_ostream() will output the flat numeric index for all Component_payload-passing log call sites; else it will print the string name from the map (but if not in the map, then it'll fall back to the flat index again).
payload_type_prefix_or_emptyOptional prefix helpful as a disambiguating "namespace" to preprend to values in component_names. Details above.

◆ init_component_to_union_idx_mapping()

template<typename Component_payload >
void flow::log::Config::init_component_to_union_idx_mapping ( component_union_idx_t  enum_to_num_offset,
size_t  enum_sparse_length 
)

Registers a generically-typed enum class that represents the full set of the calling module's possible component values that it will supply at subsequent log call sites from that module.

The caller supplies:

  • The template argument Component_payload, which is an enum and is thus castable to the unsigned integer type component_union_idx_t.
  • The signed integer that shall be added to any log-call-site-supplied enum value in order to yield the flat-union index in *this merged table of all component enums. For example, if we assume that no module will ever exceed 1,000 components in its enum, then module 1 can register its enum C1 with enum_to_num_offset 1,000, module 2 with 2,000, module 3 with 3,000, etc. Then the various C1 enum values 0, 1, ... will map to merged 1,000, 1,001, ...; C2's 0, 1, ... to 2,000, 2,001, ...; etc.
    • This can be negative, because why not? Be careful.
  • The "sparse size" of the enum. Details are below.
Note
If this is not acceptable – maybe you want to pack them more tightly, or you have some other clever mapping in mind – then Config might require a new feature (likely an overload of this method) which lets one simply provide the mapping in function (callback) form. In fact, in a previous version of Flow, this was provided; and in fact the present overload merely wrapped that more-general overload. We removed this primarily for perf reasons: Always using this numeric-offset technique allowed for an inlined implementation in the very-frequently-called (at every log call site) output_whether_should_log(). It would be possible to add it back in while also optimizing for the expected-to-be-used-typically offset technique, thus having essentially the best of both worlds (perf when possible, flexibility when necessary). However, since the "flexible" API appears unlikely to be needed, we decided it's over-engineering to keep it in – unless the need does appear in the future. In that case it should be possible to look in source control history and bring back its core elements (without removing the inlined offset-technique code path). At this stage this is not a formal to-do – more of a note for posterity.

Behavior is undefined if an index collision occurs here or in a subsequent init_*() or other relevant call. In particular take care to provide sufficient slack space (e.g., if you use enum_to_num_offset which are multiples of 5, then a collision will probably occur at some point).

If one has called init_component_to_union_idx_mapping<T>() with the same T in the past, then behavior is undefined, so don't. (Informally, depending on whether/how one has called init_component_names() and configure_component_verbosity*(), this can actually be done safely and with well-defined results. However, I did not want to go down that rabbit hole. If it becomes practically necessary, which I doubt, we can revisit. This is not a formal to-do as of this writing.)

See also
Component::payload_type() doc header.
Template Parameters
Component_payloadSee the doc header for the template param Payload on Component::payload(). In addition, in our context, it must be convertible to component_union_idx_t (an unsigned integer). Informally, Component_payload must be a sane unsigned enum with end sentinel S_END_SENTINEL. The various input Component_payload types are distinguished via typeid(Component_payload) and further type_index(typeid(Component_payload)). I provide this implementation detail purely for general context; it should not be seen as relevant to how one uses the API.
Parameters
enum_to_num_offsetFor each further-referenced Component_payload value C, its flat union index shall be component_union_idx_t(C) + enum_to_num_offset. So this is the "base" index for the enum you are registering, in the final flat table of components.
enum_sparse_lengthFormally, one plus the highest numeric value of a Component_payload value that will ever be passed to configure_component_verbosity() (directly; or indirectly if using configure_component_verbosity_by_name()). Informally, we recommend that you (a) use the config_enum_start_hdr.macros.hpp mechanism to create Component_payload type in the first place; and (b) therefore use standard_component_payload_enum_sparse_length<Component_payload>() for the present arg's value.

◆ operator=() [1/2]

void flow::log::Config::operator= ( Config &&  )
delete

For now at least there's no reason for move assignment.

Todo:
Reconsider providing a Config move assignment. I just didn't need to deal with it.

◆ operator=() [2/2]

void flow::log::Config::operator= ( const Config )
delete

For now at least there's no reason for copy assignment.

Todo:
Reconsider providing a Config copy assignment. I just didn't need to deal with it.

◆ output_component_to_ostream()

bool flow::log::Config::output_component_to_ostream ( std::ostream *  os,
const Component component 
) const

An output of Config, this writes a string representation of the given component value to the given ostream, if possible.

Returns true if it wrote anything, false otherwise. Call this only after the full round of construction, init_component_to_union_idx_mapping(), and init_component_names().

If the component's type (component.payload_type()) has not been properly registered via init_component_to_union_idx_mapping(), it returns false and writes nothing. Otherwise, if no name was registered (either because it wasn't included in a init_component_names() call, or because in that call output_components_numerically == true), it will output the component's numeric index in the flat union table; and return true. Finally, if a name is indeed registered, it will output that string (details in init_component_names() doc header) and also return true.

Parameters
osPointer (not null) to the ostream to which to possibly write.
componentThe component value from the log call site. component.empty() (no component) is allowed.
Returns
true if 1 or more characters have been written to *os; else false.

◆ output_whether_should_log()

bool flow::log::Config::output_whether_should_log ( Sev  sev,
const Component component 
) const

A key output of Config, this computes the verbosity-filtering answer to Logger::should_log() based on the given log-call-site severity and component and the verbosity configuration in this Config, including the value at *(this_thread_verbosity_override()), the value from configure_default_verbosity(), and the config from configure_component_verbosity*().

Call this only after the full round of construction, init_component_to_union_idx_mapping(), and (initial) configure_..._verbosity(). In addition, it is specifically safe to concurrently set verbosity via configure_default_verbosity() and/or configure_component_verbosity*().

Thread safety on *this

The last sentence means it's possible to change verbosities even while logging (which invokes us), as long as the init_*() stuff has all been completed. For formal details see notes on thread safety in class Config doc header.

Algorithm for computing return value

It's a matter of comparing sev to S, where S is the applicable log::Sev verbosity setting. Return true if and only if sev <= S.

What is S? It is the first available value of the following three bits of config:

  1. S = *(this_thread_verbosity_override())...
  2. S = the verbosity configured via configure_component_verbosity*() for component.
    • ...unless no such per-component verbosity was set; then:
  3. S = the value given to configure_component_verbosity() or ctor, whichever happened later.
    • This is always available.
See also
this_thread_verbosity_override() and this_thread_verbosity_override_auto().
configure_default_verbosity() and Config().
configure_component_verbosity() and configure_component_verbosity_by_name().
Parameters
sevSee Logger::should_log().
componentSee Logger::should_log().
Returns
true if we recommend to let the associated message be logged; false to suppress it.

◆ standard_component_payload_enum_sparse_length()

template<typename Component_payload >
size_t flow::log::Config::standard_component_payload_enum_sparse_length
static

Returns highest numeric value in the given component-payload enum, plus 1, assuming that enum was created using the config_enum_start_hdr.macros.hpp mechanism with all requirements followed by user.

This is useful for most invocations of init_component_to_union_idx_mapping() for its enum_sparse_length argument.

For example, if one wants to store a vector that uses size_t(X), where X is a Component_payload, as an associative-key-like index, then the vector would have to be sized whatever the present method returns to guarantee no out-of-bounds error.

See also
init_component_to_union_idx_mapping(), particularly the enum_sparse_length argument.
Template Parameters
Component_payloadAn enum class type created by the mechanism prescribed in config_enum_start_hdr.macros.hpp, using that mechanism and with user following the documented requirements therein. Alternatively (though it's not recommended) the following is sufficient if one makes the type some other way: Component_payload::S_END_SENTINEL must have the highest numeric value, without ties; and the backing type is unsigned and with a bit width no higher than that of size_t (Component::enum_raw_t is what the aforementioned standard mechanism uses as of this writing).
Returns
See above.

◆ this_thread_verbosity_override()

Sev * flow::log::Config::this_thread_verbosity_override ( )
static

Returns pointer to this thread's mutable verbosity override, for querying or assignment alike.

The value of this override, at any given time, shall affect output_whether_should_log() return value. See output_whether_should_log().

If you would like to query the current setting, use this method.

If you would like to modify the current setting, it is safer and easier to use this_thread_verbosity_override_auto() which supplies RAII-style auto-restore.

For each thread:

Returns
See above. Note that, for any given thread, the returned pointer shall always be the same.

◆ this_thread_verbosity_override_auto()

util::Scoped_setter< Sev > flow::log::Config::this_thread_verbosity_override_auto ( Sev  most_verbose_sev_or_none)
static

Sets *(this_thread_verbosity_override()) = most_verbose_sev_or_none; and returns an object that shall restore it to its current value when it goes out of scope.

See class doc header for an example of use.

It is recommended to use this method instead of direct assignment to the location this_thread_verbosity_override(), as then it'll be auto-restored.

See also
this_thread_verbosity_override() and output_whether_should_log().
Parameters
most_verbose_sev_or_noneA value suitable for configure_default_verbosity(); or the special value Sev::S_END_SENTINEL which disables the override (meaning C.output_whether_should_log() shall actually follow the config in Config C and not any override).
Returns
Object that, when it is destroyed, will restore the verbosity override to what it was before this call. (The object cannot be copied, to prevent a double-restore. It can however be moved-from.)

Member Data Documentation

◆ m_use_human_friendly_time_stamps

bool flow::log::Config::m_use_human_friendly_time_stamps

Config setting: If true, time stamps will include a (deterministically formatted) date, time, time zone, all in the OS's current time zone; else raw # of seconds passed since POSIX (Unix) Epoch (1970, Jan 1, 00:00, GMT).

In both cases time is expressed with microsecond resolution (but the accuracy is only as good as the computer's clock hardware and OS software allow, presumably, though this isn't in the purview of class Config).


The documentation for this class was generated from the following files: