Design question on wrapping a logging library

D

Dilip

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?
 
V

Victor Bazarov

Dilip said:
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

Whatever floats your boat said:
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).

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.
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?

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

V
 
D

Dilip

Victor said:
Whatever floats your boat, but C++ doesn't define "thread". <g>

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.
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;

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?
And do it so that outputting 'flush' is an atomic operation.

This went completely over my head. Could you clarify please?
No, it doesn't have to. Just define operator << for it.

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

Joe Van Dyk

Dilip said:
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;

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
 
V

Victor Bazarov

Dilip said:
Victor said:
Dilip said:
[..]
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).

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;

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...
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?

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...
I would like to avoid that. Hence my
request to have just one global localwrapper

Huh? Global LOCALwrapper? What's that mean?
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?


This went completely over my head. Could you clarify please?

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).
[..]
No, it doesn't have to. Just define operator << for it.

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

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::eek:stringstream os;
os << t;
lw.buffer += os.str();
return lw;
}

V
 
D

Dilip

Victor said:
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...

Got it! Should that be

LoggerObject(actual_single_logger_obj) << SEVERITY_WARNING << "crazy
dude" << LoggerWrapper::flush();
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...

Understood. Thanks for clarifying.
Huh? Global LOCALwrapper? What's that mean?

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.
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).

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.
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::eek:stringstream os;
os << t;
lw.buffer += os.str();
return lw;
}

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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Logging 9
Logging 0
logging design question 0
Logging Question 20
Use of logging module to track TODOs 0
Boost Logging Lib, v2 6
Logging - Best Practices 4
Javascript interview question that I couldn't solve 1

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,825
Latest member
VernonQuy6

Latest Threads

Top