Connecting Tech Pros Worldwide Help | Site Map
 
 
LinkBack Thread Tools Search this Thread
  #1  
Old May 23rd, 2006, 09:35 PM
Dilip
Guest
 
Posts: n/a
Default Design question on wrapping a logging library


I have a tiny design question to ask.

I am stuck with a home-brewed logging library that has different
methods for logging messages based on severity (like warning, error,
information etc.). Its very primitive, very simple and kind of gets
the job done.

The only drawback, I find myself littering my entire code base like
this:

ostringstream msg;
msg.str("");
msg << "some error code=" << errcode;
LoggingObj->ErrorMsg(msg.str().c_str());

I can probably have just one global ostringstream object and just keep
re-using it. Even then, the remaining 3 statements gets repeated
everywhere. Its bloating my codebase and is an eye-sore to look at!
Ideally I would love to be able to do something like:

wrapperObj << "some message to log. error code=" << err << endl;

where wrapperObj is an instance of a LogWrapper class that implements
the << operator and internally just takes the formatted string and
calls LoggingObj's appropriate method. LogWrapper will also probably
manage the lifetime of the actual logging object.
I haven't figured out how to tell wrapperObj what kind of severity the
message has. In this case I will have to assume that there will be
only one instance of wrapperObj which can be manipulated by multiple
threads, so I probably need a way to insert the severity information
inside the extractor operator itself... something like:

wrapperObj << setSeverity(severityID::warning) << "blah....\n";

I am working under the assumption that since every thread has its own
stack, any local variables inside the function will be local to that
thread.. so I basically want to pass the severity information as some
kind of argument to the extractor operator (along with the message to
be logged of course).

I apologize for the 10,000 feet description of the problem -- I would
appreciate being pointed in the right direction to achieve my goal. I
am fairly proficient in C++ but I don't know a whole lot about C++ I/O
Streams. However I can pick up quite fast if I just knew what I should
start looking for.

For ex: should LogWrapper class derive from ostream or something like
that to inherit the functionality available to cout and friends?

  #2  
Old May 23rd, 2006, 09:45 PM
Victor Bazarov
Guest
 
Posts: n/a
Default Re: Design question on wrapping a logging library

Dilip wrote:[color=blue]
> I have a tiny design question to ask.
>
> I am stuck with a home-brewed logging library that has different
> methods for logging messages based on severity (like warning, error,
> information etc.). Its very primitive, very simple and kind of gets
> the job done.
>
> The only drawback, I find myself littering my entire code base like
> this:
>
> ostringstream msg;
> msg.str("");
> msg << "some error code=" << errcode;
> LoggingObj->ErrorMsg(msg.str().c_str());
>
> I can probably have just one global ostringstream object and just keep
> re-using it. Even then, the remaining 3 statements gets repeated
> everywhere. Its bloating my codebase and is an eye-sore to look at!
> Ideally I would love to be able to do something like:
>
> wrapperObj << "some message to log. error code=" << err << endl;
>
> where wrapperObj is an instance of a LogWrapper class that implements
> the << operator and internally just takes the formatted string and
> calls LoggingObj's appropriate method. LogWrapper will also probably
> manage the lifetime of the actual logging object.
> I haven't figured out how to tell wrapperObj what kind of severity the
> message has. In this case I will have to assume that there will be
> only one instance of wrapperObj which can be manipulated by multiple
> threads, so I probably need a way to insert the severity information
> inside the extractor operator itself... something like:
>
> wrapperObj << setSeverity(severityID::warning) << "blah....\n";
>
> I am working under the assumption that since every thread has its own[/color]

Whatever floats your boat, but C++ doesn't define "thread". <g>
[color=blue]
> stack, any local variables inside the function will be local to that
> thread.. so I basically want to pass the severity information as some
> kind of argument to the extractor operator (along with the message to
> be logged of course).[/color]

Have a 'wrapperObj' _per_thread_ and give it the ability to "flush" its
contents to the real logger (which is a singleton). Then you could
write

