ROSE
0.11.96.11
|
Formatted diagnostic messages emitted to various backends.
This namespace provides functionality to conditionally emit diagnostic messages based on software component and message importance. Although the library has extensive capabilities for controlling the message format and where to send the message, most uses of this namespace are to emit a message, so we'll describe that by way of introduction...
The mlog
is a message Facility that holds a number of streams, each of which can be enabled or disabled. Inserting to a disabled message stream causes the message to be thrown away. The INFO
is a message Importance, of which the library defines eight levels ranging from debug through fatal. The expression mlog[INFO]
selects one of the message Stream objects from the facility. The linefeed marks the end of a message; messages are "partial" until the linefeed is inserted, but depending on the sink (final destination) even partial messages might be shown. Since a message stream is a subclass of std::ostream, all the usual output insertion operators just work.
One of the benefits of using this namespace is that the output will have a consistent look and feel. It can even be colorized based on message importance (at least for ANSI terminal output). The output will look something like this:
The "identityTranslator" is the name of the executable, the 31044 is the process ID, "0.00128" is the time in seconds since the program started, "disassembler" is the software component that is reporting this messsage (i.e., the messsage facility), and "INFO" is the importance level.
Before one can start emitting messages, he needs to define a message Facility (mlog
in the first example). The library defines one facility, Sawyer::Message::mlog, which can be used, but it's better to define a message facility for each software component because then messages can be controlled per software component, and the component will be clearly indicated in the output ("disassembler" in the previous example). A software component could be a function, class, namespace, entire library, or entire program–whatever granularity one wants.
Here's an example that creates a facility at class level:
The mlog
is constructed in a useable state with all streams enabled and output going to standard error via file descriptor 2. It is given a string name, "disassembler", that will show up in the output. The class may want to make two adjustments at run-time:
The initStreams
call reinitializes the message streams so they use your library's message plumbing, destination
, which is a Sawyer::Message::DestinationPtr (types whose names end with "Ptr" point to reference counted objects). The streams had previously been using Sawyer::Message::merr, but pointing them all to your destination means that the destination for all messages can be changed in one place: if you want messages to go to syslogd, you change how destination
is created. As we'll see below, the destination also controls things like colored output.
The mfacilities.insert
call registers the new mlog
with a global Facilities object, which we describe next...
Any combination of Facility objects can be grouped together into one or more Facilities (note plural) objects so they can be enabled and disabled collectively. A Facilities object, also referred to as a facility group, is able to parse a simple language provided as a string, which is often provided from the command-line parser.
This example creates three facilities and groups them together:
The three insert
calls incorporate the three facilities into the group by reference and give them names in the configuration language. The names in the config language must look like C++ names (including ::
and .
operators), so the second insert
needs to supply a valid name. The other two use the facility's name, which is also the string that is printed when messages are output. A unique, non-empty name must be supplied for each facility in a group, although the names for the facilities themselves need not be unique or even non-empty. The library provides Sawyer::Message::mfacilities and users are encouraged to use it as their primary facility group.
The facilities in a group can be controlled (enabled or disabled) with the Facilities::control method, which takes a string in the control language (see method for details). For example, to enable output for all levels of importance except debugging, across all registered facilities, but only warning and higher messages for the disassembler's facility:
The terms are processed from left to right, but only if the entire string is valid. If the string is invalid then an exception is thrown that includes an error message and the exact position in the string where the error occurred.
Inserting a message to a stream with <<
operators is only half the story–the plumbing lattice deals with how a message is conveyed to the final destination(s) and what transformations happen along the way. The plumbing is a run-time constructed lattice containing internal nodes and leaf nodes. Each leaf node is a final destination, or sink, for a message and can be a C++ std::ostream
, a C FILE
pointer, a Unix file descriptor, the syslog daemon, or a user-defined destination. The internal nodes serve as multiplexers, filters, rate limiters, etc.
All plumbing lattice nodes are dynamically allocated and reference counted. Instead of using the normal C++ constructors, plumbing objects are created with static instance
methods that perform the allocation and return a smart pointer. The type names for smart pointers are the class names with a "Ptr" suffix, and Sawyer::SharedPointer serves as the implementation.
For instance, a lattice that accepts any kind of message, limits the output to at most one message per second, and sends the messages to standard error and a file can be constructed like this:
The destination
can then be used to construct streams and facilities. The Destination class is the base class for all plumbing lattice nodes, thus DestinationPtr is the base class for all pointers to nodes. Of course, similar redirection can be done outside the program using standard Unix tools and file redirection, except that performing redirection via a plumbing lattice has a few advantages:
Avoid using the name "log" since it may conflict with ::log
from math.h.
If facilities are consistently named (e.g., always "mlog") then code that emits messages can always use that name and will automatically get the most narrowly scoped facility that's visible according to C++ visibility rules, which is probably the facility that is most applicable.
A using namespace Sawyer::Message
will prevent you from having to qualify the message importance symbols, but if you name your facilities "mlog" they may be ambiguous with Sawyer::Message::mlog. Sawyer also provides the namespace Sawyer::Message::Common, which defines only the most commonly used types (the importance levels, Stream, Facility, and Facilities).
Although every attempt was made to keep the library efficient, when features and performance conflict, features win. If messages insert operators <<
are expensive then it is best to avoid calling them when the stream to which they're inserted is disabled–they'd just be thrown away anyway. Programs that already emit messages to streams probably use if
statements or conditional compilation already, and those constructs can continue to be used. In fact, when a stream is evaluated in a Boolean context it returns true when enabled and false when disabled:
When if
statements are used extensively for this purpose in a project whose style guide mandates that the body must be on the following line, the logging code occupies twice as much vertical space as necessary (or three or four times, with curly braces) and can become quite obtrusive. Here are two other ways to get the same effect:
The alternate spelling "SAWYER_MSG" is also allowed.
Although std::ostream
objects are not copyable, and not movable before c++11, Sawyer's message streams implement a form of copying/moving. Namely, a Stream copy constructor can be used to create a new message stream identical to the source message stream, and any partial message in the source stream is moved to the destination stream. This enables a way to create a temporary stream for use inside a function:
One problem that authors often encounter when emitting diagnostics is that they want to print an incomplete line, then perform some work, then complete the line. But if the "some work" also prints diagnostics then the output will be all messed up. This library attempts to fix that, at least when the software uses this library for all its diagnostic output. Here's the most basic form:
The output to sinks like standard error that support partial messages will be, sans prefixes:
But there's a slight problem with that code: if the second line had been a message written to mlog[DEBUG]
instead of mlog[INFO]
then the library would not have been able to tell that "some other message" is unrelated to "first part" and would have emitted something that's not any better than using plain old std::cerr
:
This most often occurs when "some other message" is emitted from some other function, so its not immediately evident that something is inserting a message in the midst of our partial message. A better way is to create a temporary message stream and use that stream to complete the partial message. Since the copy constructor also moves any partial message from the source stream to the new stream, we can even emit the partial message on the same line as the declaration. Here's a silly example of the process:
The mesg
stream need not be used only for completing the one message. In fact, one of the benefits of the Temporary streams hint is that partial messages emitted using the debug
stream cannot be interferred with from called functions that might use mlog[DEBUG]
.
Namespaces | |
Common | |
Commonly used message types. | |
Classes | |
class | ColorSet |
Colors to use for each message importance. More... | |
struct | ColorSpec |
ANSI Color specification for text written to a terminal. More... | |
class | Destination |
Base class for all types of message destinations. More... | |
class | Facilities |
Collection of facilities. More... | |
class | FacilitiesGuard |
Saves and restores facilities. More... | |
class | Facility |
Collection of streams. More... | |
class | FdSink |
Send free-format messages to a Unix file descriptor. More... | |
class | FileSink |
Send free-format messages to a C FILE pointer. More... | |
class | Filter |
Base class for internal nodes that filter messages. More... | |
class | Gang |
class | HighWater |
class | ImportanceFilter |
Filters messages based on importance level. More... | |
class | Mesg |
A single message. More... | |
struct | MesgProps |
Properties for messages. More... | |
class | Multiplexer |
Sends incoming messages to multiple destinations. More... | |
class | Prefix |
Information printed at the beginning of each free-format message. More... | |
class | SequenceFilter |
Filters messages based on how many messages have been seen. More... | |
class | SProxy |
class | Stream |
Converts text to messages. More... | |
class | StreamSink |
Send free-format messages to a C++ I/O stream. More... | |
class | SyslogSink |
Sends messages to the syslog daemon. More... | |
class | TimeFilter |
Filters messages based on time. More... | |
class | UnformattedSink |
Base class for final destinations that are free-format. More... | |
Typedefs | |
typedef std::pair< DestinationPtr, MesgProps > | BakedDestination |
Baked properties for a destination. More... | |
typedef std::vector< BakedDestination > | BakedDestinations |
Baked properties for multiple destinations. | |
Enumerations | |
enum | Importance { DEBUG, TRACE, WHERE, MARCH, INFO, WARN, ERROR, FATAL, N_IMPORTANCE } |
Level of importance for a message. More... | |
enum | AnsiColor { COLOR_BLACK = 0, COLOR_RED = 1, COLOR_GREEN = 2, COLOR_YELLOW = 3, COLOR_BLUE = 4, COLOR_MAGENTA = 5, COLOR_CYAN = 6, COLOR_WHITE = 7, COLOR_DEFAULT = 8 } |
Colors used by sinks that write to terminals. More... | |
Functions | |
bool | initializeLibrary () |
Explicitly initialize the library. More... | |
std::string | stringifyImportance (Importance) |
Convert an Importance enum to a string. | |
std::string | stringifyColor (AnsiColor) |
Convert an AnsiColor enum to a string. | |
double | now () |
Current system time in seconds. | |
std::string | escape (const std::string &) |
Convert a string to its C representation. | |
bool | isTerminal (int fd) |
True if fd is a tty. | |
std::ostream & | operator<< (std::ostream &o, const MesgProps &props) |
Print the values for all message properties. More... | |
Stream & | firstEnabled (Stream &s1) |
Stream & | firstEnabled (Stream &s1, Stream &s2) |
Stream & | firstEnabled (Stream &s1, Stream &s2, Stream &s3) |
Stream & | firstEnabled (Stream &s1, Stream &s2, Stream &s3, Stream &s4) |
Stream & | firstEnabled (Stream &s1, Stream &s2, Stream &s3, Stream &s4, Stream &s5) |
void | shutdown () |
Reset global variables to initial states. More... | |
Variables | |
DestinationPtr | merr |
Library-provided message destination. More... | |
Facility | mlog |
Facility used by Sawyer components. More... | |
Facilities | mfacilities |
Library-provided facility group. More... | |
SProxy | assertionStream |
The stream to be used for assertions. More... | |
typedef SharedPointer<class Destination> Sawyer::Message::DestinationPtr |
Smart pointer.
Sawyer uses reference-counting smart pointers for most objects in order to alleviate the user from having to keep track of which objects own which other objects and when it is safe to delete an object. This is especially convenient for the plumbing part of Sawyer where objects are connected to each other in a lattice and may have multiple parents.
Sawyer uses the following conventions for objects that use smart pointers:
instance
class methods instead of constructors to instantiate an instance of such a class. These methods allocate a new object and return a smart pointer to that object. typedef SharedPointer<class Multiplexer> Sawyer::Message::MultiplexerPtr |
Smart pointer.
Sawyer uses reference-counting smart pointers for most objects in order to alleviate the user from having to keep track of which objects own which other objects and when it is safe to delete an object. This is especially convenient for the plumbing part of Sawyer where objects are connected to each other in a lattice and may have multiple parents.
Sawyer uses the following conventions for objects that use smart pointers:
instance
class methods instead of constructors to instantiate an instance of such a class. These methods allocate a new object and return a smart pointer to that object. typedef SharedPointer<class Filter> Sawyer::Message::FilterPtr |
Smart pointer.
Sawyer uses reference-counting smart pointers for most objects in order to alleviate the user from having to keep track of which objects own which other objects and when it is safe to delete an object. This is especially convenient for the plumbing part of Sawyer where objects are connected to each other in a lattice and may have multiple parents.
Sawyer uses the following conventions for objects that use smart pointers:
instance
class methods instead of constructors to instantiate an instance of such a class. These methods allocate a new object and return a smart pointer to that object. typedef SharedPointer<class SequenceFilter> Sawyer::Message::SequenceFilterPtr |
Smart pointer.
Sawyer uses reference-counting smart pointers for most objects in order to alleviate the user from having to keep track of which objects own which other objects and when it is safe to delete an object. This is especially convenient for the plumbing part of Sawyer where objects are connected to each other in a lattice and may have multiple parents.
Sawyer uses the following conventions for objects that use smart pointers:
instance
class methods instead of constructors to instantiate an instance of such a class. These methods allocate a new object and return a smart pointer to that object. typedef SharedPointer<class TimeFilter> Sawyer::Message::TimeFilterPtr |
Smart pointer.
Sawyer uses reference-counting smart pointers for most objects in order to alleviate the user from having to keep track of which objects own which other objects and when it is safe to delete an object. This is especially convenient for the plumbing part of Sawyer where objects are connected to each other in a lattice and may have multiple parents.
Sawyer uses the following conventions for objects that use smart pointers:
instance
class methods instead of constructors to instantiate an instance of such a class. These methods allocate a new object and return a smart pointer to that object. typedef SharedPointer<class ImportanceFilter> Sawyer::Message::ImportanceFilterPtr |
Smart pointer.
Sawyer uses reference-counting smart pointers for most objects in order to alleviate the user from having to keep track of which objects own which other objects and when it is safe to delete an object. This is especially convenient for the plumbing part of Sawyer where objects are connected to each other in a lattice and may have multiple parents.
Sawyer uses the following conventions for objects that use smart pointers:
instance
class methods instead of constructors to instantiate an instance of such a class. These methods allocate a new object and return a smart pointer to that object. typedef SharedPointer<class Gang> Sawyer::Message::GangPtr |
Smart pointer.
Sawyer uses reference-counting smart pointers for most objects in order to alleviate the user from having to keep track of which objects own which other objects and when it is safe to delete an object. This is especially convenient for the plumbing part of Sawyer where objects are connected to each other in a lattice and may have multiple parents.
Sawyer uses the following conventions for objects that use smart pointers:
instance
class methods instead of constructors to instantiate an instance of such a class. These methods allocate a new object and return a smart pointer to that object. typedef SharedPointer<class Prefix> Sawyer::Message::PrefixPtr |
Smart pointer.
Sawyer uses reference-counting smart pointers for most objects in order to alleviate the user from having to keep track of which objects own which other objects and when it is safe to delete an object. This is especially convenient for the plumbing part of Sawyer where objects are connected to each other in a lattice and may have multiple parents.
Sawyer uses the following conventions for objects that use smart pointers:
instance
class methods instead of constructors to instantiate an instance of such a class. These methods allocate a new object and return a smart pointer to that object. typedef SharedPointer<class UnformattedSink> Sawyer::Message::UnformattedSinkPtr |
Smart pointer.
Sawyer uses reference-counting smart pointers for most objects in order to alleviate the user from having to keep track of which objects own which other objects and when it is safe to delete an object. This is especially convenient for the plumbing part of Sawyer where objects are connected to each other in a lattice and may have multiple parents.
Sawyer uses the following conventions for objects that use smart pointers:
instance
class methods instead of constructors to instantiate an instance of such a class. These methods allocate a new object and return a smart pointer to that object. typedef SharedPointer<class FdSink> Sawyer::Message::FdSinkPtr |
Smart pointer.
Sawyer uses reference-counting smart pointers for most objects in order to alleviate the user from having to keep track of which objects own which other objects and when it is safe to delete an object. This is especially convenient for the plumbing part of Sawyer where objects are connected to each other in a lattice and may have multiple parents.
Sawyer uses the following conventions for objects that use smart pointers:
instance
class methods instead of constructors to instantiate an instance of such a class. These methods allocate a new object and return a smart pointer to that object. typedef SharedPointer<class FileSink> Sawyer::Message::FileSinkPtr |
Smart pointer.
Sawyer uses reference-counting smart pointers for most objects in order to alleviate the user from having to keep track of which objects own which other objects and when it is safe to delete an object. This is especially convenient for the plumbing part of Sawyer where objects are connected to each other in a lattice and may have multiple parents.
Sawyer uses the following conventions for objects that use smart pointers:
instance
class methods instead of constructors to instantiate an instance of such a class. These methods allocate a new object and return a smart pointer to that object. typedef SharedPointer<class StreamSink> Sawyer::Message::StreamSinkPtr |
Smart pointer.
Sawyer uses reference-counting smart pointers for most objects in order to alleviate the user from having to keep track of which objects own which other objects and when it is safe to delete an object. This is especially convenient for the plumbing part of Sawyer where objects are connected to each other in a lattice and may have multiple parents.
Sawyer uses the following conventions for objects that use smart pointers:
instance
class methods instead of constructors to instantiate an instance of such a class. These methods allocate a new object and return a smart pointer to that object. typedef SharedPointer<class SyslogSink> Sawyer::Message::SyslogSinkPtr |
Smart pointer.
Sawyer uses reference-counting smart pointers for most objects in order to alleviate the user from having to keep track of which objects own which other objects and when it is safe to delete an object. This is especially convenient for the plumbing part of Sawyer where objects are connected to each other in a lattice and may have multiple parents.
Sawyer uses the following conventions for objects that use smart pointers:
instance
class methods instead of constructors to instantiate an instance of such a class. These methods allocate a new object and return a smart pointer to that object. typedef std::pair<DestinationPtr, MesgProps> Sawyer::Message::BakedDestination |
Baked properties for a destination.
Rather than recompute properties every time characters of a message are inserted into a stream, the library computes the properties just once when the first character of a message is inserted. This process is called "baking the properties" and the result is an std::pair
that contains a destination and its baked properties.
Level of importance for a message.
The library defines only these importance levels and does not provide a mechanism by which additional levels can be added. The reasoning is that multiple message facilities should be used to subdivide universal stream of messages into smaller categories, and that message sinks should be able to expect a well defined set of importance levels.
The message importance symbols are sorted numerically so that more critical levels have a higher numeric value than less critical levels.
Enumerator | |
---|---|
DEBUG | Messages intended to be useful primarily to the author of the code. This level of importance is not often present in publically-released source code and serves mostly as an alternative for authors that like to debug-by-print. |
TRACE | Detailed tracing information useful to end-users that are trying to understand program internals. These messages can also be thought of as debug messages that are useful to end users. Tracing occurs in two levels, where |
WHERE | Granular tracing information useful to end-users that are trying to understand program internals. These can also be thought of as debug messages that are useful to end users. Tracing occurs in two levels, where |
MARCH | Progress reports and other similar rapidly updating partial messages. |
INFO | Informative messages. These messages confer information that might be important but do not indicate situations that are abnormal. |
WARN | Warning messages that indicate an unusual situation from which the program was able to fully recover. |
ERROR | Error messages that indicate an abnormal situation from which the program was able to at least partially recover. |
FATAL | Messages that indicate an abnormal situation from which the program was unable to recover. Producing a message of this type does not in itself terminate the program–it merely indicates that the program is about to terminate. Since one software component's fatal error might be a calling components's recoverable error, exceptions should generally be thrown. About the only time |
N_IMPORTANCE | Number of distinct importance levels. |
Colors used by sinks that write to terminals.
Note that most modern terminal emulators allow the user to specify the actual colors that are displayed for each of these, so the display color might not match the color name.
bool Sawyer::Message::initializeLibrary | ( | ) |
Explicitly initialize the library.
This initializes any global objects provided by the library to users. This happens automatically for many API calls, but sometimes needs to be called explicitly. Calling this after the library has already been initialized does nothing. The function always returns true.
Thread safety: This function is thread-safe.
std::ostream& Sawyer::Message::operator<< | ( | std::ostream & | o, |
const MesgProps & | props | ||
) |
Print the values for all message properties.
void Sawyer::Message::shutdown | ( | ) |
Reset global variables to initial states.
This function resets global variables such as merr, mlog, and mfacilities to their default-constructed state. Sometimes it's necessary to do this during program exit, otherwise the C++ runtime might terminate the Boost thread synchronization library before Sawyer, which leads to exceptions or segmentation faults when Sawyer's stream destructors run. In fact, initializeLibrary arranges for this shutdown function to be called by exit.
DestinationPtr Sawyer::Message::merr |
Library-provided message destination.
This is the top of a lattice that sends all messages to file descriptor 2, which is usually standard error.
Facility Sawyer::Message::mlog |
Facility used by Sawyer components.
This facility is added to the mfacilities group with the name "sawyer".
Referenced by Sawyer::CommandLine::Boost::command_line_parser::command_line_parser().
Facilities Sawyer::Message::mfacilities |
Library-provided facility group.
When the library is initialized, this group contains only mlog with the name "sawyer". Users are free to manipulate this facility however they choose, and if everyone uses this group then all the facilities across all users can be controlled from this one place.
SProxy Sawyer::Message::assertionStream |
The stream to be used for assertions.
The default is to use Sawyer::Message::mlog[FATAL]
. This variable is initialized at the first call to Assert::fail if it is a null pointer. Users can assign a different stream to it any time before then: