// 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 #include #include #include #include #include #include #include #include #include #include #include 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 \"" <mlog[INFO] selects one of the message * Stream objects from the facility. The linefeed marks the end of a message; messages are "partial" until the linefeed is * inserted, but depending on the sink (final destination) even partial messages might be shown. Since a message stream is a * subclass of std::ostream, all the usual output insertion operators just work. * * One of the benefits of using this namespace is that the output will have a consistent look and feel. It can even be * colorized based on message importance (at least for ANSI terminal output). The output will look something like this: * * @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 mfacilities.insert 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 :: and * . 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 << operators is only half the story--the plumbing lattice deals with how * a message is conveyed to the final destination(s) and what transformations happen along the way. The plumbing is a run-time * constructed lattice containing internal nodes and leaf nodes. Each leaf node is a final destination, or sink, for a message * and can be a C++ std::ostream, a C @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 \::log from math.h. * * If facilities are consistently named (e.g., always "mlog") then code that emits messages can always use that name and will * automatically get the most narrowly scoped facility that's visible according to C++ visibility rules, which is probably the * facility that is most applicable. * * A using namespace Sawyer::Message will prevent you from having to qualify the message importance symbols, but * if you name your facilities "mlog" they may be ambiguous with Sawyer::Message::mlog. Sawyer also provides the namespace * Sawyer::Message::Common, which defines only the most commonly used types (the importance levels, Stream, Facility, and * Facilities). * * @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 << 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 " <std::ostream objects are not copyable, and not movable before c++11, Sawyer's message streams * implement a form of copying/moving. Namely, a Stream copy constructor can be used to create a new message stream identical * to the source message stream, and any partial message in the source stream is moved to the destination stream. This * enables a way to create a temporary stream for use inside a function: * * @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 mlog[DEBUG] * instead of mlog[INFO] then the library would not have been able to tell that "some other message" is unrelated * to "first part" and would have emitted something that's not any better than using plain old std::cerr: * * @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(" <mlog[DEBUG]. */ 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(!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 facilityName; /**< The name of the logging facility that produced this message. */ Optional importance; /**< The message importance level. */ dyncompat::tribool isBuffered; /**< Whether the output buffered and emitted on a per-message basis. */ Optional completionStr; /**< String to append to the end of each complete message. */ Optional interruptionStr; /**< String to append when a partial message is interrupted. */ Optional cancelationStr; /**< String to append to a partial message when it is destroyed. */ Optional 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: * *
    *
  1. 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.
  2. *
  3. 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.
  4. *
  5. 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.
  6. *
  7. Sawyer uses @ref Sawyer::SharedPointer "SharedPointer" for its smart pointer implementation.
  8. *
* @{ */ typedef SharedPointer DestinationPtr; typedef SharedPointer MultiplexerPtr; typedef SharedPointer FilterPtr; typedef SharedPointer SequenceFilterPtr; typedef SharedPointer TimeFilterPtr; typedef SharedPointer ImportanceFilterPtr; typedef SharedPointer GangPtr; typedef SharedPointer PrefixPtr; typedef SharedPointer UnformattedSinkPtr; typedef SharedPointer FdSinkPtr; typedef SharedPointer FileSinkPtr; typedef SharedPointer StreamSinkPtr; typedef SharedPointer 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 std::pair that contains a destination and its baked * properties. */ typedef std::pair BakedDestination; /** Baked properties for multiple destinations. */ typedef std::vector 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 { 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 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 std::runtime_error 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 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 isValid(). */ MesgProps properties() const; size_t ntext() const; /**< Zero if !isValid(). */ }; /** @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 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 { #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 props.useColor is true. */ Optional programName_; /**< Name of program as it will be displayed (e.g., "a.out[12345]"). */ bool showProgramName_; bool showThreadId_; Optional 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& 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 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 instance 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 std::ostream and therefore allows all the usual stream insertion operators * (<<). 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 Stream(const Stream&) 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 operator=(const Stream&) 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 MemoryMap 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: " <(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 \::log 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 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 std::runtime_error 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 ImportanceSet; /**< A set of importance levels. */ private: typedef Container::Map 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 std::domain_error 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 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 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 Sawyer::Message::mlog[FATAL]. 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 > 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 using namespace Sawyer::Message::Common 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