Advertisement

Streaming Logger

Started by June 09, 2009 08:12 PM
8 comments, last by ApochPiQ 15 years, 5 months ago
I'm trying to create a log class and could use some help. I know what I want it to do and how I want it to work but have failed my search fu in finding out how to make it happen (doesn't help that I haven't written a line of code in over 10 years and that in VB). That said, here's the problem: This class should send it's input to stdout, a file (specified at creation), or both. I want it to be use just like cout is: EX: LOG log( "app.log" ); log << "The value of i is: " << i << endl; [The outputs should have "The value of i is: 7" (without the quotes and assuming i is a numeric type with a value of 7] Being able to alter the input before it is output would be a bonus. Something like: log.TimeStamp( prepend ) or log.TimeStamp( off ) or log.Prepend( " -- ERROR -- " ). I can probably figure out how to do this if I can get a nudge in the right direction. Thanks in advance.
There was a code snippet featured here not to long ago. It might be what you're looking for.
Advertisement
This is where operator overloading comes into play. Here's a nice little tutorial. With that in mind, you can do something like this:

class Log{    public:        template <typename T>        Log& operator << (const T& value);    private:        bool logToFile;        std::ofstream file;};template <typename T>Log& Log::operator << (const T& value){    // normally you'd fill the timestamp with the actual time...    std::string timestamp = "Right now";    std::cout << timestamp << ": " << value;    if (logToFile)    {        file << timestamp << ": " << value;    }    return *this;}


Disclaimer: I haven't actually tested that code. It's just example code to give you the idea. I'd be happy to explain more if you'd like.

[edit]

ninja'd++;

Oh, and you'll have to be kind of careful about the timestamp thing. If you were to do log << "I like number " << 13, it would actually print the timestamp twice (once for each time you called operator <<, which is twice in this example). There are ways to work around this though. I'll let you try to think of some as an exercise, but I'd be happy to help you generate some ideas if you're stuck.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]
Quote: Original post by _fastcall
There was a code snippet featured here not to long ago. It might be what you're looking for.


Thanks for that. Boost is on my list of Things To Learn To Play With. Guess I should bump it up a couple places. Do you have any idea how the Boost "iostream library to “transforms” our little class into something like cout"? If you don't, no sweat, I'll just wait until I can read C++ enough to find out from their source myself.

Quote: Original post by MikeTacular
This is where operator overloading comes into play. Here's a nice little tutorial. With that in mind, you can do something like this:

*** Source Snippet Removed ***

Disclaimer: I haven't actually tested that code. It's just example code to give you the idea. I'd be happy to explain more if you'd like.


Thanks a lot. I read that wikibook chapter, tried to follow along and couldn't really make any sense of the result (meaning the compiler yelled at me alot). I'll try to read over it with the snippet you provided and see if I can't make some sense of this.

Quote: Original post by MikeTacularOh, and you'll have to be kind of careful about the timestamp thing. If you were to do log << "I like number " << 13, it would actually print the timestamp twice (once for each time you called operator <<, which is twice in this example). There are ways to work around this though. I'll let you try to think of some as an exercise, but I'd be happy to help you generate some ideas if you're stuck.


If I can't make the prepended information play nicely I'll either implement my own buffer (although probably not) or wait until I can read this language better and figure out how to get at the buffer the stream objects use directly. Either way, it's a bennie and not needed for the initial implementation.
I hate to bombard people with information, but this tutorial might be a little more friendly (read all 7 pages!). In addition, this site has a lot of examples, and while they aren't tutorials, they give you an idea of what your code should look like. Last but not least, The C++ FAQ has some nice things to keep in mind, but you could probably skip most of it.

As far as prefixing logs with some value or with a timestamp, here are two ideas (though I'd love to hear other people's ideas). First, you could just not do it. Just manually add the extra timestamp or message log for each entry. Second, you could have some "special value" that, when logged, adds a new line to the log and tells the logger to insert a new timestamp or message on the next line. Once you log something to the new line, you first prefix it with the timestamp/message, and then log the user's entry, and then tell the logger to not log the timestamp/prefix-message until the "special value" is encountered again.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]
Thanks Mike! Bombard away! There's never too much information and this is a hobby for me so I'll just put a pot of coffee on and spend the night reading. This is exactly the kind of stuff I've been searching for and failing to find for the last two days (I should have come here first).

Quote: Original post by MikeTacularAs far as prefixing logs with some value or with a timestamp, here are two ideas [. . .]


Like I said above, I'm currently going to just not do it. Where's the fun in that though? Both of your ideas would work, I think, and seem straight forward but they aren't for me (unless I'm misunderstanding) as they put restrictions (as far as I can tell with my inexperience) on the format of the results imposed by the tools (which is one of my pet peeves).

As a third potential option I found this:
Standard C++ Library Iostreams and Locales User's Guide

I'm not sure if it'd let me do what I want but chapters 6 through 15 look promising once I'm fluent enough in the language to understand it when I read it.
Advertisement
Quote: Original post by MikeTacular
Oh, and you'll have to be kind of careful about the timestamp thing. If you were to do log << "I like number " << 13, it would actually print the timestamp twice (once for each time you called operator <<, which is twice in this example).


Interestingly enough, the timestamp is actually prefixed when you std::endl, from what I observed in the article.
@Mike: Your snippet worked like a champ, except that it can't take << endl; on the end without spewing an error that the function argument is ambiguous. I'll figure it out later and newline and flush manually in the mean time.

@_fastcall: quit teasing, I can only read and type so fast :)
Quote: Original post by _fastcall
Quote: Original post by MikeTacular
Oh, and you'll have to be kind of careful about the timestamp thing. If you were to do log << "I like number " << 13, it would actually print the timestamp twice (once for each time you called operator <<, which is twice in this example).


Interestingly enough, the timestamp is actually prefixed when you std::endl, from what I observed in the article.

I was thinking the same thing, except you would want to timestamp prefixed in the next write after a newline. So i would set a flag when you send endl and the on next write, add timestamp if flag is true.
[Window Detective] - Windows UI spy utility for programmers
Here's a very similar class I'm using in one of my projects, mainly for reference in how to handle std::endl:

//// The Epoch Language Project// FUGUE Virtual Machine//// Template class for handling user output.//namespace UI{	//	// This class is designed as essentially a drop-in replacement for std::wcout.	// Since the actual method of writing the messages in the buffer is controlled	// by OutputMessage, the class can easily be repurposed to work with a GUI or	// even a network connection rather than a standard console.	//	class OutputStream	{	// Destruction	public:		//		// Ensure any remaining messages are dumped when going out of scope		//		~OutputStream()		{			Flush();		}	// Stream operations	public:		//		// Flush the buffer to the final output destination		//		OutputStream& Flush()		{			std::wstring outstr = Stream.str();			if(!outstr.empty())			{				OutputMessage(outstr);				Stream.str(L"");			}			return *this;		}	// Stream output operations	public:		//		// Collect a value in the output buffer		//		template<typename T>		OutputStream& operator << (const T& val)		{			Stream << val;			return *this;		}		//		// Perform a standard operation on the output buffer stream.		// This provides support for controls like std::endl.		//		OutputStream& operator << (std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& (__cdecl *ptr)(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >&))		{			(*ptr)(Stream);			return Flush();		}	// Internal storage	protected:		std::wostringstream Stream;	};}


I have the central OutputMessage function simply use WriteConsole, but you can obviously set it up to do just about any type of output you want.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

This topic is closed to new replies.

Advertisement