Design question on wrapping a logging library

Discussion in 'C++' started by Dilip, May 23, 2006.

  1. Dilip

    Dilip Guest

    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?
    Dilip, May 23, 2006
    #1
    1. Advertising

  2. Dilip wrote:
    > 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, but C++ doesn't define "thread". <g>

    > 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
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, May 23, 2006
    #2
    1. Advertising

  3. Dilip

    Dilip Guest

    Victor Bazarov wrote:
    > Dilip wrote:
    > > 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, 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.

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

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


    Would you be able to show me a tiny code snippet? Something I can use
    to build on..?
    Dilip, May 23, 2006
    #3
  4. Dilip

    Joe Van Dyk Guest

    Dilip wrote:
    > 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
    Joe Van Dyk, May 23, 2006
    #4
  5. Dilip wrote:
    > Victor Bazarov wrote:
    >> Dilip wrote:
    >>> [..]
    >>> 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?
    >
    >> And do it so that outputting 'flush' is an atomic operation.

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

    >[..]
    >>> 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.

    >
    > 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
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, May 23, 2006
    #5
  6. Dilip

    Dilip Guest

    Victor Bazarov wrote:
    >
    > 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.

    > > I would like to avoid that. Hence my
    > > request to have just one global localwrapper

    >
    > 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!
    Dilip, May 24, 2006
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Roy Smith

    Wrapping a C library in Python

    Roy Smith, Nov 19, 2004, in forum: Python
    Replies:
    13
    Views:
    709
    Mark Asbach
    Nov 22, 2004
  2. Replies:
    0
    Views:
    308
  3. Daniel Pitts
    Replies:
    0
    Views:
    332
    Daniel Pitts
    Jun 14, 2008
  4. Scott Raymond
    Replies:
    4
    Views:
    123
    Devin Mullins
    Dec 23, 2005
  5. Replies:
    0
    Views:
    93
Loading...

Share This Page