myLocalWrapperObj << setSeverity(blah) << "blah...\" << flush;

And do it so that outputting 'flush' is an atomic operation.
[color=blue]
> I apologize for the 10,000 feet description of the problem -- I would
> appreciate being pointed in the right direction to achieve my goal. I
> am fairly proficient in C++ but I don't know a whole lot about C++ I/O
> Streams. However I can pick up quite fast if I just knew what I
> should start looking for.
>
> For ex: should LogWrapper class derive from ostream or something like
> that to inherit the functionality available to cout and friends?[/color]

No, it doesn't have to. Just define operator << for it.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask


  #3  
Old May 23rd, 2006, 10:05 PM
Dilip
Guest
 
Posts: n/a
Default Re: Design question on wrapping a logging library

Victor Bazarov wrote:[color=blue]
> Dilip wrote:[color=green]
> > I have a tiny design question to ask.
> >
> > I am stuck with a home-brewed logging library that has different
> > methods for logging messages based on severity (like warning, error,
> > information etc.). Its very primitive, very simple and kind of gets
> > the job done.
> >
> > The only drawback, I find myself littering my entire code base like
> > this:
> >
> > ostringstream msg;
> > msg.str("");
> > msg << "some error code=" << errcode;
> > LoggingObj->ErrorMsg(msg.str().c_str());
> >
> > I can probably have just one global ostringstream object and just keep
> > re-using it. Even then, the remaining 3 statements gets repeated
> > everywhere. Its bloating my codebase and is an eye-sore to look at!
> > Ideally I would love to be able to do something like:
> >
> > wrapperObj << "some message to log. error code=" << err << endl;
> >
> > where wrapperObj is an instance of a LogWrapper class that implements
> > the << operator and internally just takes the formatted string and
> > calls LoggingObj's appropriate method. LogWrapper will also probably
> > manage the lifetime of the actual logging object.
> > I haven't figured out how to tell wrapperObj what kind of severity the
> > message has. In this case I will have to assume that there will be
> > only one instance of wrapperObj which can be manipulated by multiple
> > threads, so I probably need a way to insert the severity information
> > inside the extractor operator itself... something like:
> >
> > wrapperObj << setSeverity(severityID::warning) << "blah....\n";
> >
> > I am working under the assumption that since every thread has its own[/color]
>
> Whatever floats your boat, but C++ doesn't define "thread". <g>[/color]

Victor, I have been hanging out in c.l.c++ long enough to know that you
will jump down the throat of anyone who posts OT and things that aren't
covered under the C++ standard to this NG. I brought up that point
only to emphasize the fact I am looking for a way to pass severity info
as an argument to the << operator. thats all.
[color=blue][color=green]
> > stack, any local variables inside the function will be local to that
> > thread.. so I basically want to pass the severity information as some
> > kind of argument to the extractor operator (along with the message to
> > be logged of course).[/color]
>
> Have a 'wrapperObj' _per_thread_ and give it the ability to "flush" its
> contents to the real logger (which is a singleton). Then you could
> write
>
> myLocalWrapperObj << setSeverity(blah) << "blah...\" << flush;[/color]

If I do that, then if the thread branches to many different method
calls during the course of its processing, do I keep passing around the
wrapperObj everywhere? I would like to avoid that. Hence my request
to have just one global localwrapper obj that will just take in all
kinds of info necessary to cause the ultimate logging. Realistically
apart from severity and the actual message, what else will I need
anyway?
[color=blue]
> And do it so that outputting 'flush' is an atomic operation.[/color]

This went completely over my head. Could you clarify please?
[color=blue][color=green]
> > I apologize for the 10,000 feet description of the problem -- I would
> > appreciate being pointed in the right direction to achieve my goal. I
> > am fairly proficient in C++ but I don't know a whole lot about C++ I/O
> > Streams. However I can pick up quite fast if I just knew what I
> > should start looking for.
> >
> > For ex: should LogWrapper class derive from ostream or something like
> > that to inherit the functionality available to cout and friends?[/color]
>
> No, it doesn't have to. Just define operator << for it.[/color]

