Commit in slic on MAIN
include/logstream.h+190added 1.1
       /streamext.h+92added 1.1
src/logstream.cc+419added 1.1
+701
3 added files
Added small logging class found on WWW. (Need to check if usage is okay.)

slic/include
logstream.h added at 1.1
diff -N logstream.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ logstream.h	13 Oct 2005 23:52:11 -0000	1.1
@@ -0,0 +1,190 @@
+// ======================================================================================
+// logstream.h - Version 1.0 - Copyright ©2002 Stephen W. Chappell
+// A customized output stream for logging data
+//
+// Namespace: slug
+//
+// Requires: streamext.h
+//
+// Contents:
+//   - logbuf    - a filtering streambuf used with the logstream; it inserts the date,
+//                 time, and log level into the real buffer
+//   - log_base  - a base class for the logstream; it contains members needed to
+//                 initialize the other logstream base, ostream, as well as a workaround
+//                 for not being able to cast rdbuf() is MSVC
+//   - logstream - an ostream descendent that uses the filtering logbuf to output data
+//
+// Description:
+//   This customized ostream descendant extends the ostream class using standard (but
+//   somewhat cryptic) methods. The logstream itself is simply an ostream descendant that
+//   utilizes a custom streambuf. The custom streambuf (logbuf, a streambuf descendant)
+//   is what really does the work:
+//
+//   - the logbuf filters the data going to some other streambuf or streambuf descendant,
+//     essentially looking for newlines so it can insert some stuff in the beginning
+//   - the other streambuf does its normal thing to output data; it's supplied to the
+//     logbuf constructor as a parameter
+//
+//   This might seem complicated, but since the streambuf is what really does all the
+//   work in standard iostreams, this makes a whole lot of sense, and, now that it's
+//   done, it seems a whole lot less complicated
+//
+//   For the moment, log_base contains a std::filebuf to use to actually output the data,
+//   at some point it would be nice to make this a parameter so that, for example, some
+//   sort of remote window logging or multithreaded logging could be implemented with
+//   another custom streambuf (the logbuf itself shouldn't have to change)
+//
+// Additional credits:
+//   - the basic class architecture was strongly influenced by the iostreams postings of
+//     Dietmar Kühl regarding "prefix" streams at 
+//     http://www.inf.uni-konstanz.de/~kuehl/c++/iostream/
+//
+// Revision History:
+//   Date       | Ver | Author | Description
+//   -----------+-----+--------+--------------------------------------------------------
+//   01/14/2002 | 1.0 | SwC    | Initial Version
+// ======================================================================================
+#ifndef SLUG_LOGSTREAM_H
+#define SLUG_LOGSTREAM_H
+
+#include <fstream>
+#include <ostream>
+#include <streambuf>
+
+// ======================================================================================
+
+namespace slug
+{
+
+// ======================================================================================
+// Class Declarations
+// ======================================================================================
+
+// --------------------------------------------------------------------------------------
+// class logbuf - this is a "filtering" streambuf; it filters data destined for the
+//   output, when it finds a newline it inserts the log line header
+// Note that the streambuf passed in the constructor is NOT owned by logbuf
+// --------------------------------------------------------------------------------------
+class logbuf : public std::streambuf
+{
+public:
+    explicit logbuf(std::streambuf *sb);
+    virtual ~logbuf();
+    
+    void     level(unsigned level);
+    unsigned level(void) const;
+        // setter/getter for the level of the next line to be output
+
+    void suppress(bool value);
+    bool suppress(void) const;
+        // suppresses the output of the line prefix
+
+protected:
+    int overflow(int ch);
+        // called to output the next character
+
+    int sync();
+        // called to sync the buffer with the actual output
+
+private:
+    class logbufImpl *impl; // hides implementation to reduce dependencies
+};
+
+// --------------------------------------------------------------------------------------
+// class log_base - exists solely for the filebuf & logbuf members to be inherited, so
+//   that they're available to initialize logstream's ostream base with
+// --------------------------------------------------------------------------------------
+class log_base
+{
+public:
+    enum log_level // LogMask - values control what messages get logged
+    {
+        ll_debug   = 0x0001,
+        ll_info    = 0x0002,
+        ll_warning = 0x0004,
+        ll_error   = 0x0008,
+        ll_trace   = 0x0010,
+        ll_winmsg  = 0x0020,
+        ll_all     = 0xFFFF
+    };
+
+    log_base():m_buf(), m_logbuf( new logbuf(&m_buf) ) {}
+    virtual ~log_base() { delete m_logbuf; }
+
+    logbuf* get_logbuf(void) const  { return m_logbuf;  }
+        // returns a pointer to the log output buffer
+        // this is a concession for MSVC - trying to cast rdbuf() results in AV's, so
+        // this is a less than perfect workaround
+
+protected:
+    std::filebuf  m_buf;    // this must be declared before m_logbuf!
+    logbuf       *m_logbuf; 
+};
+
+// --------------------------------------------------------------------------------------
+// class logstream - an output stream tailored to logging
+// --------------------------------------------------------------------------------------
+class logstream : public log_base, public std::ostream
+{
+public:
+    logstream();
+    ~logstream();
+
+    void open(const char *filename);
+
+    logstream& level(unsigned value);
+        // sets the level for the next data to be output
+
+    void     filter(unsigned value) { m_filter = value; }
+    unsigned filter(void) const     { return m_filter;  }
+        // sets/gets the filter for the next data to be output
+
+    void write_hex(const char *p_data, unsigned size, unsigned level);
+
+private:
+    unsigned  m_filter;
+
+    void translate(const unsigned char *start, const unsigned char *end);
+};
+
+// --------------------------------------------------------------------------------------
+// class setlevel - an output manipulator for the logstream
+// --------------------------------------------------------------------------------------
+struct setlevel
+{
+    setlevel(unsigned level):m_level(level) {}
+    unsigned m_level;
+};
+
+// ======================================================================================
+// Function Declarations
+// ======================================================================================
+
+// --------------------------------------------------------------------------------------
+// operator<< - overloaded for logstream to allow filtering; doesn't work as a class
+//   member so it's implemented as a standalone (manipulators like endl become ambiguous)
+// Additional overloads for custom manipulators and to resolve the odd ambiguity (e.g.
+//   const char*) 
+// --------------------------------------------------------------------------------------
+template <typename T>
+logstream& operator<<(logstream &lhs, const T& rhs)
+{
+    std::ostream::sentry cerberus(lhs);
+    if ( cerberus )
+    {
+        if ( lhs.get_logbuf()->level() & lhs.filter() )
+            *(dynamic_cast<std::ostream*>(&lhs)) << rhs;
+    }
+
+    return lhs;
+}
+
+logstream& operator<<(logstream &lhs, const char *rhs);
+logstream& operator<<(logstream &lhs, const setlevel &rhs);
+
+} // namespace slug
+
+// --------------------------------------------------------------------------------------
+#endif
+// ======================================================================================
+

