Logging using ostream and streambuf

I architected all of my subsystems in the VDrift refactor to receive ostream objects that they use to output logging information/errors. This is convenient because it keeps all of my subsystems independent from a central logging class; all they have to know about is std::ostream. Of course, to get any advanced functionality out of this I realized I’d need to create a class that inherits from the streambuf class and then create my ostreams with the special logger streambuf. I did exactly that today and have posted the results below. The way it’s currently designed, you pass your logger streambuf a prefix and a forwarding ostream. Then, when you use the ostream that’s using your logger streambuf, the streambuf will attach your prefix and send the output to the forwarding ostream. In the future I might create support for multiple forwarding ostreams with filter levels — say I want to send some errors to the std output, everything to a log.txt file, and errors again to a stringstream that i can use to access the errors from within my program (for dialog boxes, etc).

class logstreambuf : public std::streambuf
{
	public:
		typedef int int_type;
		typedef std::streampos pos_type;
		typedef std::streamoff off_type;
		typedef std::char_traits traits_type;

	protected:
		std::string buffer;
		const std::string prefix;
		std::ostream & forwardstream;

	public:
		logstreambuf(const std::string & newprefix, std::ostream & forwardee) : 
			prefix(newprefix),forwardstream(forwardee) {}
		
		std::string str() const
		{
			std::string ret;
			if (this->pptr())
			{
				ret = std::string(this->pbase(), this->pptr());
			}
			else
				ret = buffer;
			return ret;
		}

	protected:
		virtual int_type overflow(int_type c = traits_type::eof())
		{
			
			const std::string::size_type capacity = buffer.capacity();
			const std::string::size_type max_size = buffer.max_size();
			
			/*std::cout << "Overflow: \"" << (char)c << "\" (" << (int)c << ")" << std::endl;
			std::cout << "Capacity: " << capacity << std::endl;
			std::cout << "pptr: " << this->pptr() - this->pbase() << std::endl;
			std::cout << "epptr: " << this->epptr() - this->pbase() << std::endl;
			std::cout << "alignment check: " << this->pbase() - buffer.data() << std::endl;*/
			
			// Try to append __c into output sequence in one of two ways.
			// Order these tests done in is unspecified by the standard.
			const char conv = traits_type::to_char_type(c);
			char * bufferend = const_cast(buffer.data());
			if (this->pptr() >= this->epptr())
			{
				// NB: Start ostringstream buffers at 512 chars.  This is an
				// experimental value (pronounced "arbitrary" in some of the
				// hipper english-speaking countries), and can be changed to
				// suit particular needs.
							//
				// _GLIBCXX_RESOLVE_LIB_DEFECTS
				// 169. Bad efficiency of overflow() mandated
				// 432. stringbuf::overflow() makes only one write position
				//      available
				std::string::size_type opt_len = std::max(std::string::size_type(2 * capacity),
						std::string::size_type(512));
				if (this->epptr() - this->pbase() < capacity)
					opt_len = capacity; 
				const std::string::size_type len = std::min(opt_len, max_size);
				std::string tmp;
				tmp.reserve(len);
				if (this->pbase())
					tmp.assign(this->pbase(), this->epptr() - this->pbase());
				tmp.push_back(conv);
				buffer.swap(tmp);
				std::string::size_type o = this->pptr() - this->pbase();
				char * newbasep = const_cast(buffer.data());
				this->setp(newbasep, newbasep + buffer.capacity());
				this->pbump(o);
			}
			else
				*this->pptr() = conv;
			this->pbump(1);
			return c;
		}
		
		virtual int_type sync()
		{
			std::string myoutput = prefix + str();
			for (int i = 0; i < myoutput.length()-1; i++)
			{
				if (myoutput[i] == '\n')
					myoutput.insert(i+1,prefix.length(),' ');
			}
			
			forwardstream << myoutput << std::flush;
			char * newbasep = const_cast(buffer.data());
			this->setp(newbasep, newbasep+buffer.capacity());
			pbump(newbasep-this->pptr());
		}
};

int main()
{
	logstreambuf joebuf("INFO: ",std::cout);
	std::ostream os(&joebuf);
	os << "Test stream 1" << std::endl;
	os << "Test stream 2\n";
	os << "Test stream 3" << std::endl;
	os << "Test stream 4" << std::endl;
	
	return 0;
}

Comments are closed.

Dev Journal and Project Hosting