CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
2000 lines
93 KiB
C++
2000 lines
93 KiB
C++
// See also rose::Diagnostics in $ROSE/src/roseSupport/Diagnostics.h
|
|
// WARNING: Changes to this file must be contributed back to Sawyer or else they will
|
|
// be clobbered by the next update from Sawyer. The Sawyer repository is at
|
|
// https://github.com/matzke1/sawyer.
|
|
|
|
|
|
|
|
|
|
#ifndef Sawyer_Message_H
|
|
#define Sawyer_Message_H
|
|
|
|
#include "Map.h"
|
|
#include "Optional.h"
|
|
#include "Sawyer.h"
|
|
#include "SharedPointer.h"
|
|
#include "Synchronization.h"
|
|
|
|
#include <stddef.h>
|
|
#include <utility>
|
|
#include <dyncompat/config.hpp>
|
|
#include <dyncompat/logic/tribool.hpp>
|
|
#include <cassert>
|
|
#include <cstring>
|
|
#include <list>
|
|
#include <ostream>
|
|
#include <set>
|
|
#include <streambuf>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace Sawyer {
|
|
|
|
/** 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...
|
|
*
|
|
* @section emitting Emitting a message
|
|
*
|
|
* @code
|
|
* using namespace Sawyer::Message;
|
|
* mlog[INFO] <<"about to process \"" <<filename <<"\"\n"
|
|
* @endcode
|
|
*
|
|
* The @c 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 @c INFO is a message Importance, of which the library
|
|
* defines eight levels ranging from debug through fatal. The expression <code>mlog[INFO]</code> 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:
|
|
*
|
|
* @code
|
|
* identityTranslator[31044] 0.00128 disassembler[INFO]: about to process "grep"
|
|
* @endcode
|
|
*
|
|
* 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.
|
|
*
|
|
* @section facility Defining facilities
|
|
*
|
|
* Before one can start emitting messages, he needs to define a message Facility (@c 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:
|
|
*
|
|
* @code
|
|
* class Disassembler {
|
|
* static Sawyer::Message::Facility mlog;
|
|
* ...
|
|
* };
|
|
*
|
|
* Sawyer::Message::Facility Disassembler::mlog("disassembler");
|
|
* @endcode
|
|
*
|
|
* The @c 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:
|
|
*
|
|
* @code
|
|
* void Disassembler::initClass() {
|
|
* mlog.initStreams(destination);
|
|
* mfacilities.insert(mlog);
|
|
* }
|
|
* @endcode
|
|
*
|
|
* The @c initStreams call reinitializes the message streams so they use your library's message plumbing, @c 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 @c destination is
|
|
* created. As we'll see below, the destination also controls things like colored output.
|
|
*
|
|
* The <code>mfacilities.insert</code> call registers the new @c mlog with a global Facilities object, which we describe
|
|
* next...
|
|
*
|
|
* @section facilities Collections of facility objects
|
|
*
|
|
* 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:
|
|
*
|
|
* @code
|
|
* Facility mlog1("disassembler", destination);
|
|
* Facility mlog2("c++ parser", destination)
|
|
* Facility mlog3("StringUtility::split", destination);
|
|
*
|
|
* Facilities facilities;
|
|
* facilities.insert(mlog1);
|
|
* facilities.insert(mlog2, "cpp_parser");
|
|
* facilities.insert(mlog3);
|
|
* @endcode
|
|
*
|
|
* The three @c 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 <code>::</code> and
|
|
* <code>.</code> operators), so the second @c 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:
|
|
*
|
|
* @code
|
|
* facilities.control("all, !debug, disassembler(>=warn)")
|
|
* @endcode
|
|
*
|
|
* 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.
|
|
*
|
|
* @section plumbing Plumbing lattice
|
|
*
|
|
* Inserting a message to a stream with <code><<</code> 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++ <code>std::ostream</code>, a C @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 @c 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 @ref 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:
|
|
*
|
|
* @code
|
|
* DestinationPtr destination = TimeFilter::instance(1.0)
|
|
* ->to(FdSink::instance(2), //standard error on Unix
|
|
* FileSink::instance(f)); // f is some FILE* we opened already
|
|
* @endcode
|
|
*
|
|
* The @c 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:
|
|
*
|
|
* @li the program itself can control the redirection
|
|
* @li different paths through the lattice can result in different message formats
|
|
* @li final destinations can be programmatic, such as writing to syslogd or a database
|
|
* @li the library might do a better presentation when two streams go to the same destination
|
|
*
|
|
* @section tips Tips and tricks
|
|
*
|
|
* @subsection name Naming tips
|
|
*
|
|
* Avoid using the name "log" since it may conflict with <code>\::log</code> 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 <code>using namespace Sawyer::Message</code> 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).
|
|
*
|
|
* @subsection perf Performance tips
|
|
*
|
|
* Although every attempt was made to keep the library efficient, when features and performance conflict, features win. If
|
|
* messages insert operators <code><<</code> 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 @c 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:
|
|
*
|
|
* @code
|
|
* if (mlog[DEBUG])
|
|
* mlog[DEBUG] <<"the result is " <<something_expensive() <<"\n";
|
|
* @endcode
|
|
*
|
|
* When @c 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:
|
|
*
|
|
* @code
|
|
* mlog[DEBUG] and mlog[DEBUG] <<"the result is " <<something_expensive() <<"\n";
|
|
* SAWYER_MESG(mlog[DEBUG]) <<"the result is " <<something_expensive() <<"\n";
|
|
* @endcode
|
|
*
|
|
* @subsection temp Temporary streams
|
|
*
|
|
* Although <code>std::ostream</code> 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:
|
|
*
|
|
* @code
|
|
* void longFunctionWithLotsOfDebugging() {
|
|
* Stream debug(mlog[DEBUG]);
|
|
* ...
|
|
* debug <<"got here\n";
|
|
* }
|
|
* @endcode
|
|
*
|
|
* @subsection partial Partial messages
|
|
*
|
|
* 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:
|
|
*
|
|
* @code
|
|
* mlog[DEBUG] <<"first part"; // partial since no line-feed
|
|
* mlog[INFO] <<"some other message\n"; // unrelated message
|
|
* mlog[DEBUG] <<"; second part\n"; // complete the partial message
|
|
* @endcode
|
|
*
|
|
* The output to sinks like standard error that support partial messages will be, sans prefixes:
|
|
*
|
|
* @code
|
|
* first part...
|
|
* some other message
|
|
* first part; second part
|
|
* @endcode
|
|
*
|
|
* But there's a slight problem with that code: if the second line had been a message written to <code>mlog[DEBUG]</code>
|
|
* instead of <code>mlog[INFO]</code> 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 <code>std::cerr</code>:
|
|
*
|
|
* @code
|
|
* first partsome other message
|
|
* ; second part
|
|
* @endcode
|
|
*
|
|
* 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:
|
|
*
|
|
* @code
|
|
* unsigned multiply(unsigned a, unsigned b) {
|
|
* Stream mesg(mlog[DEBUG] <<"multiply(" <<a <<", " <<b <<")");
|
|
* unsigned result = 0;
|
|
* for (unsigned i=0; i<b; ++i)
|
|
* result = add(result, b); //might also write to mlog[DEBUG]
|
|
* mesg <<" = " <<result <<"\n"; // finish the message
|
|
* return result;
|
|
* }
|
|
* @endcode
|
|
*
|
|
* The @c mesg stream need not be used only for completing the one message. In fact, one of the benefits of the @ref temp hint
|
|
* is that partial messages emitted using the @c debug stream cannot be interferred with from called functions that might use
|
|
* <code>mlog[DEBUG]</code>.
|
|
*/
|
|
namespace Message {
|
|
|
|
/** 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. */
|
|
SAWYER_EXPORT bool initializeLibrary();
|
|
|
|
// Any header that #defines words that are this common is just plain stupid!
|
|
#if defined(DEBUG) || defined(TRACE) || defined(WHERE) || defined(MARCH) || \
|
|
defined(INFO) || defined(WARN) || defined(ERROR) || defined(FATAL)
|
|
# ifdef _MSC_VER
|
|
# pragma message("Undefining common words from the global namespace: DEBUG, TRACE, WHERE, MARCH, INFO, WARN, ERROR, FATAL")
|
|
# else
|
|
# warning "Undefining common words from the global namespace: DEBUG, TRACE, WHERE, MARCH, INFO, WARN, ERROR, FATAL"
|
|
# endif
|
|
# undef DEBUG
|
|
# undef TRACE
|
|
# undef WHERE
|
|
# undef MARCH
|
|
# undef INFO
|
|
# undef WARN
|
|
# undef ERROR
|
|
# undef FATAL
|
|
#endif
|
|
|
|
/** 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. */
|
|
enum Importance {
|
|
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 @c TRACE is the low-level tracing that includes all
|
|
* traceable messages. It can be assumed that if @c TRACE messages are enabled then @c WHERE messages
|
|
* are also enabled to provide the broader context. */
|
|
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 @c WHERE provides a more granular overview of the trace. */
|
|
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 @c FATAL is used is when a logic assertion is about to fail, and even then it's usually
|
|
* called from inside assert-like functions (see Sawyer::Assert for examples). */
|
|
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. */
|
|
enum AnsiColor { // the values are important: they are the ANSI foreground and background color offsets
|
|
COLOR_BLACK = 0, /**< ANSI "black" color. */
|
|
COLOR_RED = 1, /**< ANSI "red" color. */
|
|
COLOR_GREEN = 2, /**< ANSI "green" color. */
|
|
COLOR_YELLOW = 3, /**< ANSI "yellow" color. */
|
|
COLOR_BLUE = 4, /**< ANSI "blue" color. */
|
|
COLOR_MAGENTA = 5, /**< ANSI "magenta" color. */
|
|
COLOR_CYAN = 6, /**< ANSI "cyan" color. */
|
|
COLOR_WHITE = 7, /**< ANSI "white" color. */
|
|
COLOR_DEFAULT = 8 /**< ANSI default color. */
|
|
};
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Functions
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SAWYER_EXPORT std::string stringifyImportance(Importance); /**< Convert an @ref Importance enum to a string. */
|
|
SAWYER_EXPORT std::string stringifyColor(AnsiColor); /**< Convert an @ref AnsiColor enum to a string. */
|
|
SAWYER_EXPORT double now(); /**< Current system time in seconds. */
|
|
SAWYER_EXPORT std::string escape(const std::string&); /**< Convert a string to its C representation. */
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Colors
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** ANSI Color specification for text written to a terminal.
|
|
*
|
|
* Thread safety: This object uses no global state, but is otherwise not thread-safe except where noted. */
|
|
struct SAWYER_EXPORT ColorSpec {
|
|
AnsiColor foreground; /**< Foreground color, or @ref COLOR_DEFAULT. */
|
|
AnsiColor background; /**< Background color, or @ref COLOR_DEFAULT. */
|
|
#include "WarningsOff.h"
|
|
dyncompat::tribool bold; /**< Use ANSI "bold" attribute? */
|
|
#include "WarningsRestore.h"
|
|
|
|
/** Constructs an object with default foreground and background colors. */
|
|
ColorSpec(): foreground(COLOR_DEFAULT), background(COLOR_DEFAULT), bold(false) {}
|
|
|
|
/** Constructs an object that specifies only a foreground color. */
|
|
explicit ColorSpec(AnsiColor fg): foreground(fg), background(COLOR_DEFAULT), bold(false) {}
|
|
|
|
/** Constructs an object with fully-specified colors. */
|
|
ColorSpec(AnsiColor fg, AnsiColor bg, bool bold): foreground(fg), background(bg), bold(bold) {}
|
|
|
|
/** Returns true if this object is in its default-constructed state. */
|
|
bool isDefault() const { return COLOR_DEFAULT==foreground && COLOR_DEFAULT==background
|
|
&& static_cast<bool>(!bold); }
|
|
};
|
|
|
|
/** Colors to use for each message importance.
|
|
*
|
|
* Thread safety: This object uses no global state but is otherwise not thread safe. */
|
|
class SAWYER_EXPORT ColorSet {
|
|
ColorSpec spec_[N_IMPORTANCE];
|
|
public:
|
|
/** Returns a color set that uses only default colors. Warning, error, and fatal messages will use the bold attribute, but
|
|
* no colors are employed. */
|
|
static ColorSet blackAndWhite();
|
|
|
|
/** Returns a color set that uses various foreground colors for the different message importance levels. */
|
|
static ColorSet fullColor();
|
|
|
|
/** Colors for a message. Returns a reference to the color specification for a particular message importance.
|
|
* @{ */
|
|
const ColorSpec& operator[](Importance imp) const { return spec_[imp]; }
|
|
ColorSpec& operator[](Importance imp) { return spec_[imp]; }
|
|
/** @} */
|
|
};
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Message Properties
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** Properties for messages.
|
|
*
|
|
* Each message property is optional. When a message is sent through the plumbing, each node of the plumbing lattice may
|
|
* provide default values for properties that are not set, or may override properties that are set.
|
|
*
|
|
* Thread safety: This object uses no global state, but is otherwise not thread-safe except where noted. */
|
|
struct SAWYER_EXPORT MesgProps {
|
|
#include "WarningsOff.h"
|
|
Optional<std::string> facilityName; /**< The name of the logging facility that produced this message. */
|
|
Optional<Importance> importance; /**< The message importance level. */
|
|
dyncompat::tribool isBuffered; /**< Whether the output buffered and emitted on a per-message basis. */
|
|
Optional<std::string> completionStr; /**< String to append to the end of each complete message. */
|
|
Optional<std::string> interruptionStr; /**< String to append when a partial message is interrupted. */
|
|
Optional<std::string> cancelationStr; /**< String to append to a partial message when it is destroyed. */
|
|
Optional<std::string> lineTermination; /**< Line termination for completion, interruption, and cancelation. */
|
|
dyncompat::tribool useColor; /**< Whether to use ANSI escape sequences to colorize output. */
|
|
#include "WarningsRestore.h"
|
|
|
|
MesgProps(): isBuffered(dyncompat::indeterminate), useColor(dyncompat::indeterminate) {}
|
|
|
|
/** Merge the specified properties into this object and return new properties. Each property of the return value will be
|
|
* the value from this object, except when this object's property is missing, in which case the property value from the
|
|
* argument is used (which may also be missing). */
|
|
MesgProps merge(const MesgProps&) const;
|
|
|
|
/** Print the values for all properties. This is used mainly for debugging. */
|
|
void print(std::ostream&) const;
|
|
};
|
|
|
|
/** Print the values for all message properties. @sa MesgProps::print(). */
|
|
SAWYER_EXPORT std::ostream& operator<<(std::ostream &o, const MesgProps &props);
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Type defintions
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** 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:
|
|
*
|
|
* <ol>
|
|
* <li>The name of the smart pointer type is the name of the base class followed by "Ptr". For instance, a smart
|
|
* pointer for the FileSink class is named FileSinkPtr.</li>
|
|
* <li>C++ constructors for such objects are protected to that users don't inadvertently create such an object on
|
|
* the stack and then try to use its address in a smart pointer situation.</li>
|
|
* <li>Users should use the @c 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.</li>
|
|
* <li>Sawyer uses @ref Sawyer::SharedPointer "SharedPointer" for its smart pointer implementation.</li>
|
|
* </ol>
|
|
* @{ */
|
|
typedef SharedPointer<class Destination> DestinationPtr;
|
|
typedef SharedPointer<class Multiplexer> MultiplexerPtr;
|
|
typedef SharedPointer<class Filter> FilterPtr;
|
|
typedef SharedPointer<class SequenceFilter> SequenceFilterPtr;
|
|
typedef SharedPointer<class TimeFilter> TimeFilterPtr;
|
|
typedef SharedPointer<class ImportanceFilter> ImportanceFilterPtr;
|
|
typedef SharedPointer<class Gang> GangPtr;
|
|
typedef SharedPointer<class Prefix> PrefixPtr;
|
|
typedef SharedPointer<class UnformattedSink> UnformattedSinkPtr;
|
|
typedef SharedPointer<class FdSink> FdSinkPtr;
|
|
typedef SharedPointer<class FileSink> FileSinkPtr;
|
|
typedef SharedPointer<class StreamSink> StreamSinkPtr;
|
|
typedef SharedPointer<class SyslogSink> SyslogSinkPtr;
|
|
/** @} */
|
|
|
|
/** 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 <code>std::pair</code> that contains a destination and its baked
|
|
* properties. */
|
|
typedef std::pair<DestinationPtr, MesgProps> BakedDestination;
|
|
|
|
/** Baked properties for multiple destinations. */
|
|
typedef std::vector<BakedDestination> BakedDestinations;
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Messages
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** A single message.
|
|
*
|
|
* A message consists of a string and can be in a partial, completed, or canceled state. More text can be added to a message
|
|
* while it is in the partial state, but once the message is marked as completed or canceled the text becomes immutable.
|
|
* Messages also have properties, which are fed into the plumbing lattice and adjusted as they traverse to the final
|
|
* destinations during a process called "baking".
|
|
*
|
|
* Thread safety: This object uses global state, but is otherwise not thread-safe except where noted. */
|
|
class SAWYER_EXPORT Mesg {
|
|
static unsigned nextId_; // class-wide unique ID numbers
|
|
unsigned id_; // unique message ID
|
|
#include "WarningsOff.h"
|
|
std::string text_; // text of the message
|
|
#include "WarningsRestore.h"
|
|
bool isComplete_; // true when the message is complete
|
|
bool isCanceled_; // true when message is being canceled without completing
|
|
MesgProps props_; // message properties
|
|
public:
|
|
|
|
/** Creates a new, partial message that is empty. */
|
|
Mesg()
|
|
: id_(nextId_++), isComplete_(false), isCanceled_(false) {}
|
|
|
|
/** Creates a new, partial message that is empty. Sets the facility name and importance properties as specified. */
|
|
Mesg(const std::string &facilityName, Importance imp)
|
|
: id_(nextId_++), isComplete_(false), isCanceled_(false) {
|
|
props_.facilityName = facilityName;
|
|
props_.importance = imp;
|
|
}
|
|
|
|
/** Creates a new, partial message that is empty. Initializes message properties as specified. */
|
|
explicit Mesg(const MesgProps &props)
|
|
: id_(nextId_++), isComplete_(false), isCanceled_(false), props_(props) {}
|
|
|
|
/** Creates a new, completed message with the specified text. */
|
|
explicit Mesg(const std::string &mesg)
|
|
: id_(nextId_++), text_(mesg), isComplete_(true), isCanceled_(false) {}
|
|
|
|
/** Creates a new, completed message with the specified text. Also sets the facility name and importance properties
|
|
* as specified. */
|
|
Mesg(const std::string &facilityName, Importance imp, const std::string &mesg)
|
|
: id_(nextId_++), text_(mesg), isComplete_(true), isCanceled_(false) {
|
|
props_.facilityName = facilityName;
|
|
props_.importance = imp;
|
|
}
|
|
|
|
/** Creates a new, completed message with the specified text. Also sets the message properties as specified. */
|
|
Mesg(const MesgProps &props, const std::string &mesg)
|
|
: id_(nextId_++), text_(mesg), isComplete_(true), isCanceled_(false), props_(props) {}
|
|
|
|
/** Return unique message ID. Each message has an identification number that is unique within a process. */
|
|
unsigned id() const { return id_; }
|
|
|
|
/** Return the message text. */
|
|
const std::string& text() const { return text_; }
|
|
|
|
/** Returns true if the message has entered the completed state. Once a message is completed its text cannot be changed. */
|
|
bool isComplete() const { return isComplete_; }
|
|
|
|
/** Returns true if the message has entered the canceled state. Messages typically only enter this state when they
|
|
* are being destroyed. */
|
|
bool isCanceled() const { return isCanceled_; }
|
|
|
|
/** Returns true if the message has no text. This is similar, but not identical to the the inverse of the @ref
|
|
* hasText method. */
|
|
bool isEmpty() const { return text_.empty(); }
|
|
|
|
/** Returns true if the message has text other than white space. This is similar, but not identical to the inverse
|
|
* of the @ref isEmpty method. */
|
|
bool hasText() const;
|
|
|
|
/** Returns a reference to message properties. Once a message's properties are "baked" by sending them through the
|
|
* plumbing lattice, changing the message properties has no effect on the text that is emitted by the message
|
|
* sinks. A stream typically bakes the message when the first text is inserted.
|
|
* @{ */
|
|
const MesgProps& properties() const { return props_; }
|
|
MesgProps& properties() { return props_; }
|
|
/** @} */
|
|
|
|
/** Cause the message to enter the completed state. Once a message enters this state its text cannot be modified. */
|
|
void complete() { isComplete_ = true; }
|
|
|
|
/** Cause the message to enter the canceled state. This typically happens automatically when a message is being
|
|
* destroyed. Once a message enters this state its text cannot be modified. */
|
|
void cancel() { isCanceled_ = true; }
|
|
|
|
/** Append additional text to the end of the message. If the message is in the completed or canceled state then
|
|
* an std::runtime_error is thrown.
|
|
* @{ */
|
|
void insert(const std::string&);
|
|
void insert(char);
|
|
/** @} */
|
|
|
|
/** Send a message to its final destinations. */
|
|
void post(const BakedDestinations&) const;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Plumbing lattice nodes
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** Base class for all types of message destinations.
|
|
*
|
|
* This is the base class for all nodes in the plumbing lattice. */
|
|
class SAWYER_EXPORT Destination: public SharedObject, public SharedFromThis<Destination> {
|
|
protected:
|
|
mutable SAWYER_THREAD_TRAITS::RecursiveMutex mutex_; /**< Mutex protecting data members here and in subclasses. */
|
|
MesgProps dflts_; /**< Default properties merged into each incoming message. */
|
|
MesgProps overrides_; /**< Override properties applied to incoming message. */
|
|
protected:
|
|
Destination() {}
|
|
public:
|
|
virtual ~Destination() {}
|
|
|
|
/** Default values for message properties.
|
|
*
|
|
* Any message property that has no value coming from a higher layer in the plumbing lattice will be set to the default
|
|
* value if one is defined.
|
|
*
|
|
* Thread safety: Not thread safe.
|
|
*
|
|
* @{ */
|
|
const MesgProps& defaultPropertiesNS() const { return dflts_; }
|
|
MesgProps& defaultPropertiesNS() { return dflts_; }
|
|
/** @} */
|
|
|
|
/** Overrides message properties.
|
|
*
|
|
* Any overriding property value that is defined will override the value coming from a higher layer in the plumbing
|
|
* lattice.
|
|
*
|
|
* Thread safety: Not thread safe.
|
|
*
|
|
* @{ */
|
|
const MesgProps& overridePropertiesNS() const { return overrides_; }
|
|
MesgProps& overridePropertiesNS() { return overrides_; }
|
|
/** @} */
|
|
|
|
/** Bakes message properties according to the plumbing lattice.
|
|
*
|
|
* The given message properties are applied to the plumbing lattice rooted at this Destination, adjusted according to the
|
|
* default and override properties at this node of the lattice and all lower nodes. The property values at the bottom
|
|
* nodes of the lattice are appended to the @p baked argument.
|
|
*
|
|
* Thread safety: All implementations must be thread-safe. */
|
|
virtual void bakeDestinations(const MesgProps&, BakedDestinations &baked);
|
|
|
|
/** Causes a message to be emitted. The @p bakedProperties argument is one of the values returned by the @ref
|
|
* bakeDestinations method. */
|
|
virtual void post(const Mesg&, const MesgProps &bakedProperties) = 0;
|
|
|
|
/** Merge properties of this lattice node into the specified properties.
|
|
*
|
|
* Any property in @p props that is not set will be set to this node's default value for the property (if any). Any
|
|
* override property defined for this node will override the value (or lack of value) in @p props. The merged result is
|
|
* returned.
|
|
*
|
|
* Thread safety: Not thread safe. */
|
|
MesgProps mergePropertiesNS(const MesgProps &props);
|
|
};
|
|
|
|
/** Sends incoming messages to multiple destinations.
|
|
*
|
|
* This is the base class for all internal nodes of the plumbing lattice. */
|
|
class SAWYER_EXPORT Multiplexer: public Destination {
|
|
typedef std::list<DestinationPtr> Destinations;
|
|
#include "WarningsOff.h"
|
|
Destinations destinations_;
|
|
#include "WarningsRestore.h"
|
|
protected:
|
|
/** Constructor for derived classes. Non-subclass users should use @ref instance instead. */
|
|
Multiplexer() {}
|
|
public:
|
|
/** Allocating constructor. */
|
|
static MultiplexerPtr instance() { return MultiplexerPtr(new Multiplexer); }
|
|
|
|
virtual void bakeDestinations(const MesgProps&, BakedDestinations&) /*override*/;
|
|
virtual void post(const Mesg&, const MesgProps&) /*override*/;
|
|
|
|
/** Adds a child node to this node of the lattice.
|
|
*
|
|
* An <code>std::runtime_error</code> is thrown if the addition of this child would cause the lattice to become malformed
|
|
* by having a cycle.
|
|
*
|
|
* Thread safety: This method is not thread-safe.
|
|
*
|
|
* @sa to */
|
|
MultiplexerPtr addDestination(const DestinationPtr&);
|
|
|
|
/** Removes the specified child from this node of the lattice.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @sa to */
|
|
MultiplexerPtr removeDestination(const DestinationPtr&);
|
|
|
|
/** Add a child nodes to this node of the lattice and returns this node.
|
|
*
|
|
* It is often more convenient to call this method instead of @ref addDestination.
|
|
*
|
|
* Thread safety: This method is not thread-safe.
|
|
*
|
|
* @{ */
|
|
MultiplexerPtr to(const DestinationPtr&); // more convenient form of addDestination()
|
|
MultiplexerPtr to(const DestinationPtr&, const DestinationPtr&);
|
|
MultiplexerPtr to(const DestinationPtr&, const DestinationPtr&, const DestinationPtr&);
|
|
MultiplexerPtr to(const DestinationPtr&, const DestinationPtr&, const DestinationPtr&, const DestinationPtr&);
|
|
/** @} */
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Filters
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** Base class for internal nodes that filter messages.
|
|
*
|
|
* Filtering is applied when properties are baked, which should happen exactly once for each message, usually just before the
|
|
* message is posted for the first time. */
|
|
class SAWYER_EXPORT Filter: public Multiplexer {
|
|
protected:
|
|
/** Constructor for derived classes. Non-subclass users should use @ref instance instead. */
|
|
Filter() {}
|
|
public:
|
|
virtual void bakeDestinations(const MesgProps&, BakedDestinations&) /*override*/;
|
|
|
|
/** Predicate determines when a message should be forwarded onward.
|
|
*
|
|
* This method is called once from bakeDestinations(), and if it returns true then the baking is forwarded on to the child
|
|
* nodes in the plumbing lattice. If this method returns false then none of the descendents will see the properties
|
|
* (unless they can be reached by some other path), and the unreached leaf nodes will not be returned by
|
|
* bakeDestinations().
|
|
*
|
|
* Thread safety: all implementations must be thread-safe. */
|
|
virtual bool shouldForward(const MesgProps&) = 0; // return true if message should be forwarded to children when baking
|
|
|
|
/** Called once by bakeDestinations if shouldForward() returned true.
|
|
*
|
|
* This method is called after the properties have been forwarded to the child nodes. */
|
|
virtual void forwarded(const MesgProps&) = 0; // message was forwarded to children
|
|
};
|
|
|
|
/** Filters messages based on how many messages have been seen.
|
|
*
|
|
* The first @e n messages are skipped (not forwarded to children), the first one of every @e m messages thereafter is
|
|
* forwarded, for a total of @e t messages forwarded. */
|
|
class SAWYER_EXPORT SequenceFilter: public Filter {
|
|
size_t nSkip_; // skip initial messages posted to this sequencer
|
|
size_t rate_; // emit only 1/Nth of the messages (0 and 1 both mean every message)
|
|
size_t limit_; // emit at most this many messages (0 means infinite)
|
|
size_t nPosted_; // number of messages posted (including those suppressed)
|
|
protected:
|
|
/** Constructor for derived classes. Non-subclass users should use @ref instance instead. */
|
|
SequenceFilter(size_t nskip, size_t rate, size_t limit)
|
|
: nSkip_(nskip), rate_(rate), limit_(limit), nPosted_(0) {}
|
|
public:
|
|
/** Construct an instance.
|
|
*
|
|
* The node will skip the first @p nskip messages, then forward the first of every @p rate following messages, for a grand
|
|
* total of @p limit messages forwarded. If @p rate is zero or one then every message after the first @p nskip is
|
|
* forwarded (up to the @p limit). If @p limit is zero then there is no limit. */
|
|
static SequenceFilterPtr instance(size_t nskip, size_t rate, size_t limit) {
|
|
return SequenceFilterPtr(new SequenceFilter(nskip, rate, limit));
|
|
}
|
|
|
|
/** Property: number of initial messages to skip.
|
|
*
|
|
* The first @p n messages sent through this filter are discarded.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @{ */
|
|
size_t nSkip() const;
|
|
SequenceFilterPtr nSkip(size_t n);
|
|
/** @} */
|
|
|
|
/** Property: rate of messages to emit after initial messages are skipped.
|
|
*
|
|
* A rate of @e n means the first message of every group of @e n messages is forwarded to children nodes in the plumbing
|
|
* lattice. A rate of zero means the same thing as a rate of one--every message is forwarded.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @{ */
|
|
size_t rate() const;
|
|
SequenceFilterPtr rate(size_t n);
|
|
/** @} */
|
|
|
|
/** Property: total number of messages forwarded.
|
|
*
|
|
* At most @e n messages are forwarded to children in the lattice, after which messages are discarded. A value of zero
|
|
* means no limit is in effect.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @{ */
|
|
size_t limit() const;
|
|
SequenceFilterPtr limit(size_t n);
|
|
/** @} */
|
|
|
|
/** Number of messages processed.
|
|
*
|
|
* This includes messages forwarded and messages not forwarded.
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
size_t nPosted() const;
|
|
|
|
virtual bool shouldForward(const MesgProps&) /*override*/;
|
|
virtual void forwarded(const MesgProps&) /*override*/ {}
|
|
};
|
|
|
|
/** Filters messages based on time.
|
|
*
|
|
* Any message posted within some specified time of a previously forwarded message will not be forwarded to children nodes in
|
|
* the plumbing lattice. */
|
|
class SAWYER_EXPORT TimeFilter: public Filter {
|
|
double initialDelay_; // amount to delay before emitting the first message
|
|
double minInterval_; // minimum time between messages
|
|
double prevMessageTime_; // time previous message was emitted
|
|
double lastBakeTime_; // time cached by shouldForward, used by forwarded
|
|
size_t nPosted_; // number of messages posted (including those suppressed)
|
|
protected:
|
|
/** Constructor for derived classes.
|
|
*
|
|
* Non-subclass users should use @ref instance instead. */
|
|
explicit TimeFilter(double minInterval)
|
|
: initialDelay_(0.0), minInterval_(minInterval), prevMessageTime_(0.0), lastBakeTime_(0.0) {}
|
|
public:
|
|
/** Allocating constructor.
|
|
*
|
|
* Creates an instance that limits forwarding of messages to at most one message every @p minInterval seconds. */
|
|
static TimeFilterPtr instance(double minInterval) {
|
|
return TimeFilterPtr(new TimeFilter(minInterval));
|
|
}
|
|
|
|
/** Property: minimum time between messages.
|
|
*
|
|
* Any message arriving within the specified interval from a previous message that was forwarded to children in the
|
|
* lattice will be discarded.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @{ */
|
|
double minInterval() const;
|
|
TimeFilterPtr minInterval(double d);
|
|
/** @} */
|
|
|
|
/** Property: delay before the next message.
|
|
*
|
|
* Any message arriving within the specified number of seconds from this call will be discarded.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @{ */
|
|
double initialDelay() const;
|
|
TimeFilterPtr initialDelay(double d);
|
|
/** @} */
|
|
|
|
/** Number of messages processed.
|
|
*
|
|
* This includes messages forwarded and messages not forwarded.
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
size_t nPosted() const;
|
|
|
|
virtual bool shouldForward(const MesgProps&) /*override*/;
|
|
virtual void forwarded(const MesgProps&) /*override*/;
|
|
};
|
|
|
|
/** Filters messages based on importance level.
|
|
*
|
|
* For instance, to disable all messages except fatal and error messages:
|
|
*
|
|
* @code
|
|
* DestinationPtr d = ImportanceFilter::instance(false)->enable(FATAL)->enable(ERROR);
|
|
* @endcode */
|
|
class SAWYER_EXPORT ImportanceFilter: public Filter {
|
|
bool enabled_[N_IMPORTANCE];
|
|
protected:
|
|
/** Constructor for derived classes.
|
|
*
|
|
* Non-subclass users should use @ref instance instead. */
|
|
explicit ImportanceFilter(bool dflt) {
|
|
memset(enabled_, dflt?0xff:0, sizeof enabled_);
|
|
}
|
|
public:
|
|
/** Allocating constructor.
|
|
*
|
|
* Creates an instance that either allows or disallows all importance levels depending on whether @p dflt is true or
|
|
* false, respectively. */
|
|
static ImportanceFilterPtr instance(bool dflt) {
|
|
return ImportanceFilterPtr(new ImportanceFilter(dflt));
|
|
}
|
|
|
|
/** Property: the enabled/disabled state for a message importance level.
|
|
*
|
|
* Importance levels that are enabled are forwarded to children in the lattice, and those that are disable are discarded.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @{ */
|
|
bool enabled(Importance imp) const;
|
|
ImportanceFilterPtr enabled(Importance imp, bool b);
|
|
/** @} */
|
|
|
|
/** Enable an importance level.
|
|
*
|
|
* Returns this node so that the method can be chained. */
|
|
ImportanceFilterPtr enable(Importance);
|
|
|
|
/** Disable an importance level.
|
|
*
|
|
* Returns this node so that the method can be chained. */
|
|
ImportanceFilterPtr disable(Importance);
|
|
|
|
virtual bool shouldForward(const MesgProps&) /*override*/;
|
|
virtual void forwarded(const MesgProps&) /*override*/ {}
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Support for final destinations
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** @internal
|
|
* Keeps track of how much of a partial message was already emitted.
|
|
*
|
|
* Thread safety: All methods in this class are thread safe. */
|
|
class HighWater {
|
|
Optional<unsigned> id_; /**< ID number of last message to be emitted, if any. */
|
|
MesgProps props_; /**< Properties used for the last emission. */
|
|
size_t ntext_; /**< Number of characters of the message we've seen already. */
|
|
protected:
|
|
mutable SAWYER_THREAD_TRAITS::RecursiveMutex mutex_;
|
|
HighWater(const HighWater&) { abort(); } // not copyable
|
|
HighWater& operator=(const HighWater&) { abort(); } // not copyable
|
|
public:
|
|
HighWater(): ntext_(0) {}
|
|
explicit HighWater(const Mesg &m, const MesgProps &p) { emitted(m, p); }
|
|
SAWYER_THREAD_TRAITS::RecursiveMutex& mutex() const { return mutex_; }
|
|
void emitted(const Mesg&, const MesgProps&); /**< Make specified message the high water mark. */
|
|
void clear(); /**< Reset to initial state. */
|
|
bool isValid() const; /**< Returns true if high water is defined. */
|
|
unsigned id() const; /**< Exception unless <code>isValid()</code>. */
|
|
MesgProps properties() const;
|
|
size_t ntext() const; /**< Zero if <code>!isValid()</code>. */
|
|
};
|
|
|
|
/** @internal
|
|
* Coordination between two or more sinks.
|
|
*
|
|
* A Gang is used to coordinate output from two or more sinks. This is normally used by sinks writing the tty, such as a sink
|
|
* writing to stdout and another writing to stderr--they need to coordinate with each other if they're both going to the
|
|
* terminal. A gang just keeps track of what message was most recently emitted.
|
|
*
|
|
* Thread safety: All methods in this class are thread safe. */
|
|
class Gang: public HighWater, public SharedObject {
|
|
typedef Sawyer::Container::Map<int, GangPtr> GangMap;
|
|
static GangMap *gangs_; /**< Gangs indexed by file descriptor or other ID. */
|
|
static const int TTY_GANG = -1; /**< The ID for streams that are emitting to a terminal device. */
|
|
static SAWYER_THREAD_TRAITS::Mutex classMutex_; /**< Mutex for class data. */
|
|
protected:
|
|
Gang() {}
|
|
public:
|
|
static GangPtr instance() { return GangPtr(new Gang); }
|
|
static GangPtr instanceForId(int id); /**< The gang for the specified ID, creating a new one if necessary. */
|
|
static GangPtr instanceForTty(); /**< Returns the gang for streams that are emitting to a tty. */
|
|
static void removeInstance(int id); /**< Remove specified gang from global list. */
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Messages prefixes
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** Information printed at the beginning of each free-format message. */
|
|
class SAWYER_EXPORT Prefix: public SharedObject, public SharedFromThis<Prefix> {
|
|
#include "WarningsOff.h"
|
|
public:
|
|
/** When to show something. */
|
|
enum When {
|
|
NEVER=0, /**< Never show this item. */
|
|
SOMETIMES=1, /**< Show this item only sometimes (depends on the item). */
|
|
ALWAYS=2 /**< Always show this item. */
|
|
};
|
|
private:
|
|
mutable SAWYER_THREAD_TRAITS::RecursiveMutex mutex_; /**< Mutex for data members here and in subclasses. */
|
|
ColorSet colorSet_; /**< Colors to use if <code>props.useColor</code> is true. */
|
|
Optional<std::string> programName_; /**< Name of program as it will be displayed (e.g., "a.out[12345]"). */
|
|
bool showProgramName_;
|
|
bool showThreadId_;
|
|
Optional<double> startTime_; /**< Time at which program started. */
|
|
bool showElapsedTime_;
|
|
When showFacilityName_; /**< Whether the facility name should be displayed. */
|
|
bool showImportance_; /**< Whether the message importance should be displayed. */
|
|
void initFromSystem(); /**< Initialize data from the operating system. */
|
|
#include "WarningsRestore.h"
|
|
protected:
|
|
/** Constructor for derived classes.
|
|
*
|
|
* Non-subclass users should use @ref instance instead. */
|
|
Prefix()
|
|
: showProgramName_(true), showThreadId_(true), showElapsedTime_(true), showFacilityName_(SOMETIMES),
|
|
showImportance_(true) {
|
|
initFromSystem();
|
|
colorSet_ = ColorSet::fullColor();
|
|
}
|
|
public:
|
|
virtual ~Prefix() {}
|
|
|
|
/** Allocating constructor.
|
|
*
|
|
* Creates a new prefix instance and returns a pointer to it. The new object is initialized with information about the
|
|
* thread that created it, such as program name, thread ID, time of creation, etc. */
|
|
static PrefixPtr instance() { return PrefixPtr(new Prefix); }
|
|
|
|
/** Construct a prefix that is silent.
|
|
*
|
|
* Constructs a prefix whose output properties are all false, thus producing no output. */
|
|
static PrefixPtr silentInstance();
|
|
|
|
/** Property: colors to use for the prefix if coloring is enabled.
|
|
*
|
|
* Colors can be specified even for sinks that don't support them--they just won't be used.
|
|
*
|
|
* Thread safety: Not thread-safe.
|
|
*
|
|
* @{ */
|
|
const ColorSet& colorSet() const { return colorSet_; }
|
|
ColorSet& colorSet() { return colorSet_; }
|
|
/** @} */
|
|
|
|
/** Property: program name.
|
|
*
|
|
* The default is the base name of the file that was executed. An "lt-" prefix is removed from the base name since this
|
|
* is typically added by libtool and doesn't correspond to the name that the user executed.
|
|
*
|
|
* Thread safety: Not thread safe.
|
|
*
|
|
* @sa setProgramName @ref showProgramName
|
|
*
|
|
* @{ */
|
|
const Optional<std::string>& programName() const { return programName_; }
|
|
PrefixPtr programName(const std::string &s) { programName_ = s; return sharedFromThis(); }
|
|
/** @} */
|
|
|
|
/** Reset the program name from operating system information.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @sa programName showProgramName */
|
|
void setProgramName();
|
|
|
|
/** Property: whether to show the program name in the message prefix area.
|
|
*
|
|
* The default is true.
|
|
*
|
|
* Thread safety: This method is not thread-safe.
|
|
*
|
|
* @sa programName
|
|
* @{ */
|
|
bool showProgramName() const { return showProgramName_; }
|
|
PrefixPtr showProgramName(bool b) { showProgramName_ = b; return sharedFromThis(); }
|
|
/** @} */
|
|
|
|
/** Property: whether to show the thread ID in the message prefix area.
|
|
*
|
|
* The default is true.
|
|
*
|
|
* Thread safety: This method is not thread-safe.
|
|
*
|
|
* @{ */
|
|
bool showThreadId() const { return showThreadId_; }
|
|
PrefixPtr showThreadId(bool b) { showThreadId_ = b; return sharedFromThis(); }
|
|
/** @} */
|
|
|
|
/** Property: start time when emitting time deltas.
|
|
*
|
|
* On some systems the start time will be the time at which this object was created.
|
|
*
|
|
* Thread safety: This method is not thread-safe.
|
|
*
|
|
* @sa setStartTime @ref showElapsedTime
|
|
*
|
|
* @{ */
|
|
const Optional<double> startTime() const;
|
|
PrefixPtr startTime(double t);
|
|
/** @} */
|
|
|
|
/** Reset the start time from operating system information.
|
|
*
|
|
* On some systems this will be the time at which the first prefix object was created rather than the time the operating
|
|
* system created the main process.
|
|
*
|
|
* Thread safety: This method is not thread-safe.
|
|
*
|
|
* @sa startTime showElapsedTime */
|
|
void setStartTime();
|
|
|
|
/** Property: whether to show time deltas.
|
|
*
|
|
* Time deltas are displayed as fractional seconds since some starting time.
|
|
*
|
|
* Thread safety: This method is not thread-safe.
|
|
*
|
|
* @sa startTime setStartTime
|
|
* @{ */
|
|
bool showElapsedTime() const { return showElapsedTime_; }
|
|
PrefixPtr showElapsedTime(bool b) { showElapsedTime_ = b; return sharedFromThis(); }
|
|
/** @} */
|
|
|
|
/** Property: whether to show the facilityName property.
|
|
*
|
|
* When set to SOMETIMES, the facility name is shown when it differs from the program name and the program name has been
|
|
* shown. In any case, the facility name is not shown if it has no value.
|
|
*
|
|
* Thread safety: This method is not thread-safe.
|
|
*
|
|
* @{ */
|
|
When showFacilityName() const { return showFacilityName_; }
|
|
PrefixPtr showFacilityName(When w) { showFacilityName_ = w; return sharedFromThis(); }
|
|
/** @} */
|
|
|
|
/** Property: whether to show the importance property.
|
|
*
|
|
* In any case, the importance level is not shown if it has no value.
|
|
*
|
|
* Thread safety: This method is not thread-safe.
|
|
*
|
|
* @{ */
|
|
bool showImportance() const { return showImportance_; }
|
|
PrefixPtr showImportance(bool b) { showImportance_ = b; return sharedFromThis(); }
|
|
/** @} */
|
|
|
|
/** Return a prefix string.
|
|
*
|
|
* Generates a string from this prefix object.
|
|
*
|
|
* Thread safety: This method is not thread-safe. */
|
|
virtual std::string toString(const Mesg&, const MesgProps&) const;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Final destinations (sinks)
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** Base class for final destinations that are free-format.
|
|
*
|
|
* These are destinations that emit messages as strings. */
|
|
class SAWYER_EXPORT UnformattedSink: public Destination {
|
|
#include "WarningsOff.h"
|
|
GangPtr gang_;
|
|
PrefixPtr prefix_;
|
|
#include "WarningsRestore.h"
|
|
|
|
protected:
|
|
bool partialMessagesAllowed_;
|
|
|
|
protected:
|
|
/** Constructor for derived classes. Non-subclass users should use <code>instance</code> factory methods instead. */
|
|
explicit UnformattedSink(const PrefixPtr &prefix): partialMessagesAllowed_(true) {
|
|
gang_ = Gang::instance();
|
|
prefix_ = prefix ? prefix : Prefix::instance();
|
|
init();
|
|
}
|
|
public:
|
|
/** Property: sink gang.
|
|
*
|
|
* Message sinks use a gang to coordinate their output. For instance, two sinks that both write to the same file should
|
|
* share the same gang. A shared gang can also be used for sinks that write to two different locations that both happen
|
|
* to be visible collectively, such as when writing to standard error and standard output when both are writing to the
|
|
* terminal or when both have been redirected to the same file. Not all sinks support automatically choosing the correct
|
|
* gang (e.g., C++ std::ofstream doesn't have the necessary capabilities), so it must be chosen manually sometimes for the
|
|
* output to be coordinated. See Gang::instanceForId() and Gang::instanceForTty().
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @{ */
|
|
GangPtr gang() const;
|
|
UnformattedSinkPtr gang(const GangPtr &g);
|
|
/** @} */
|
|
|
|
/** Property: allow partial message output.
|
|
*
|
|
* If partial messages are not allowed, then a message is emitted only when its terminating linefeed is encountered.
|
|
* The default for unformatted sinks is to allow partial message output. Disabling partial message output is sometimes
|
|
* useful in multi-threaded applications to prevent two threads from competing with each other when they both try to
|
|
* output a complete message at the same time: one thread might interrupt the other midway through the other's message, in
|
|
* which case the other thread would have emitted a partial message. I.e., disabling partial messages works like this:
|
|
*
|
|
* @code
|
|
* concurrently {
|
|
* mlog0[INFO] <<"message from first thread\n";
|
|
* mlog1[INFO] <<"message from second thread\n";
|
|
* }
|
|
* @endcode
|
|
*
|
|
* If @c mlog0 and @c mlog1 are both emitting to standard error then the output might look like this (sans prefixes):
|
|
*
|
|
* @code
|
|
* mlog0[INFO]: message from fir...
|
|
* mlog1[INFO]: message from second thread
|
|
* mlog0[INFO]: message from first thread
|
|
* @endcode
|
|
*
|
|
* Disabling partial message output would have prevented the first partial message.
|
|
*
|
|
* @{ */
|
|
bool partialMessagesAllowed() const;
|
|
UnformattedSinkPtr partialMessagesAllowed(bool);
|
|
/** @} */
|
|
|
|
/** Property: how to generate message prefixes.
|
|
*
|
|
* This is a pointer to the object that is responsible for generating the string that appears at the beginning of each
|
|
* line and usually contains such things as the program and importance level.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @{ */
|
|
PrefixPtr prefix() const;
|
|
UnformattedSinkPtr prefix(const PrefixPtr &p);
|
|
/** @} */
|
|
|
|
/** Support function for emitting a message.
|
|
*
|
|
* This string terminates the previous partial message from the same gang. This method must be called exactly once during
|
|
* @ref render for its possible side effects, although the return value doesn't have to be used.
|
|
*
|
|
* Thread safety: not thread-safe. However, it is usually only called from inside @ref render after locks for this object
|
|
* and the gang have been obtained. */
|
|
virtual std::string maybeTerminatePrior(const Mesg&, const MesgProps&);
|
|
|
|
/** Support function for emitting a message.
|
|
*
|
|
* This string is the prefix generated by calling Prefix::toString if a prefix is necessary. This method must be called
|
|
* exactly once during @ref render for its possible side effects, although the return value doesn't have to be used.
|
|
*
|
|
* Thread safety: not thread-safe. However, it is usually only called from inside @ref render after locks for this object
|
|
* and the gang have been obtained. */
|
|
virtual std::string maybePrefix(const Mesg&, const MesgProps&);
|
|
|
|
/** Support function for emitting a message.
|
|
*
|
|
* This string is the message body, or at least the part of the body that hasn't been emitted yet. This method must be
|
|
* called exactly once during @ref render for its possible side effects, although the return value doesn't have to be
|
|
* used.
|
|
*
|
|
* Thread safety: not thread-safe. However, it is usually only called from inside @ref render after locks for this object
|
|
* and the gang have been obtained. */
|
|
virtual std::string maybeBody(const Mesg&, const MesgProps&);
|
|
|
|
/** Support function for emitting a message.
|
|
*
|
|
* This string is the message termination, interruption, or cancelation and the following line termination as
|
|
* necessary. This method must be called exactly once during @ref render for its possible side effects, although the
|
|
* return value doesn't have to be used.
|
|
*
|
|
* Thread safety: not thread-safe. However, it is usually only called from inside @ref render after locks for this object
|
|
* and the gang have been obtained. */
|
|
virtual std::string maybeFinal(const Mesg&, const MesgProps&);
|
|
|
|
/** Support function for emitting a message.
|
|
*
|
|
* The return string is constructed by calling @ref maybeTerminatePrior, @ref maybePrefix, @ref maybeBody, and @ref
|
|
* maybeFinal one time each (because some of them have side effects).
|
|
*
|
|
* Thread safety: This method is thread safe. It obtains and holds locks for this object and the gang across all the calls
|
|
* to @ref maybeTerminatePrior, @ref maybePrefix, @ref maybeBody, and @ref maybeFinal. */
|
|
virtual std::string render(const Mesg&, const MesgProps&);
|
|
protected:
|
|
/** Cause this sink to be coordinated with others.
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
void gangInternal(const GangPtr &g);
|
|
private:
|
|
void init();
|
|
};
|
|
|
|
/** Send free-format messages to a Unix file descriptor. */
|
|
class SAWYER_EXPORT FdSink: public UnformattedSink {
|
|
int fd_; // file descriptor or -1
|
|
protected:
|
|
/** Constructor for derived classes. Non-subclass users should use @ref instance instead. */
|
|
FdSink(int fd, const PrefixPtr &prefix): UnformattedSink(prefix), fd_(fd) { init(); }
|
|
public:
|
|
/** Allocating constructor. Constructs a new message sink that sends messages to the specified Unix file descriptor. */
|
|
static FdSinkPtr instance(int fd, const PrefixPtr &prefix=PrefixPtr()) {
|
|
return FdSinkPtr(new FdSink(fd, prefix));
|
|
}
|
|
|
|
virtual void post(const Mesg&, const MesgProps&) /*override*/;
|
|
private:
|
|
void init();
|
|
};
|
|
|
|
/** Send free-format messages to a C @c FILE pointer. */
|
|
class SAWYER_EXPORT FileSink: public UnformattedSink {
|
|
FILE *file_;
|
|
protected:
|
|
/** Constructor for derived classes. Non-subclass users should use @ref instance instead. */
|
|
FileSink(FILE *f, const PrefixPtr &prefix): UnformattedSink(prefix), file_(f) { init(); }
|
|
public:
|
|
/** Allocating constructor. Constructs a new message sink that sends messages to the specified C @c FILE pointer. */
|
|
static FileSinkPtr instance(FILE *f, const PrefixPtr &prefix=PrefixPtr()) {
|
|
return FileSinkPtr(new FileSink(f, prefix));
|
|
}
|
|
|
|
virtual void post(const Mesg&, const MesgProps&) /*override*/;
|
|
private:
|
|
void init();
|
|
};
|
|
|
|
/** Send free-format messages to a C++ I/O stream. */
|
|
class SAWYER_EXPORT StreamSink: public UnformattedSink {
|
|
std::ostream &stream_;
|
|
protected:
|
|
/** Constructor for derived classes. Non-subclass users should use @ref instance instead. */
|
|
StreamSink(std::ostream &stream, const PrefixPtr &prefix): UnformattedSink(prefix), stream_(stream) {}
|
|
public:
|
|
/** Allocating constructor. Constructs a new message sink that sends messages to the specified C++ output stream. */
|
|
static StreamSinkPtr instance(std::ostream &stream, const PrefixPtr &prefix=PrefixPtr()) {
|
|
return StreamSinkPtr(new StreamSink(stream, prefix));
|
|
}
|
|
|
|
virtual void post(const Mesg&, const MesgProps&) /*override*/;
|
|
};
|
|
|
|
#ifndef _WIN32
|
|
/** Sends messages to the syslog daemon.
|
|
*
|
|
* Thread safety: Unknown (depends on whether syslog is thread-safe). */
|
|
class SAWYER_EXPORT SyslogSink: public Destination {
|
|
protected:
|
|
/** Constructor for derived classes. Non-subclass users should use @ref instance instead. */
|
|
SyslogSink(const char *ident, int option, int facility);
|
|
public:
|
|
/** Allocating constructor. Constructs a new message sink that sends messages to the syslog daemon. The syslog API
|
|
* doesn't use a handle to refer to the syslog, nor does it specify what happens when @c openlog is called more than once,
|
|
* or whether the @p ident string is copied. Best practice is to use only constant strings as the @c ident argument. */
|
|
static SyslogSinkPtr instance(const char *ident, int option, int facility) {
|
|
return SyslogSinkPtr(new SyslogSink(ident, option, facility));
|
|
}
|
|
virtual void post(const Mesg&, const MesgProps&) /*override*/;
|
|
private:
|
|
void init();
|
|
};
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Message streams
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class Stream;
|
|
class StreamBuf;
|
|
|
|
/** @internal
|
|
* Adjusts reference counts for a stream and deletes the stream when the reference count hits zero.
|
|
*
|
|
* This object acts like a POD pointer type except it adjusts the reference count for the stream to which it
|
|
* points in a manner similar to SharedPointer. The reason we don't use SharedPointer is that SProxy needs special
|
|
* constructors to handle std::ostream operands with runtime type of Stream.
|
|
*
|
|
* Thread safety: Adjustments to reference counts are synchronized. */
|
|
class SAWYER_EXPORT SProxy {
|
|
Stream *stream_;
|
|
public:
|
|
SProxy(): stream_(NULL) {}
|
|
SProxy(std::ostream*); /*implicit*/
|
|
SProxy(std::ostream&); /*implicit*/
|
|
SProxy(const SProxy&);
|
|
SProxy& operator=(const SProxy&);
|
|
~SProxy() { reset(); }
|
|
Stream& operator*() { return *stream_; }
|
|
Stream* operator->() { return stream_; }
|
|
Stream* get() const { return stream_; }
|
|
operator bool() const;
|
|
void reset();
|
|
};
|
|
|
|
/** Converts text to messages.
|
|
*
|
|
* A message stream is a subclass of <code>std::ostream</code> and therefore allows all the usual stream insertion operators
|
|
* (<code><<</code>). A stream converts each line of output text to a single message, creating the message with properties
|
|
* defined for the stream and sending the results to a specified destination. Streams typically impart a facility name and
|
|
* importance level to each message via the stream's properties. */
|
|
class SAWYER_EXPORT Stream: public std::ostream {
|
|
friend class StreamBuf;
|
|
mutable SAWYER_THREAD_TRAITS::Mutex mutex_;
|
|
size_t nrefs_; // used when we don't have std::move semantics
|
|
StreamBuf *streambuf_; // each stream has its own, protected by our mutex
|
|
public:
|
|
|
|
/** Construct a stream and initialize its name and importance properties. */
|
|
Stream(const std::string facilityName, Importance imp, const DestinationPtr &destination);
|
|
|
|
/** Construct a stream and initialize its properties as specified. */
|
|
Stream(const MesgProps &props, const DestinationPtr &destination);
|
|
|
|
/** Construct a new stream from an existing stream.
|
|
*
|
|
* If @p other has a pending message then ownership of that message is moved to this new stream.
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
Stream(const Stream &other);
|
|
|
|
/** Initialize this stream from another stream.
|
|
*
|
|
* If @p other has a pending message then ownership of that message is moved to this stream.
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
Stream& operator=(const Stream &other);
|
|
|
|
/** Copy constructor with dynamic cast.
|
|
*
|
|
* Same as <code>Stream(const Stream&)</code> but declared so we can do things like this:
|
|
*
|
|
* @code
|
|
* Stream m1(mlog[INFO] <<"first part of the message");
|
|
* m1 <<"; finalize message\n";
|
|
* @endcode
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
Stream(const std::ostream &other_);
|
|
|
|
/** Assignment with dynamic cast.
|
|
*
|
|
* Same as <code>operator=(const Stream&)</code> but declared so we can do things like this:
|
|
*
|
|
* @code
|
|
* Stream m1 = mlog[INFO] <<"first part of the message");
|
|
* m1 <<"; finalize message\n";
|
|
* @endcode
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
Stream& operator=(const std::ostream &other_);
|
|
|
|
~Stream();
|
|
|
|
// used internally by SProxy; thread-safe
|
|
void incrementRefCount();
|
|
size_t decrementRefCount(); // returns new ref count
|
|
|
|
/** Used for partial messages when std::move is missing.
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
SProxy dup() const;
|
|
|
|
protected:
|
|
/** Initiaize this stream from @p other.
|
|
*
|
|
* This stream will get its own StreamBuf (if it doesn't have one already), and any pending message from @p other will be
|
|
* moved (not copied) to this stream.
|
|
*
|
|
* Thread safety: This method is not thread-safe--the caller is expected to obtain the necessary locks. */
|
|
void initFromNS(const Stream &other);
|
|
|
|
public:
|
|
/** Returns true if a stream is enabled.
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
bool enabled() const;
|
|
|
|
// We'd like bool context to return a value that can't be used in arithmetic or comparison operators, but unfortunately
|
|
// we need to also work with the super class (std::basic_ios) that has an implicit "void*" conversion which conflicts with
|
|
// the way Sawyer normally handles this (see SharedPointer for an example). We therefore override the super-class'
|
|
// void* conversion and "!" operator instead.
|
|
|
|
/** Returns true if this stream is enabled.
|
|
*
|
|
* This implicit conversion to bool can be used to conveniently avoid expensive insertion operations when a stream is
|
|
* disabled. For example, if printing <code>MemoryMap</code> is an expensive operation then the logging can be written
|
|
* any of these ways to avoid formatting memorymap when logging is disabled:
|
|
*
|
|
* @code
|
|
* if (mlog[DEBUG])
|
|
* mlog[DEBUG] <<"the memory map is: " <<memoryMap <<"\n";
|
|
*
|
|
* mlog[DEBUG] && mlog[DEBUG] <<"the memory map is: " <<memoryMap <<"\n";
|
|
*
|
|
* SAWYER_MESG(mlog[DEBUG]) <<"the memory map is: " <<memoryMap <<"\n";
|
|
* @endcode
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @{ */
|
|
operator void*() const {
|
|
return enabled() ? const_cast<Stream*>(this) : NULL;
|
|
}
|
|
#if __cplusplus >= 201103L
|
|
explicit operator bool() const {
|
|
return enabled();
|
|
}
|
|
#endif
|
|
/** @} */
|
|
|
|
/** Returns false if this stream is enabled. */
|
|
bool operator!() const { return !enabled(); }
|
|
|
|
// See Stream::bool()
|
|
#define SAWYER_MESG(message_stream) message_stream && message_stream
|
|
|
|
/** Enable or disable a stream.
|
|
*
|
|
* A disabled stream buffers the latest partial message and enabling the stream will cause the entire accumulated message
|
|
* to be emitted--whether the partial message immediately appears on the output is up to the message sinks.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @{ */
|
|
void enable(bool b=true);
|
|
void disable() { enable(false); }
|
|
/** @} */
|
|
|
|
/** Set message or stream property.
|
|
*
|
|
* If @p asDefault is false then the property is set only in the current message and the message must be empty (e.g., just
|
|
* after the previous message was completed). When @p asDefault is true the new value becomes the default for all future
|
|
* messages. The default also affects the current message if the current message is empty.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @{ */
|
|
void completionString(const std::string &s, bool asDefault=true);
|
|
void interruptionString(const std::string &s, bool asDefault=true);
|
|
void cancelationString(const std::string &s, bool asDefault=true);
|
|
void facilityName(const std::string &s, bool asDefault=true);
|
|
/** @} */
|
|
|
|
/** Property: message destination.
|
|
*
|
|
* This is the pointer to the top of the plumbing lattice.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @{ */
|
|
DestinationPtr destination() const;
|
|
Stream& destination(const DestinationPtr &d);
|
|
/** @} */
|
|
|
|
/** Return the default properties for this stream.
|
|
*
|
|
* These are the properties of the stream itself before they are adjusted by the destination lattice. The default
|
|
* properties are used each time the stream creates a message.
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
MesgProps properties() const;
|
|
};
|
|
|
|
/** Collection of streams.
|
|
*
|
|
* This forms a collection of message streams for a software component and contains one stream per message importance level.
|
|
* A facility is intended to be used by a software component at whatever granularity is desired by the author (program, name
|
|
* space, class, method, etc.) and is usually given a string name that is related to the software component which it serves.
|
|
* The string name becomes part of messages and is also the default name used by Facilities::control. All message streams
|
|
* created for the facility are given the same name, message prefix generator, and message sink, but they can be adjusted
|
|
* later on a per-stream basis.
|
|
*
|
|
* The C++ name for the facility is often just "mlog" or "logger" (appropriately scoped) so that code to emit messages is self
|
|
* documenting. The name "log" is sometimes used, but can be ambiguous with the <code>\::log</code> function in math.h.
|
|
*
|
|
* @code
|
|
* mlog[ERROR] <<"I got an error\n";
|
|
* @endcode
|
|
*
|
|
* Thread safety: This object is thread-safe except where noted. */
|
|
class SAWYER_EXPORT Facility {
|
|
static const unsigned CONSTRUCTED_MAGIC = 0x73617779;
|
|
unsigned constructed_;
|
|
mutable SAWYER_THREAD_TRAITS::Mutex mutex_;
|
|
#include "WarningsOff.h"
|
|
std::string name_;
|
|
std::vector<SProxy> streams_;
|
|
#include "WarningsRestore.h"
|
|
|
|
public:
|
|
/** Construct an empty facility.
|
|
*
|
|
* The facility will have no name and all streams will be uninitialized. Any attempt to emit anything to a facility in
|
|
* the default state will cause an <code>std::runtime_error</code> to be thrown with a message similar to "stream INFO is
|
|
* not initialized yet". This facility can be initialized by assigning it a value from another initialized facility. */
|
|
Facility(): constructed_(CONSTRUCTED_MAGIC) {}
|
|
|
|
/** Construct a new facility from an existing facility.
|
|
*
|
|
* The new facility will point to the same streams as the existing facility. */
|
|
Facility(const Facility &other);
|
|
|
|
/** Assignment operator.
|
|
*
|
|
* The destination facility will point to the same streams as the source facility. */
|
|
Facility& operator=(const Facility &src);
|
|
|
|
/** Create a named facility with default destinations. All streams are enabled and all output goes to file descriptor
|
|
* 2 (standard error) via unbuffered system calls. Facilities initialized to this state can typically be used before the
|
|
* C++ runtime is fully initialized and before @ref Sawyer::initializeLibrary is called. */
|
|
explicit Facility(const std::string &name): constructed_(CONSTRUCTED_MAGIC), name_(name) {
|
|
//initializeLibrary() //delay until later
|
|
initStreams(FdSink::instance(2));
|
|
}
|
|
|
|
/** Creates streams of all importance levels. */
|
|
Facility(const std::string &name, const DestinationPtr &destination): constructed_(CONSTRUCTED_MAGIC), name_(name) {
|
|
initStreams(destination);
|
|
}
|
|
|
|
~Facility() {
|
|
constructed_ = 0;
|
|
}
|
|
|
|
/** Returns true if called on an object that has been constructed.
|
|
*
|
|
* Returns true if this is constructed, false if it's allocated but not constructed. For instance, this method may return
|
|
* false if the object is declared at namespace scope and this method is called before the C++ runtime has had a chance to
|
|
* initialize it.
|
|
*
|
|
* Thread safety: This method is not thread-safe. */
|
|
bool isConstructed() const { return constructed_ == CONSTRUCTED_MAGIC; }
|
|
|
|
/** Returns a stream for the specified importance level.
|
|
*
|
|
* It is permissible to do the following:
|
|
*
|
|
* @code
|
|
* Stream m1 = (mlog[INFO] <<"message 1 part 1");
|
|
* Stream m2 = (mlog[INFO] <<"message 2 part 1");
|
|
* m1 <<" part 2\n";
|
|
* m2 <<" part 2\n";
|
|
* @endcode
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @{ */
|
|
Stream& get(Importance imp);
|
|
Stream& operator[](Importance imp) {
|
|
return get(imp);
|
|
}
|
|
/** @} */
|
|
|
|
/** Return the name of the facility.
|
|
*
|
|
* This is a read-only field initialized at construction time.
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
std::string name() const;
|
|
|
|
/** Renames all the facility streams.
|
|
*
|
|
* Invokes Stream::facilityName for each stream. If a name is given then that name is used, otherwise this facility's name
|
|
* is used.
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
Facility& renameStreams(const std::string &name = "");
|
|
|
|
/** Cause all streams to use the specified destination.
|
|
*
|
|
* This can be called for facilities that already have streams and destinations, but it can also be called to initialize
|
|
* the streams for a default-constructed facility.
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
Facility& initStreams(const DestinationPtr&);
|
|
};
|
|
|
|
/** Collection of facilities.
|
|
*
|
|
* A facility group is a collection of message facilities that allows its members to be configured collectively, such as from
|
|
* command-line parsing. Each stream can be enabled or disabled individually, a member facility can be enabled or disabled as
|
|
* a whole, or specific importance levels can be enabled or disabled across the entire collection.
|
|
*
|
|
* Whenever a member facility is enabled as a whole, the current set of enabled importance levels is used. This set is
|
|
* maintained automatically: when the first facility is inserted, its enabled importance levels initialize the default set.
|
|
* Whenever a message importance level is enabled or diabled via the version of @ref enable or @ref disable that takes an
|
|
* importance argument, the set is adjusted.
|
|
*
|
|
* Thread safety: This object is thread safe. */
|
|
class SAWYER_EXPORT Facilities {
|
|
public:
|
|
typedef std::set<Importance> ImportanceSet; /**< A set of importance levels. */
|
|
private:
|
|
typedef Container::Map<std::string, Facility*> FacilityMap;
|
|
mutable SAWYER_THREAD_TRAITS::Mutex mutex_;
|
|
#include "WarningsOff.h"
|
|
FacilityMap facilities_;
|
|
ImportanceSet impset_;
|
|
bool impsetInitialized_;
|
|
#include "WarningsRestore.h"
|
|
public:
|
|
|
|
/** Constructs an empty container of Facility objects. */
|
|
Facilities() {
|
|
//initializeLibrary(); -- delay until later for sake of static initialization of Message::mfacilities
|
|
// Initialize impset_ for the sake of insertAndAdjust(), but do not consider it to be truly initialized until after
|
|
// a MessageFacility is inserted.
|
|
impset_.insert(WARN);
|
|
impset_.insert(ERROR);
|
|
impset_.insert(FATAL);
|
|
impsetInitialized_ = false;
|
|
}
|
|
|
|
/** Copy constructor. */
|
|
Facilities(const Facilities&);
|
|
|
|
/** Assignment operator. */
|
|
Facilities& operator=(const Facilities&);
|
|
|
|
/** Return the set of default-enabled message importances.
|
|
*
|
|
* See Facilities class documentation for details.
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
ImportanceSet impset() const;
|
|
|
|
/** Add or remove a default importance level. The specified level is inserted or removed from the set of default enabled
|
|
* importance levels without affecting any member facility objects. If this %Facilities doesn't have a default importance
|
|
* set then it is initialized to {WARN, ERROR, FATAL} prior to adding or removing the specified level. Calling this
|
|
* function also prevents the first @ref insert from initializing the set of default importance levels. See Facilities
|
|
* class documentation for details.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @sa insert, @ref insertAndAdjust, @ref reenable. */
|
|
Facilities& impset(Importance, bool enabled);
|
|
|
|
/** Register a facility so it can be controlled as part of a collection of facilities. The optional @p name is the @e
|
|
* FACILITY_NAME string of the control language for the inserted @p facility, and defaults to the facility's name (see
|
|
* @ref control). The name consists of one or more symbols separated by "::", or "." and often corresponds to the source
|
|
* code component that it serves. Names are case-sensitive in the control language. No two facilities in the same
|
|
* facility group may have the same name, but a single facility may appear multiple times in the group with different
|
|
* names.
|
|
*
|
|
* This method comes in two flavors: @ref insert, and @ref insertAndAdjust. The latter immediately enables and disables
|
|
* the facility's streams according to the current default importance levels of this facility group. If @p facility is
|
|
* the first facility to be inserted, and it is inserted by @ref insert rather than @ref insertAndAdjust, then the
|
|
* facility's currently enabled streams are used to initialize the set of default enabled importance levels.
|
|
*
|
|
* The @p facility is incorporated by reference and should not be destroyed until after this facility group is
|
|
* destroyed.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @{ */
|
|
Facilities& insert(Facility &facility, const std::string &name="");
|
|
Facilities& insertAndAdjust(Facility &facility, std::string name="");
|
|
/** @} */
|
|
|
|
/** Remove a facility by name.
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
Facilities& erase(const std::string &name);
|
|
|
|
/** Remove all occurrences of the facility.
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
Facilities& erase(Facility &facility);
|
|
|
|
/** Return an existing facility by name.
|
|
*
|
|
* Returns a reference to the specified facility. Throws an <code>std::domain_error</code> if no facility exists with the
|
|
* specified name.
|
|
*
|
|
* Thread safety: This method is thread safe, but it returns a reference to a facility that could be deleted before it can
|
|
* be used. The caller must ensure that no other thread is invoking the @ref erase method concurrently on this same
|
|
* Facilities object. */
|
|
Facility& facility(const std::string &name) const;
|
|
|
|
/** Return names for all known facilities.
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
std::vector<std::string> facilityNames() const;
|
|
|
|
/** Parse a single command-line switch and enable/disable the indicated streams. Returns an empty string on success, or an
|
|
* error message on failure. No configuration changes are made if a failure occurs.
|
|
*
|
|
* The control string, @p s, is a sentence in the following language:
|
|
*
|
|
* @code
|
|
* Sentence := (FacilitiesControl ( ',' FacilitiesControl )* )?
|
|
*
|
|
* FacilitiesControl := AllFacilitiesControl | NamedFacilityControl
|
|
* AllFacilitiesControl := StreamControlList
|
|
* NamedFacilityControl := FACILITY_NAME '(' StreamControlList ')'
|
|
*
|
|
* StreamControlList := StreamControl ( ',' StreamControl )*
|
|
* StreamControl := StreamState? Relation? STREAM_NAME | 'all' | 'none'
|
|
* StreamState := '+' | '!'
|
|
* Relation := '>=' | '>' | '<=' | '<'
|
|
* @endcode
|
|
*
|
|
* The language is processed left-to-right and stream states are affected immediately. This allows, for instance, turning
|
|
* off all streams and then selectively enabling streams.
|
|
*
|
|
* Some examples:
|
|
* @code
|
|
* all // enable all event messages
|
|
* none,debug // turn on only debugging messages
|
|
* all,!debug // turn on all except debugging
|
|
* fatal // make sure fatal errors are enabled (without changing others)
|
|
* none,<=trace // disable all except those less than or equal to 'trace'
|
|
*
|
|
* frontend(debug) // enable debugging streams in the 'frontend' facility
|
|
* warn,frontend(!warn) // turn on warning messages everywhere except the frontend
|
|
* @endcode
|
|
*
|
|
* The global list, @e AllFacilitiesControl, also affects the list of default-enabled importance levels.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*/
|
|
std::string control(const std::string &s);
|
|
|
|
/** Returns a configuration string.
|
|
*
|
|
* Returns a string suitable for passing to @ref control. */
|
|
std::string configuration() const;
|
|
|
|
/** Readjust all member facilities.
|
|
*
|
|
* All members are readjusted to enable only those importance levels that are part of this facility group's default
|
|
* importance levels. It is as if we called @ref disable then @ref enable for each message facility by name. See
|
|
* Facilities class documentation for details.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @sa impset
|
|
*
|
|
* @{ */
|
|
Facilities& reenable();
|
|
Facilities& reenableFrom(const Facilities &other);
|
|
/** @} */
|
|
|
|
/** Enable/disable specific importance level across all facilities.
|
|
*
|
|
* This method also affects the set of "current importance levels" used when enabling an entire facility.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @sa impset
|
|
*
|
|
* @{ */
|
|
Facilities& enable(Importance, bool b=true);
|
|
Facilities& disable(Importance imp) { return enable(imp, false); }
|
|
/** @} */
|
|
|
|
/** Enable/disable a facility by name.
|
|
*
|
|
* When disabling, all importance levels of the specified facility are disabled. When enabling, only the current
|
|
* importance levels are enabled for the facility. If the facility is not found then nothing happens.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @{ */
|
|
Facilities& disable(const std::string &switch_name) { return enable(switch_name, false); }
|
|
Facilities& enable(const std::string &switch_name, bool b=true);
|
|
/** @} */
|
|
|
|
/** Enable/disable all facilities.
|
|
*
|
|
* When disabling, all importance levels of all facilities are disabled. When enabling, only the current importance
|
|
* levels are enabled for each facility.
|
|
*
|
|
* Thread safety: This method is thread-safe.
|
|
*
|
|
* @{ */
|
|
Facilities& enable(bool b=true);
|
|
Facilities& disable() { return enable(false); }
|
|
/** @} */
|
|
|
|
/** Print the list of facilities and their states.
|
|
*
|
|
* This is mostly for debugging purposes. The output may have internal line feeds and will end with a line feed.
|
|
*
|
|
* Thread safety: This method is thread-safe. */
|
|
void print(std::ostream&) const;
|
|
|
|
private:
|
|
/** @internal Private info used by control() to indicate what should be adjusted. */
|
|
struct ControlTerm {
|
|
ControlTerm(const std::string &facilityName, bool enable)
|
|
: facilityName(facilityName), lo(DEBUG), hi(DEBUG), enable(enable) {}
|
|
std::string toString() const; /**< String representation of this struct for debugging. */
|
|
std::string facilityName; /**< %Optional facility name. Empty implies all facilities. */
|
|
Importance lo, hi; /**< Inclusive range of importances. */
|
|
bool enable; /**< New state. */
|
|
};
|
|
|
|
Facilities& insertNS(Facility&, std::string); // non-synchronized version
|
|
Facilities& enableNS(Importance, bool); // non-synchronized version
|
|
|
|
// Error information thrown internally.
|
|
struct ControlError {
|
|
ControlError(const std::string &mesg, const char *position): mesg(mesg), inputPosition(position) {}
|
|
std::string mesg;
|
|
const char *inputPosition;
|
|
};
|
|
|
|
// Functions used by the control() method
|
|
static std::string parseFacilityName(const char* &input);
|
|
static std::string parseEnablement(const char* &input);
|
|
static std::string parseRelation(const char* &input);
|
|
static std::string parseImportanceName(const char* &input);
|
|
static Importance importanceFromString(const std::string&);
|
|
static std::list<ControlTerm> parseImportanceList(const std::string &facilityName, const char* &input, bool isGlobal);
|
|
|
|
};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** Library-provided message destination. This is the top of a lattice that sends all messages to file descriptor 2, which is
|
|
* usually standard error. */
|
|
SAWYER_EXPORT extern DestinationPtr merr;
|
|
|
|
/** Facility used by Sawyer components. This facility is added to the @ref mfacilities group with the name "sawyer". */
|
|
SAWYER_EXPORT extern Facility mlog;
|
|
|
|
/** Library-provided facility group. When the library is initialized, this group contains only @ref 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. */
|
|
SAWYER_EXPORT extern Facilities mfacilities;
|
|
|
|
/** The stream to be used for assertions. The default is to use <code>Sawyer::Message::mlog[FATAL]</code>. This variable is
|
|
* initialized at the first call to @ref Assert::fail if it is a null pointer. Users can assign a different stream to it any
|
|
* time before then:
|
|
*
|
|
* @code
|
|
* int main(int argc, char *argv[]) {
|
|
* Sawyer::Message::assertionStream = Sawer::Message::mlog[FATAL];
|
|
* @endcode */
|
|
SAWYER_EXPORT extern SProxy assertionStream;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Facilities guard
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** Saves and restores facilities.
|
|
*
|
|
* The constructor saves the facilities settings (which streams are enabled and disabled) and the destructor restores
|
|
* them. This does not save the actual list of streams, so if streams are erased/inserted they will not be restored. */
|
|
class SAWYER_EXPORT FacilitiesGuard {
|
|
Facilities &facilities_;
|
|
typedef Sawyer::Container::Map<std::string, std::vector<bool> > State;
|
|
State state_;
|
|
public:
|
|
/** Saves and restores the global message facilities.
|
|
*
|
|
* Saves and restores which streams are enabled in @ref mfacilities. */
|
|
FacilitiesGuard()
|
|
: facilities_(mfacilities) {
|
|
save();
|
|
}
|
|
|
|
/** Saves and restores specified message facilities. */
|
|
explicit FacilitiesGuard(Facilities &facilities)
|
|
: facilities_(facilities) {
|
|
save();
|
|
}
|
|
|
|
/** Restores previously saved facility settings. */
|
|
~FacilitiesGuard() {
|
|
restore();
|
|
}
|
|
|
|
private:
|
|
void save();
|
|
void restore();
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// The most commonly used stuff
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** Commonly used message types.
|
|
*
|
|
* This namespace exists so that users can say <code>using namespace Sawyer::Message::Common</code> to be able to use the most
|
|
* important message types without name qualification and without also bringing in all the things that are less frequently
|
|
* used. In particular, this does not include Sawyer::Message::mlog since users often name their own facilities "mlog". */
|
|
namespace Common {
|
|
|
|
using Message::DEBUG;
|
|
using Message::TRACE;
|
|
using Message::WHERE;
|
|
using Message::MARCH;
|
|
using Message::INFO;
|
|
using Message::WARN;
|
|
using Message::ERROR;
|
|
using Message::FATAL;
|
|
|
|
using Message::Stream;
|
|
using Message::Facility;
|
|
using Message::Facilities;
|
|
|
|
} // namespace
|
|
|
|
|
|
} // namespace
|
|
} // namespace
|
|
|
|
#endif
|