Would you be able to show me a tiny code snippet? Something I can use
to build on..?

  #4  
Old May 23rd, 2006, 10:55 PM
Victor Bazarov
Guest
 
Posts: n/a
Default Re: Design question on wrapping a logging library

Dilip wrote:[color=blue]
> Victor Bazarov wrote:[color=green]
>> Dilip wrote:[color=darkred]
>>> [..]
>>> stack, any local variables inside the function will be local to that
>>> thread.. so I basically want to pass the severity information as
>>> some kind of argument to the extractor operator (along with the
>>> message to be logged of course).[/color]
>>
>> Have a 'wrapperObj' _per_thread_ and give it the ability to "flush"
>> its contents to the real logger (which is a singleton). Then you
>> could write
>>
>> myLocalWrapperObj << setSeverity(blah) << "blah...\" << flush;[/color][/color]

If you look at my code below, it will be more like

LoggerWrapper() << SEVERITY_WARNING
<< "You have been warned!...\n"
<< LoggerWrapper::flush();

And if you don't care to see things in the log right away, then you
could simply implement the flushing functionality in the destructor,
and use temporary objects. They'll flush as soon as they are killed.
And if you use local objects, they'll flush going out of scope...
[color=blue]
>
> If I do that, then if the thread branches to many different method
> calls during the course of its processing, do I keep passing around
> the wrapperObj everywhere?[/color]

Everywhere -- where? If you create it once, as an automatic object,
then pass it around in the same thread, it should be fine. Or you
can recreate it in each function locally. Or you could use a temporary
object:

MyWrapperClass() << setSeverity(blah) << "blah...
[color=blue]
> I would like to avoid that. Hence my
> request to have just one global localwrapper[/color]

Huh? Global LOCALwrapper? What's that mean?
[color=blue]
> obj that will just take
> in all kinds of info necessary to cause the ultimate logging.
> Realistically apart from severity and the actual message, what else
> will I need anyway?
>[color=green]
>> And do it so that outputting 'flush' is an atomic operation.[/color]
>
> This went completely over my head. Could you clarify please?[/color]

Writing to the global logger has to be portioned, right? And every
write has to be as short as possible not to hold any other logger in
another thread, right? So, if it takes some time to prepare the line
to be output, it needs to be done on thread's own time, and then the
buffer needs to be output to the global logger in a critical section
(although all those things are better discussed in a threading NG).
[color=blue]
>[..][color=green][color=darkred]
>>> For ex: should LogWrapper class derive from ostream or something
>>> like that to inherit the functionality available to cout and
>>> friends?[/color]
>>
>> No, it doesn't have to. Just define operator << for it.[/color]
>
> Would you be able to show me a tiny code snippet? Something I can use
> to build on..?[/color]

template<class T>
LoggerWrapper& operator <<(LoggerWrapper& lw, T const& t);

class LoggerWrapper
{
Logger &globalLogger;
std::string buffer;
Severity severity;

public:
LoggerWrapper(Logger& g)
: globalLogger(g), severity(SEVERITY_INFO) {}

LoggerWrapper& operator << (Severity s) { severity = s; return *this; }

template<class T> friend LoggerWrapper& operator <<(LoggerWrapper&,
T const&);

struct flush {};

void operator << (flush f) {
CRITICAL_SECTION {
globalLogger.writeLine(severity, buffer);
}
buffer.clear();
severity = SEVERITY_INFO;
}
};

template<class T>
LoggerWrapper& operator <<(LoggerWrapper& lw, T const& t)
{
std::ostringstream os;
os << t;
lw.buffer += os.str();
return lw;
}

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask


  #5  
Old May 23rd, 2006, 11:25 PM
Joe Van Dyk
Guest
 
Posts: n/a
Default Re: Design question on wrapping a logging library

