Quote:
Original post by hymerman
Okay, I'm now very interested in this idea of using std::cerr and std::cout, could any of you point me in the direction of some kind of documentation that'll tell me how to modify them (replacing streams and whatnot)?
Hmm, well, I've been intending to write a series of articles on my website but as usual time and the elements have conspired against me. I'll give a few basics, though.
First off, I use a custom streambuf. Here's the header file for a class that just wraps whatever is already in a standard stream and prepends a severity, a timestamp, and an optional facility. Just as an example.
#include <streambuf>#include <string>namespace Bregma{ /** * The DebugStreambuf provides a concrete basic_streambuf that appends a * timestamp and process ID (or thread ID) at the start of each new line. * * This streambuf is an unbuffered streambuf. It is not capable of input. */ template<typename Char, typename Traits = std::char_traits<Char> > class DebugStreambuf : public std::basic_streambuf<Char, Traits> { public: typedef typename std::basic_streambuf<Char, Traits>::traits_type traits_type; typedef typename std::basic_streambuf<Char, Traits>::int_type int_type; public: /** * Buffer construction. */ DebugStreambuf(std::basic_streambuf<Char, Traits> *pRealBuf); void setLogLevel(const LogLevel &logLevel) { m_logLevel = logLevel; } void setFacility(const std::string &facility) { m_facility = facility; } protected: /** * Function called by an ostream when it's time to send something out. * * @param c The value to be written out (generally a single character). * * @returns A value equal to traits_type::eof() on failure, * traits_type::not_eof() on success. */ int_type overflow(int_type c = traits_type::eof()); private: DebugStreambuf(const DebugStreambuf&); DebugStreambuf& operator=(const DebugStreambuf&); private: std::basic_streambuf<Char, Traits> *m_pRealBuf; bool m_bAtBeginningOfLine; LogLevel m_logLevel; std::string m_facility; };} // namespace Bregma
The LogLevel is an enum in another header file. Don't worry about it for now. The important thing is the implementation. Keep in mind this is modified from production code, so any errors are mine during online editing (not sure if I got the braces right, this online editor is awkward).
#include "debugstreambuf.h"#include <sstream>using namespace std;/** * Constructs a basic DebugStream. */template<typename C, typename T> Bregma::DebugStreambuf<C,T>:: DebugStreambuf(basic_streambuf<C,T>* pRealBuf) : m_pRealBuf(pRealBuf) , m_bAtBeginningOfLine(true) , m_logLevel(kLOG_INFO) { }/** * Actual function to move bytes to the logging stream if appropriate. */template<typename C, typename T> typename Bregma::DebugStreambuf<C,T>::int_type Bregma::DebugStreambuf<C,T>:: overflow(int_type c) { int_type retval = traits_type::not_eof(c); if (!traits_type::eq_int_type(c, traits_type::eof())) { if (m_bAtBeginningOfLine) { m_bAtBeginningOfLine = false; basic_ostringstream<C,T> ostr; // Format and display the level tag. char tag = '?'; switch (m_logLevel) { case kLOG_FATAL: tag = 'F'; break; case kLOG_ERROR: tag = 'E'; break; case kLOG_WARNING: tag = 'W'; break; case kLOG_INFO: tag = 'I'; break; case kLOG_VERBOSE: tag = 'V'; break; case kLOG_DEBUG: tag = 'D'; break; default: tag = '?'; } ostr << '-' << tag << '-'; // Format and display the time stamp. time_t curTime = std::time(NULL); char timestamp[32]; std::strftime(timestamp, sizeof(timestamp), "%Y.%m.%dT%H:%M:%S", localtime(&curTime)); ostr << timestamp; // Format and display the facility. if (!m_facility.empty()) { ostr << '[' << m_facility << ']'; } if (!ostr.str().empty()) { ostr << ": "; } // Send the prefix string out. const basic_string<C,T>& str = ostr.str(); streamsize sz = m_pRealBuf->sputn(str.c_str(), str.length()); if (sz != str.length()) { return traits_type::eof(); } } // Send the real character out. retval = m_pRealBuf->sputc(c); } // If the end-of-line was seen, reset the beginning-of-line indicator and // the default log level. if (traits_type::eq_int_type(c, traits_type::to_int_type('\n'))) { m_bAtBeginningOfLine = true; m_logLevel = kLOG_INFO; } return retval; }
None of the above is publicly available and can live in its own little DLL if necessary (or not). The public interface would be in a header file.
namespace Bregma{ enum LogLevel { kLOG_FATAL, kLOG_ERROR, kLOG_WARNING, kLOG_INFO, kLOG_VERBOSE, kLOG_DEBUG }; /** * Convert an IOStream to a Bregma logging stream. * * This can be used to convert, for example, std::cerr into a Bregma logging * stream. * * @param ostr [IN] The IOStream to convert. * @param level [IN] The default loglevel cutoff (default is INFO). * @param flags [IN] Flags to toggle various output fields. */ void convertToBregmaLogger(std::ostream &ostr); /** * Setter for the log level cutoff. * * @param ostr [IN] The IOStream for which the log cutoff is to be set. * @param level [IN] The new log level cutoff. */ void setLogCutoff(std::ostream &ostr, const LogLevel &level); /** * Manipulator helper for setting the current log level on a debug stream. */ class LogLevelSetting { public: LogLevel level() const { return m_level; } private: explicit LogLevelSetting(LogLevel level): m_level(level) {} friend const LogLevelSetting logLevel(LogLevel); private: LogLevel m_level; }; /** * Ostream manipulator for setting the current log level. */ inline const LogLevelSetting logLevel(LogLevel level) { return LogLevelSetting(level); } /** * Ostream inserter for the log level manipulator. * * @param ostr [IN] The output stream. * @param ls [IN] The log level setting. */ std::ostream& operator<<(std::ostream& ostr, const LogLevelSetting ls); /** * A manipulator helper for seeting the facility. */ class LogFacilitySetter { public: const std::string &facility() const { return m_facility; } private: LogFacilitySetter(const std::string &facility): m_facility(facility) {} friend const LogFacilitySetter logFacility(const std::string &facility); private: const std::string& m_facility; }; /** * An ostream manipulator for setting the current facility in the log stream. */ inline const LogFacilitySetter logFacility(const std::string &facility) { return LogFacilitySetter(facility); } /** * Ostream inserter for the facility manipulator. * * @param ostr [IN] The output stream. * @param ls [IN] The log facility setter manipulator helper.. */ std::ostream& operator<<(std::ostream& ostr, const LogFacilitySetter ls); /** * A handy stream to mark the entry and exit of a scope. * * @param ostr [IN] The output stream. * @param ls [IN] A string. */ class ScopeMarker { public: ScopeMarker(std::ostream &ostr, const std::string &what) : m_ostr(ostr) , m_what(what) { m_ostr << m_what << " begins\n"; } ~ScopeMarker() { m_ostr << m_what << " ends\n"; } private: std::ostream &m_ostr; std::string m_what; };} // namespace Bregma
Naturally you'll need an implementation for some of the above.
void Bregma::convertToBregmaLogger(ostream &ostr){ ostr.rdbuf(new DebugStreambuf<char>(ostr.rdbuf()));}ostream &Bregma::operator<<(ostream& ostr, const Bregma::LogLevelSetting ls){ typedef DebugStreambuf<char> Dstr; Dstr *pDstr = dynamic_cast<Dstr*>(ostr.rdbuf()); if (pDstr) { pDstr->setLogLevel(ls.level()); } return ostr;}ostream &Bregma::operator<<(ostream& ostr, const Bregma::LogFacilitySetter ls){ typedef DebugStreambuf<char> Dstr; Dstr *pDstr = dynamic_cast<Dstr*>(ostr.rdbuf()); if (pDstr) { pDstr->setFacility(ls.facility()); } return ostr;}
At last, an example of how to use this.
#include "bregmalogger.h"#include <iostream>using namespace std;using namespace Bregma;int main(int, char*[]){ convertToBregmaLogger(cerr); cerr << logFacility("TEST") << logLevel(kLOG_WARNING) << "This is a demo.\n";}
As I said, I have a whole lot more that needs to be turned into human-readable annotated code (and provided as a downloadable source). I just need a Round Tuit and another 25 hours in the day.
--smw