slic/include
streamext.h added at 1.1
diff -N streamext.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ streamext.h	13 Oct 2005 23:52:11 -0000	1.1
@@ -0,0 +1,92 @@
+// ======================================================================================
+// streamext.h - Version 1.1 - Copyright ©2002 Stephen W. Chappell
+// Generic stream extensions to make my life easier
+//
+// Contents:
+//   - operator<< for tm, based strongly on operator<< presented in "The Standard C++
+//     Locale" by Nathan C. Myers, posted at html://www.cantrip.org/locale.html
+//
+// Revision History:
+//   Date       | Ver | Author | Description
+//   -----------+-----+--------+--------------------------------------------------------
+//   01/11/2002 | 1.1 | SwC    | Problems with locale inclusion, so hdrs now always
+//              |     |        |   included; also removed from slug namespace;
+//              |     |        |   Microsoft compatibility; now outputs using specific
+//              |     |        |   format ('c' doesn't work in MSVC, time doesn't output
+//              |     |        |   readably)
+//   10/09/2001 | 1.0 | SwC    | Initial Version
+// ======================================================================================
+#ifndef SLUG_STREAMEXT_H
+#define SLUG_STREAMEXT_H
+
+#include <ctime>
+#include <locale>
+#include <ostream>
+
+// ======================================================================================
+// Overloaded Operators
+// ======================================================================================
+
+// --------------------------------------------------------------------------------------
+// operator<< - overloading for std::tm; allows outputting of the date using standard
+//   streams; Microsoft's locale implementation is a little different from the standard
+//   hence the different version, also time functions aren't in the std namespace in
+//   MSVC
+// --------------------------------------------------------------------------------------
+#if !defined(_MSC_VER)
+    template<class charT, class traits>
+    std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT,traits>& os,
+        const std::tm& date)
+    {
+        // typedefs to make the rest of this function more readable
+        typedef std::ostreambuf_iterator<charT,traits> outIter_t;
+        typedef std::time_put<charT, outIter_t> Facet;
+
+        // makes this safe in a multithreaded environment
+        std::basic_ostream<charT, traits>::sentry cerberus(os);
+        if ( cerberus ) 
+        {
+            // output the date & time in the local format
+            //const Facet& fac = std::use_facet<Facet>(os.getloc());
+            //fac.put(os, os, os.fill(), &date, 'c');
+
+            // output the date & time, plus weekday and AM/PM indicator, in the local 
+            // format
+            static const char format[] = "%a %x %X %p";
+            const Facet& fac = std::use_facet<Facet>(os.getloc());
+            if ( fac.put(os, os, os.fill(), &date, format, format + 11).failed() )
+                os.setstate(os.badbit);
+        }
+
+        return os;
+    }
+#else
+    template<class charT, class traits>
+    std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT,traits>& os,
+        const tm& date)
+    {
+        // typedefs to make the rest of this function more readable
+        typedef std::ostreambuf_iterator<charT,traits> outIter_t;
+        typedef std::time_put<charT, outIter_t> Facet;
+
+        // makes this safe in a multithreaded environment
+        std::basic_ostream<charT, traits>::sentry cerberus(os);
+        if ( cerberus )
+        {
+            // output the date & time, plus weekday and AM/PM indicator, in the local 
+            // format note that the simpler version of put (std version above) doesn't
+            // work in MSVC
+            static const char format[] = "%a %x %I:%M:%S %p";
+            const Facet& fac = std::_USE(os.getloc(), Facet);
+            if ( fac.put(os, os, &date, format, format + 17).failed() )
+                os.setstate(os.badbit);
+        }
+
+        return os;
+    }
+#endif
+
+// --------------------------------------------------------------------------------------
+#endif
+// ======================================================================================
+