Dilip wrote:[color=blue]
> I have a tiny design question to ask.
>
> I am stuck with a home-brewed logging library that has different
> methods for logging messages based on severity (like warning, error,
> information etc.). Its very primitive, very simple and kind of gets
> the job done.
>
> The only drawback, I find myself littering my entire code base like
> this:
>
> ostringstream msg;
> msg.str("");
> msg << "some error code=" << errcode;
> LoggingObj->ErrorMsg(msg.str().c_str());
>
> I can probably have just one global ostringstream object and just keep
> re-using it. Even then, the remaining 3 statements gets repeated
> everywhere. Its bloating my codebase and is an eye-sore to look at!
> Ideally I would love to be able to do something like:
>
> wrapperObj << "some message to log. error code=" << err << endl;[/color]

What's wrong with:

void log(const std::string &message)
{
Logger l; // Where Logger is a singleton wrapper around the actual
// Logging library object (if it's an object)
l.ErrorMsg(message.c_str());
}

Joe
  #6  
Old May 24th, 2006, 03:25 AM
Dilip
Guest
 
Posts: n/a
Default Re: Design question on wrapping a logging library

Victor Bazarov wrote:[color=blue]
>
> LoggerWrapper() << SEVERITY_WARNING
> << "You have been warned!...\n"
> << LoggerWrapper::flush();
>
> And if you don't care to see things in the log right away, then you
> could simply implement the flushing functionality in the destructor,
> and use temporary objects. They'll flush as soon as they are killed.
> And if you use local objects, they'll flush going out of scope...[/color]

Got it! Should that be

LoggerObject(actual_single_logger_obj) << SEVERITY_WARNING << "crazy
dude" << LoggerWrapper::flush();
[color=blue]
> Everywhere -- where? If you create it once, as an automatic object,
> then pass it around in the same thread, it should be fine. Or you
> can recreate it in each function locally. Or you could use a temporary
> object:
>
> MyWrapperClass() << setSeverity(blah) << "blah...[/color]

Understood. Thanks for clarifying.
[color=blue][color=green]
> > I would like to avoid that. Hence my
> > request to have just one global localwrapper[/color]
>
> Huh? Global LOCALwrapper? What's that mean?[/color]

Probably nothing. I didn't come up with that name localwrapper. What
I wanted to say was I could get away with just one instance of the
localWrapper object. Your approach evidently makes better sense.
[color=blue]
> Writing to the global logger has to be portioned, right? And every
> write has to be as short as possible not to hold any other logger in
> another thread, right? So, if it takes some time to prepare the line
> to be output, it needs to be done on thread's own time, and then the
> buffer needs to be output to the global logger in a critical section
> (although all those things are better discussed in a threading NG).[/color]

Gotcha! I have a cool way of handling this. My actual logger class
spins off a thread and posts all logging messages to itself. A message
pump services that thread's message queue and outputs all those
messages to a file... I am oversimplifying but I understand what you
are saying and have got it covered.
[color=blue]
> template<class T>
> LoggerWrapper& operator <<(LoggerWrapper& lw, T const& t);
>
> class LoggerWrapper
> {
> Logger &globalLogger;
> std::string buffer;
> Severity severity;
>
> public:
> LoggerWrapper(Logger& g)
> : globalLogger(g), severity(SEVERITY_INFO) {}
>
> LoggerWrapper& operator << (Severity s) { severity = s; return *this; }
>
> template<class T> friend LoggerWrapper& operator <<(LoggerWrapper&,
> T const&);
>
> struct flush {};
>
> void operator << (flush f) {
> CRITICAL_SECTION {
> globalLogger.writeLine(severity, buffer);
> }
> buffer.clear();
> severity = SEVERITY_INFO;
> }
> };
>
> template<class T>
> LoggerWrapper& operator <<(LoggerWrapper& lw, T const& t)
> {
> std::ostringstream os;
> os << t;
> lw.buffer += os.str();
> return lw;
> }[/color]

Exactly what I needed to get me going. You are da man Victor. Thanks!

 

Bookmarks

Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On

Popular Articles

What is Bytes?

We are a network of experts and professionals in IT and software development that help one another with answers to tough questions and share insights. Get the best answers to your questions from over 205,338 network members.