Flow 1.0.0
Flow project: Public API.
|
An implementation of Logger that logs messages to the given ostream
s (e.g., cout
or an ofstream
for a file).
More...
#include <simple_ostream_logger.hpp>
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_holder & | operator= (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... | |
An implementation of Logger that logs messages to the given ostream
s (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.
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.
ostream
s!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 *this
es.
|
explicit |
Constructs logger to subsequently log to the given standard ostream
(or 2 thereof).
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.
config | Controls 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. |
os | ostream to which to log messages of severity strictly less severe than Sev::S_WARNING. |
os_for_err | ostream to which to log messages of severity Sev::S_WARNING or more severe. |
|
overridevirtual |
Implements interface method by synchronously logging the message and some subset of the metadata in a fashion controlled by m_config.
metadata | All information to potentially log in addition to msg . |
msg | The message. |
Implements flow::log::Logger.
|
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.
Implements flow::log::Logger.
|
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
.
sev | Severity of the message. |
component | Component of the message. Reminder: component.empty() == true is allowed. |
Implements flow::log::Logger.