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

An implementation of Logger that logs messages to the given ostreams (e.g., cout or an ofstream for a file). More...

#include <simple_ostream_logger.hpp>

Inheritance diagram for flow::log::Simple_ostream_logger:
[legend]
Collaboration diagram for flow::log::Simple_ostream_logger:
[legend]

Public Member Functions

 Simple_ostream_logger (Config *config, std::ostream &os=std::cout, std::ostream &os_for_err=std::cerr)
 Constructs logger to subsequently log to the given standard ostream (or 2 thereof). More...
 
bool should_log (Sev sev, const Component &component) const override
 Implements interface method by returning true if the severity and component (which is allowed to be null) indicate it should. More...
 
bool logs_asynchronously () const override
 Implements interface method by returning false, indicating that this Logger will not need the contents of *metadata and msg passed to do_log(), as the latter will output them synchronously. More...
 
void do_log (Msg_metadata *metadata, util::String_view msg) override
 Implements interface method by synchronously logging the message and some subset of the metadata in a fashion controlled by m_config. More...
 
- Public Member Functions inherited from flow::log::Logger
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 be logged and false otherwise (e.g., if the verbosity of the message is above the current configured verbosity threshold for the Component specified). More...
 
virtual bool logs_asynchronously () const =0
 Must return true if do_log() at least sometimes logs the given message and metadata (e.g., time stamp) after do_log() returns; false if this never occurs (i.e., it logs synchronously, always). More...
 
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 should be logged (i.e., without performing logic that should_log() performs). More...
 
std::ostream * this_thread_ostream () const
 Returns the stream dedicated to the executing thread and this Logger, so that the caller can apply state-setting formatters to it. More...
 
- Public Member Functions inherited from flow::util::Null_interface
virtual ~Null_interface ()=0
 Boring virtual destructor. More...
 
- Public Member Functions inherited from flow::util::Unique_id_holder
 Unique_id_holder ()
 Thread-safely construct an ID whose value is different from any other object of this class, past or future.
 
 Unique_id_holder (const Unique_id_holder &)
 This copy constructor is identical in behavior to Unique_id_holder(), the default ctor. More...
 
id_t unique_id () const
 Raw unique ID identifying this object as well as any object of a derived type. More...
 
const Unique_id_holderoperator= (const Unique_id_holder &) const
 This assignment operator is a const no-op. More...
 

Public Attributes

Config *const m_config
 Reference to the config object passed to constructor. Note that object is mutable; see notes on thread safety.
 

Additional Inherited Members

- Public Types inherited from flow::util::Unique_id_holder
using id_t = uint64_t
 Raw integer type to uniquely identify a thing. 64-bit width should make overflow extremely hard to reach.
 
- Static Public Member Functions inherited from flow::log::Logger
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 (such as visible in top output). More...
 
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 thread's logging-worthy string name to be output to that stream. More...
 
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, based on whether/how this_thread_set_logged_nickname() was last called in the current thread. More...
 
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_metadata. More...
 
- Static Public Member Functions inherited from flow::util::Unique_id_holder
static id_t create_unique_id ()
 Short-hand for Unique_id_holder().unique_id(); useful when all you want is the unique integer itself. More...
 

Detailed Description

An implementation of Logger that logs messages to the given ostreams (e.g., cout or an ofstream for a file).

Protects against garbling due to simultaneous logging from multiple threads.

Vaguely speaking, this Logger is suitable for console (cout, cerr) output; and, in a pinch outside of a heavy-duty production/server environment, for file (ofstream) output. For heavy-duty file logging one should use Async_file_logger. The primary reason is performance; this is discussed in the Logger class doc header. A secondary reason is additional file-logging-specific utilities – such as rotation – are now or in the future going to be in Async_file_logger, as its purpose is heavy-duty file logging specifically.

Thread safety

As noted above, simultaneous logging from multiple threads is safe from output corruption, in that simultaneous do_log() calls for the same Logger targeting the same stream will log serially to each other.

Additionally, changes to the Config (which controls should_log() behavior among other things), pointer to which is passed to constructor, are thread-safe against should_log() and do_log() if and only if class Config doc header describes them as such. Short version: You can't modify the Config anymore (while should_log() and do_log()) except you can dynamically modify verbosity via Config::configure_default_verbosity() and Config::configure_component_verbosity() or Config::configure_component_verbosity_by_name().

There are no other mutable data (state), so that's that.

Thread safety: Corner case: Don't cross the ostreams!

