Sign in to follow this  

Streaming Logger

This topic is 3111 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
@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 :)

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

This topic is 3111 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this