{"id":81,"date":"2008-10-11T09:56:17","date_gmt":"2008-10-11T16:56:17","guid":{"rendered":"http:\/\/www.venzon.org\/?p=81"},"modified":"2008-10-11T09:57:39","modified_gmt":"2008-10-11T16:57:39","slug":"logging-using-ostream-and-streambuf","status":"publish","type":"post","link":"http:\/\/www.venzon.org\/?p=81","title":{"rendered":"Logging using ostream and streambuf"},"content":{"rendered":"<p>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&#8217;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&#8217;s currently designed, you pass your logger streambuf a prefix and a forwarding ostream.  Then, when you use the ostream that&#8217;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 &#8212; 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).<br \/>\n<!--more--><\/p>\n<pre lang=\"cpp\" lineno=\"1\">\r\nclass logstreambuf : public std::streambuf\r\n{\r\n\tpublic:\r\n\t\ttypedef int int_type;\r\n\t\ttypedef std::streampos pos_type;\r\n\t\ttypedef std::streamoff off_type;\r\n\t\ttypedef std::char_traits<char> traits_type;\r\n\r\n\tprotected:\r\n\t\tstd::string buffer;\r\n\t\tconst std::string prefix;\r\n\t\tstd::ostream & forwardstream;\r\n\r\n\tpublic:\r\n\t\tlogstreambuf(const std::string & newprefix, std::ostream & forwardee) : \r\n\t\t\tprefix(newprefix),forwardstream(forwardee) {}\r\n\t\t\r\n\t\tstd::string str() const\r\n\t\t{\r\n\t\t\tstd::string ret;\r\n\t\t\tif (this->pptr())\r\n\t\t\t{\r\n\t\t\t\tret = std::string(this->pbase(), this->pptr());\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tret = buffer;\r\n\t\t\treturn ret;\r\n\t\t}\r\n\r\n\tprotected:\r\n\t\tvirtual int_type overflow(int_type c = traits_type::eof())\r\n\t\t{\r\n\t\t\t\r\n\t\t\tconst std::string::size_type capacity = buffer.capacity();\r\n\t\t\tconst std::string::size_type max_size = buffer.max_size();\r\n\t\t\t\r\n\t\t\t\/*std::cout << \"Overflow: \\\"\" << (char)c << \"\\\" (\" << (int)c << \")\" << std::endl;\r\n\t\t\tstd::cout << \"Capacity: \" << capacity << std::endl;\r\n\t\t\tstd::cout << \"pptr: \" << this->pptr() - this->pbase() << std::endl;\r\n\t\t\tstd::cout << \"epptr: \" << this->epptr() - this->pbase() << std::endl;\r\n\t\t\tstd::cout << \"alignment check: \" << this->pbase() - buffer.data() << std::endl;*\/\r\n\t\t\t\r\n\t\t\t\/\/ Try to append __c into output sequence in one of two ways.\r\n\t\t\t\/\/ Order these tests done in is unspecified by the standard.\r\n\t\t\tconst char conv = traits_type::to_char_type(c);\r\n\t\t\tchar * bufferend = const_cast<char_type*>(buffer.data());\r\n\t\t\tif (this->pptr() >= this->epptr())\r\n\t\t\t{\r\n\t\t\t\t\/\/ NB: Start ostringstream buffers at 512 chars.  This is an\r\n\t\t\t\t\/\/ experimental value (pronounced \"arbitrary\" in some of the\r\n\t\t\t\t\/\/ hipper english-speaking countries), and can be changed to\r\n\t\t\t\t\/\/ suit particular needs.\r\n\t\t\t\t\t\t\t\/\/\r\n\t\t\t\t\/\/ _GLIBCXX_RESOLVE_LIB_DEFECTS\r\n\t\t\t\t\/\/ 169. Bad efficiency of overflow() mandated\r\n\t\t\t\t\/\/ 432. stringbuf::overflow() makes only one write position\r\n\t\t\t\t\/\/      available\r\n\t\t\t\tstd::string::size_type opt_len = std::max(std::string::size_type(2 * capacity),\r\n\t\t\t\t\t\tstd::string::size_type(512));\r\n\t\t\t\tif (this->epptr() - this->pbase() < capacity)\r\n\t\t\t\t\topt_len = capacity; \r\n\t\t\t\tconst std::string::size_type len = std::min(opt_len, max_size);\r\n\t\t\t\tstd::string tmp;\r\n\t\t\t\ttmp.reserve(len);\r\n\t\t\t\tif (this->pbase())\r\n\t\t\t\t\ttmp.assign(this->pbase(), this->epptr() - this->pbase());\r\n\t\t\t\ttmp.push_back(conv);\r\n\t\t\t\tbuffer.swap(tmp);\r\n\t\t\t\tstd::string::size_type o = this->pptr() - this->pbase();\r\n\t\t\t\tchar * newbasep = const_cast<char_type*>(buffer.data());\r\n\t\t\t\tthis->setp(newbasep, newbasep + buffer.capacity());\r\n\t\t\t\tthis->pbump(o);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\t*this->pptr() = conv;\r\n\t\t\tthis->pbump(1);\r\n\t\t\treturn c;\r\n\t\t}\r\n\t\t\r\n\t\tvirtual int_type sync()\r\n\t\t{\r\n\t\t\tstd::string myoutput = prefix + str();\r\n\t\t\tfor (int i = 0; i < myoutput.length()-1; i++)\r\n\t\t\t{\r\n\t\t\t\tif (myoutput[i] == '\\n')\r\n\t\t\t\t\tmyoutput.insert(i+1,prefix.length(),' ');\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tforwardstream << myoutput << std::flush;\r\n\t\t\tchar * newbasep = const_cast<char_type*>(buffer.data());\r\n\t\t\tthis->setp(newbasep, newbasep+buffer.capacity());\r\n\t\t\tpbump(newbasep-this->pptr());\r\n\t\t}\r\n};\r\n\r\nint main()\r\n{\r\n\tlogstreambuf joebuf(\"INFO: \",std::cout);\r\n\tstd::ostream os(&joebuf);\r\n\tos << \"Test stream 1\" << std::endl;\r\n\tos << \"Test stream 2\\n\";\r\n\tos << \"Test stream 3\" << std::endl;\r\n\tos << \"Test stream 4\" << std::endl;\r\n\t\r\n\treturn 0;\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>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 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[],"class_list":["post-81","post","type-post","status-publish","format-standard","hentry","category-prototyping"],"_links":{"self":[{"href":"http:\/\/www.venzon.org\/index.php?rest_route=\/wp\/v2\/posts\/81","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.venzon.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.venzon.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.venzon.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.venzon.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=81"}],"version-history":[{"count":6,"href":"http:\/\/www.venzon.org\/index.php?rest_route=\/wp\/v2\/posts\/81\/revisions"}],"predecessor-version":[{"id":90,"href":"http:\/\/www.venzon.org\/index.php?rest_route=\/wp\/v2\/posts\/81\/revisions\/90"}],"wp:attachment":[{"href":"http:\/\/www.venzon.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=81"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.venzon.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=81"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.venzon.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=81"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}