slic/src
logstream.cc added at 1.1
diff -N logstream.cc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ logstream.cc	13 Oct 2005 23:52:11 -0000	1.1
@@ -0,0 +1,419 @@
+// ======================================================================================
+// logstream.cpp - Version 1.0 - Copyright ©2002 Stephen W. Chappell
+// A customized output stream for logging data
+//
+// Namespace: slug
+//
+// Requires: streamext.h
+//
+// Contents:
+//   - logbuf    - a filtering streambuf used with the logstream; it inserts the date,
+//                 time, and log level into the real buffer
+//   - log_base  - a base class for the logstream; it contains members needed to
+//                 initialize the other logstream base, ostream, as well as a workaround
+//                 for not being able to cast rdbuf() is MSVC
+//   - logstream - an ostream descendent that uses the filtering logbuf to output data
+//
+// Description:
+//   This customized ostream descendant extends the ostream class using standard (but
+//   somewhat cryptic) methods. The logstream itself is simply an ostream descendant that
+//   utilizes a custom streambuf. The custom streambuf (logbuf, a streambuf descendant)
+//   is what really does the work:
+//
+//   - the logbuf filters the data going to some other streambuf or streambuf descendant,
+//     essentially looking for newlines so it can insert some stuff in the beginning
+//   - the other streambuf does its normal thing to output data; it's supplied to the
+//     logbuf constructor as a parameter
+//
+//   This might seem complicated, but since the streambuf is what really does all the
+//   work in standard iostreams, this makes a whole lot of sense, and, now that it's
+//   done, it seems a whole lot less complicated
+//
+//   For the moment, log_base contains a std::filebuf to use to actually output the data,
+//   at some point it would be nice to make this a parameter so that, for example, some
+//   sort of remote window logging or multithreaded logging could be implemented with
+//   another custom streambuf (the logbuf itself shouldn't have to change)
+//
+// Additional credits:
+//   - the basic class architecture was strongly influenced by the iostreams postings of
+//     Dietmar Kühl regarding "prefix" streams at
+//     http://www.inf.uni-konstanz.de/~kuehl/c++/iostream/
+//
+// Revision History:
+//   Date       | Ver | Author | Description
+//   -----------+-----+--------+--------------------------------------------------------
+//   01/14/2002 | 1.0 | SwC    | Initial Version
+// ======================================================================================
+#include <ctime>
+#include <fstream>
+#include <iomanip>
+#include <ostream>
+#include <streambuf>
+#include <string>
+#include <sstream>
+
+#include "streamext.h"
+#include "logstream.h"
+// --------------------------------------------------------------------------------------
+using std::filebuf;
+using std::endl;
+using std::setw;
+using std::ios;
+using std::ios_base;
+using std::locale;
+using std::ostream;
+using std::streambuf;
+using std::string;
+using std::stringstream;
+
+#ifndef _MSC_VER
+    // MSVC doesn't include these in the std namespace
+    using std::time_t;
+    using std::time;
+    using std::localtime;
+#endif
+
+// ======================================================================================
+
+namespace slug
+{
+
+// ======================================================================================
+// Function Implementation
+// ======================================================================================
+
+// --------------------------------------------------------------------------------------
+// operator<< for const char* - overloaded << operator for logstreams and const chars;
+//   this is needed because the template creates ambiguities with existing ostream
+//   operators
+// --------------------------------------------------------------------------------------
+logstream& operator<<(logstream &lhs, const char *rhs)
+{
+    std::ostream::sentry cerberus(lhs);
+    if ( cerberus )
+    {
+        if ( lhs.get_logbuf()->level() & lhs.filter() )
+            *(dynamic_cast<std::ostream*>(&lhs)) << rhs;
+    }
+
+    return lhs;
+}
+
+// --------------------------------------------------------------------------------------
+// operator<< for setlevel manipulator - sets the current log level
+// --------------------------------------------------------------------------------------
+logstream& operator<<(logstream &lhs, const setlevel &rhs)
+{
+    std::ostream::sentry cerberus(lhs);
+    if ( cerberus )
+    {
+        lhs.level(rhs.m_level);
+    }
+
+    return lhs;
+}
+
+// ======================================================================================
+// logbufImpl Declaration
+// ======================================================================================
+
+struct logbufImpl
+{
+    streambuf    *m_sbuf;     // the actual streambuf used to read and write chars
+    bool          m_bol,      // remember whether we are at a new line
+                  m_suppress; // suppress the prefix output when set
+    unsigned      m_level;
+    stringstream  m_datebuf;  // used to construct the date; this is a member so it
+                              // doesn't have to be imbued with the locale more than once
+
+    explicit logbufImpl(streambuf *sb)
+        :m_sbuf(sb),
+         m_bol(true),
+         m_suppress(false),
+         m_level(log_base::ll_info),
+         m_datebuf(ios_base::out) {}
+
+    const char* leveltext(void);
+};
+
+// ======================================================================================
+// logbuf Implementation
+// ======================================================================================
+
+// --------------------------------------------------------------------------------------
+// logbuf::logbuf - constructor
+// --------------------------------------------------------------------------------------
+logbuf::logbuf(streambuf *sb)
+    :impl( new logbufImpl(sb) )
+{
+    setp(0, 0); // initializes the put buffer to be empty so overflow gets called
+    impl->m_datebuf.imbue( locale("") );
+
+    // I don't know if this is just a bug in the Borland compiler or what, but for some
+    // reason the date that comes out in m_datebuf the first time it gets written to is
+    // not correct (11/30/1990 on 1/9/2002)
+    time_t now = time(0);
+    impl->m_datebuf.str("");
+    impl->m_datebuf << *localtime(&now);
+}
+
+// --------------------------------------------------------------------------------------
+// logbuf::~logbuf - destructor
+// --------------------------------------------------------------------------------------
+logbuf::~logbuf()
+{
+    if ( !impl->m_bol )
+    {
+        impl->m_suppress = true;
+        overflow('\n');
+    }
+
+    delete impl;
+}
+
+// --------------------------------------------------------------------------------------
+// logbuf::level - sets the level of the next line to be output
+// --------------------------------------------------------------------------------------
+void logbuf::level(unsigned level)
+{
+    impl->m_level = level;
+
+    // if the level is changed and we're not at the end of a line, then we need to
+    // advance to the next line
+    if ( !impl->m_bol )
+    {
+        overflow('\n');
+    }
+}
+
+// --------------------------------------------------------------------------------------
+// logbuf::level - gets the level of the next line to be output
+// --------------------------------------------------------------------------------------
+unsigned logbuf::level(void) const
+{
+    return impl->m_level;
+}
+
+// --------------------------------------------------------------------------------------
+// logbuf::suppress - suppresses the output of the line prefix
+// --------------------------------------------------------------------------------------
+void logbuf::suppress(bool value)
+{
+    impl->m_suppress = value;
+}
+
+// --------------------------------------------------------------------------------------
+// logbuf::suppress - suppresses the output of the line prefix
+// --------------------------------------------------------------------------------------
+bool logbuf::suppress(void) const
+{
+    return impl->m_suppress;
+}
+
+// --------------------------------------------------------------------------------------
+// logbuf::logbuf - overflow is called when the put buffer is full; since the put buffer
+//   is size 0 for this logbuf, it gets called for every char; normally this is forwarded
+//   to the "real" streambuf, but in the case of a newline we set m_bol, and if m_bol is
+//   already set the line prefix is output
+// --------------------------------------------------------------------------------------
+int logbuf::overflow(int ch)
+{
+    if ( ch != EOF )
+    {
+        // when we get to the beginning of a line, output the date & time, and the
+        // currently set message level
+        if ( impl->m_bol & !impl->m_suppress )
+        {
+            // get the date and save it off
+            time_t now = time(0);
+            impl->m_datebuf.str("");
+            impl->m_datebuf << *localtime(&now) << " [" << impl->leveltext() << "] ";
+            string nowstring = impl->m_datebuf.str();
+
+            // output the date, etc
+            if ( impl->m_sbuf->sputn(nowstring.c_str(), nowstring.size())
+                 != static_cast<signed>(nowstring.size()) )
+                return EOF;
+            else
+                impl->m_bol = false;
+        }
+
+        int rc = impl->m_sbuf->sputc(ch);
+
+        if ( ch == '\n' )
+            impl->m_bol = true;
+
+        return rc;
+    }
+
+    return 0;
+}
+
+// --------------------------------------------------------------------------------------
+// logbuf::sync - syncs the (nonexistent) buffers with the actual output
+// --------------------------------------------------------------------------------------
+int logbuf::sync()
+{
+    impl->m_sbuf->pubsync();
+    return 0;
+}
+
+// ======================================================================================
+// logbuf Implementation
+// ======================================================================================
+
+// --------------------------------------------------------------------------------------
+// logbufImpl::leveltext - outputs the best match name for the currently selected level
+// --------------------------------------------------------------------------------------
+const char* logbufImpl::leveltext(void)
+{
+    if      ( m_level & log_base::ll_debug   ) return "DEBUG  ";
+    else if ( m_level & log_base::ll_info    ) return "INFO   ";
+    else if ( m_level & log_base::ll_warning ) return "WARNING";
+    else if ( m_level & log_base::ll_error   ) return "ERROR  ";
+    else if ( m_level & log_base::ll_trace   ) return "TRACE  ";
+    else if ( m_level & log_base::ll_winmsg  ) return "WINMSG ";
+
+    return "UNKNOWN";
+}
+
+// ======================================================================================
+// logstream Implementation
+// ======================================================================================
+
+// --------------------------------------------------------------------------------------
+// logstream::logstream - constructor; constructs log_base base, then uses it's m_buf
+//   member to construct the ostream base; this is important - without this arrangement
+//   something else would have to be done like maintaining a dynamic filebuf
+// --------------------------------------------------------------------------------------
+logstream::logstream()
+    :log_base(),
+     ostream( m_logbuf ),
+     m_filter(log_base::ll_error | log_base::ll_warning)
+{
+}
+
+// --------------------------------------------------------------------------------------
+// logstream::~logstream - destructor
+// --------------------------------------------------------------------------------------
+logstream::~logstream()
+{
+}
+
+// --------------------------------------------------------------------------------------
+// logstream::open - open the requested file in append mode
+// --------------------------------------------------------------------------------------
+void logstream::open(const char *filename)
+{
+    // open the file
+    m_buf.open(filename, ios::out | ios::app);
+
+    // output a divider; this is cast to ostream to avoid filtering
+    bool old_suppress = get_logbuf()->suppress();
+    get_logbuf()->suppress(true);
+
+    setf(ios::left);
+    width(115);
+    char old_fill = fill('=');
+    *dynamic_cast<ostream*>(this) << ">" << "<\n";
+    fill(old_fill);
+
+    get_logbuf()->suppress(old_suppress);
+}
+
+// --------------------------------------------------------------------------------------
+// logstream::level - sets the level for the next data to be output
+// --------------------------------------------------------------------------------------
+logstream& logstream::level(unsigned value)
+{
+    get_logbuf()->level(value);
+    return *this;
+}
+
+// --------------------------------------------------------------------------------------
+// logstream::write_hex - writes hex data to the log
+// --------------------------------------------------------------------------------------
+void logstream::write_hex(const char *p_data, unsigned size, unsigned level)
+{
+    // if this message isn't of the right level, discard it
+    if ( !(level & m_filter) ) return;
+    get_logbuf()->level(level);
+
+    // output message header
+    *dynamic_cast<ostream*>(this) << "Hex dump (" << size << " bytes):\n\t";
+
+    // output the first hex address
+    char orig_fill = fill('0');
+    setf(ios::right);
+    *dynamic_cast<ostream*>(this) << setw(8) << std::hex << 0x00000000 << ' ';
+    setf(ios::left);
+    fill(orig_fill);
+
+    const unsigned char *data = reinterpret_cast<const unsigned char*>(p_data),
+                        *line = reinterpret_cast<const unsigned char*>(p_data);
+
+    // output data in rows of hex, with a break every 16 bytes, and ASCII translation at
+    // the end of the line
+    for ( unsigned pos = 0 ; pos < size ; ++pos, ++data )
+    {
+        if ( pos )
+        {
+            if ( !(pos % 16) ) // end of line; add the translation & go to next line
+            {
+                // translate the line of text
+                *dynamic_cast<ostream*>(this) << "  ";
+                translate(line, data);
+                *dynamic_cast<ostream*>(this) << "\n\t";
+                line = data;
+
+                // output the next line's hex address
+                fill('0');
+                setf(ios::right);
+                *dynamic_cast<ostream*>(this) << std::hex << setw(8) << pos << ' ';
+                setf(ios::left);
+                fill(orig_fill);
+            }
+            else if ( !(pos % 8) ) // middle of line; insert an extra space
+            {
+                *dynamic_cast<ostream*>(this) << ' ';
+            }
+        }
+
+        fill('0');
+        *dynamic_cast<ostream*>(this) << std::hex << setw(2) << static_cast<short>(*data) << ' ';
+        fill(orig_fill);
+    }
+
+    // we hit the end of the data in the middle somewhere, so space over to where the
+    // translation should appear
+    size %= 16;
+    if ( size )
+    {
+        if ( size < 8 ) *dynamic_cast<ostream*>(this) << ' ';
+        for ( unsigned i = 16 - size ; i != 0 ; --i )
+            *dynamic_cast<ostream*>(this) << setw(3) << ' ';
+    }
+
+    // output the translation of the last (incomplete) line of data
+    *dynamic_cast<ostream*>(this) << "  ";
+    translate(line, data);
+    *dynamic_cast<ostream*>(this) << std::endl;
+}
+
+// --------------------------------------------------------------------------------------
+// logstream::translate - translates characters from start to end into ASCII text
+// --------------------------------------------------------------------------------------
+void logstream::translate(const unsigned char *start, const unsigned char *end)
+{
+    for ( ; start != end ; ++start )
+    {
+        get_logbuf()->sputc(( *start > 32 ) ? *start : '.');
+    }
+}
+
+// ======================================================================================
+
+} // namespace
+
+// ======================================================================================
+
CVSspam 0.2.8