There is another, arguably subtle, thread safety issue. Bottom line/summary: If you pass ostream S (including cout and cerr) into object Simple_ostream_logger S1 constructor, then don't access S from any other code, including outside code printing to S, AND including indirect access via any other Simple_ostream_logger S2 that isn't S1 – at least not when S1 might be logging.

Now in more detail: Suppose S is an underlying ostream; for example S could be cerr. Suppose Simple_ostream_logger S1 is constructed with S, meaning it will always print to cerr. Finally, suppose some other unrelated code also accesses the stream S while a log statement is printing to S through S1; for example that code might be writing some raw printouts to standard error by means of cerr <<. Hence, S is being write-accessed from 2 threads. At best, this can cause garbled output. (This is unavoidable but could be seen as acceptable functionally.) However the danger doesn't end there; internally S1 code will use stateful formatters to affect the output. Write ops via ostream formatters are not thread-safe against use of formatters on the same ostream from another thread. This can cause very-hard-to-identify crashes, such as double-frees reported from deep inside ostream-related library code. Garbled output may be okay in a pinch, but crashes and other concurrency-corruption issues are never okay!

One's reaction to that might be "of course!" – it's simply messing with an ostream from 2+ different threads simultaneously: it should be expected to break. However, an essentially identical problem – which one might assume wouldn't exist (but one would be wrong) – is the following. Suppose everything is as described in the previous paragraph, except that the "outside code accessing stream from another thread simultaneously" isn't unrelated but rather simply another Simple_ostream_logger S2 that happened to be constructed against ostream S, same as S1 was. This, too, can break dangerously in the same ways and for the same reason. That is, Simple_ostream_logger takes no special measures to prevent this; all built-in thread-safety measures apply to multi-threaded uses of each given *this – not across different *thises.

Constructor & Destructor Documentation

◆ Simple_ostream_logger()

flow::log::Simple_ostream_logger::Simple_ostream_logger ( Config config,
std::ostream &  os = std::cout,
std::ostream &  os_for_err = std::cerr 
)
explicit

Constructs logger to subsequently log to the given standard ostream (or 2 thereof).

Warning
It is strongly recommended that numeric format state (for integers) in os and os_for_err be at default values (e.g, no std::hex or non-default fill character) at entry to this constructor. Otherwise the contents of prefixes within log output will be formatted in some undefined way.

Note that usually one SHOULD avoid writing anything to os or os_for_err once they've been passed to Simple_ostream_logger. Logs should not (usually) be mixed with other output, as (1) it would look odd; and (2) interleaving may occur, since there is no mutex making logging mutually exclusive against other output to the same stream(s). Update: It might be worse that that; see thread safety notes in Simple_ostream_logger doc header. Reiterating: Really one and only one Simple_ostream_logger should access a given ostream, and no other code should.

The case &os == &os_for_err (in other words they are the same stream) is not only allowed but likely common, so that post-factum redirect of stderr into stdout (often done via 2>&1 on command line) becomes unnecessary.

Parameters
configControls behavior of this Logger. In particular, it affects should_log() logic (verbosity default and per-component) and output format (such as time stamp format). See thread safety notes in class doc header. This is saved in m_config.
osostream to which to log messages of severity strictly less severe than Sev::S_WARNING.
os_for_errostream to which to log messages of severity Sev::S_WARNING or more severe.

Member Function Documentation

◆ do_log()

void flow::log::Simple_ostream_logger::do_log ( Msg_metadata metadata,
util::String_view  msg 
)
overridevirtual

Implements interface method by synchronously logging the message and some subset of the metadata in a fashion controlled by m_config.

Parameters
metadataAll information to potentially log in addition to msg.
msgThe message.

Implements flow::log::Logger.

◆ logs_asynchronously()

bool flow::log::Simple_ostream_logger::logs_asynchronously ( ) const
overridevirtual

Implements interface method by returning false, indicating that this Logger will not need the contents of *metadata and msg passed to do_log(), as the latter will output them synchronously.

Returns
See above.

Implements flow::log::Logger.

◆ should_log()

bool flow::log::Simple_ostream_logger::should_log ( Sev  sev,
const Component component 
) const
overridevirtual

Implements interface method by returning true if the severity and component (which is allowed to be null) indicate it should.

As of this writing not thread-safe against changes to *m_config.

Parameters
sevSeverity of the message.
componentComponent of the message. Reminder: component.empty() == true is allowed.
Returns
See above.

Implements flow::log::Logger.


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