BIND 10 #2445: suppress initial log

BIND 10 Development do-not-reply at isc.org
Thu Dec 6 18:52:12 UTC 2012


#2445: suppress initial log
-------------------------------------+-------------------------------------
            Reporter:  jinmei        |                        Owner:
                Type:  defect        |  jinmei
            Priority:  medium        |                       Status:
           Component:  Boss of BIND  |  reviewing
            Keywords:                |                    Milestone:
           Sensitive:  0             |  Sprint-20121218
         Sub-Project:  Core          |                   Resolution:
Estimated Difficulty:  4             |                 CVSS Scoring:
         Total Hours:  0             |              Defect Severity:  N/A
                                     |  Feature Depending on Ticket:
                                     |          Add Hours to Ticket:  0
                                     |                    Internal?:  0
-------------------------------------+-------------------------------------

Comment (by jinmei):

 Replying to [comment:17 jelte]:

 '''About singleton'''

 > Come to think of it, flushing the logbuffer should to in processEnd().
 If we want to get rid of the singleton storage, what we *could* do is get
 all known loggers in processInit(), and do getAppender("buffer") on them,
 storing them in a vector of `SharedAppenderPtrs`, and flush those in
 processEnd() (instead of using the singleton like my last commit does).

 In that case the vector would have to be a singleton, so it wouldn't
 really be a "solution".  We could still have processInit() return the
 buffer to the caller, (the caller of) which would then pass it
 `processEnd()`, but that doesn't really seem to be clean anyway.  At
 this point I guess I have to give up "making it right" within this
 ticket.  So, let's keep the singleton buffer as is.

 Personally, I'd still like to clean up the singleton/global stuff in
 libb10-log as a future dream.  IMO there are too many global instances
 within this library, making it more difficult to understand/test and
 less predictable/robust.  What I'd envision is to unify all global
 stuff into a single instance of `LoggerManager` class (which will
 effectively be a singleton).  All other singleton/global instances of
 the current implementation, including the buffer added in this branch,
 will become part of the `LoggerManager` instance.  initLogger() will
 create the manager and return a pointer (or shared pointer) to it.
 This will be expected to be destroyed at the end of application's
 main(), at which necessary cleanups take place, including the
 last-resort log flush.  This happens before main() returns, minimizing
 the risk of having destruction fiasco.

 But that's obviously beyond the scope of this ticket, even if it makes
 sense.  And probably something we only dream but not actually work on.

 > > '''changelog'''
 > >
 > > I think we need a changelog entry for this task.
 >
 > ack, proposal:
 >
 > [bug/func?]   jelte
 > The system no longer prints initial log messages to stdout regardless of
 what logging configuration is present, but it temporarily stores any log
 messages until the configuration is processed. If there is no specific
 configuration, or if the configuration cannot be accessed, it will still
 fall back to stdout.
 > Note that there are still a few instances where output is printed, these
 shall be addressed separately.

 This looks okay.

 > > '''log_buffer.h'''

 > > - it directly depends on log4cplus internal.  My first question is
 > >   whether it's a good idea or we should introduce a higher level
 > >   interface for the concept of buffering.  If the answer to the first
 > >   question is yes, maybe it's better to clarify the intent by renaming
 > >   log_buffer.h to log_buffer_impl.h.  Same for .cc
 >
 > dunno, part of the reason this whole set of classes works in a non-
 obvious matter is because of the many pimpls there already :)
 [...]
 > Note, originally I had put both of these classes within an unnamed
 namespace in logger_manager_impl.cc, but then testing was a problem.

 By mentioning _impl.xx, I didn't mean the pimpl idiom, but clarifying that
 the `LogBuffer` class etc are part of internal helpers when we use
 log4cplus as the underlying log library.  e.g, so they won't be
 accidentally used by normal applications.  In that sense, my specific
 suggestion is to rename the files, and introduce a separate namespace
 like "detail", "internal", or "lgo4cplus_helper" under isc::log for
 these.

 > > '''log_buffer.cc'''

 > > - with this implementation, the singleton `LogBuffer` object will be
 > >   destoryed at an unpredictable timing, and its `stored_` member could
 > >   be a non empty (though which may be less likely in practice) vector
 > >   of a third-party type (`InternalLoggingEvent`).  I'm afraid it's
 > >   quite fragile in terms of "static destruction" fiasco.
 >
 > hence the original potentially superfluous copies, and the only-dump-to-
 stdout instead of trying to still use the log4cplus system. But do the
 above changes (store shared ptrs to clones) alleviate your concern here?

 Actually, not fully so.  For example, I'd be afraid whether 'manager'
 refers to a valid object at this point:
 {{{#!cpp
     const log4cplus::LogLevelManager& manager =
         log4cplus::getLogLevelManager();
 }}}
 Maybe log4cplus guarantees it somehow, but it seems to be less
 reliable practice to rely on such subtle details of an external
 library.

 Since this method is expected to be called in a very special context
 (essentially after the program's lifetime), I'd be as conservative as
 possible: avoid using user-defined classes or other/std libraries as
 much as possible, e.g:
 {{{#!cpp
     for (it = stored_.begin(); it != stored_.end(); ++it) {
         printf("Severity=%d [%s]: %s\n", (*it)->getLogLevel(),
                (*it)->getLoggerName().c_str(),
 (*it)->getMessage().c_str());
     }
 }}}

 > > - `LogBuffer::flush`: what if it throws before clear()?
 > >
 >
 > then it is not cleared :) (I considered copying the vector, and removing
 items as they are flushed one by one, but tbh it did not seem worth the
 trouble)

 You could safely move the contents of stored_ to a different local
 vector using vector::swap, ensuring stored_ will be always cleared
 even if flush() throws in the middle of it:

 {{{#!cpp
     LoggerEventPtrList stored_copy;
     stored_.swap(stored_copy);

     LoggerEventPtrList::const_iterator it;
     for (it = stored_copy.begin(); it != stored_copy.end(); ++it) {
         log4cplus::Logger logger =
             log4cplus::Logger::getInstance((*it)->getLoggerName());

         logger.log((*it)->getLogLevel(), (*it)->getMessage());
     }
     flushed_ = true;
 }}}

 This requires a bit more additional code but I don't see any "trouble"
 in it.  See also the next topic.

 > > '''log_buffer_unittest.cc'''

 > > - I don't understand why we need this trick (or what exactly it does):
 > > {{{#!cpp
 > >         // If any log messages are left, we don't care, get rid of
 them,
 > >         // by flushing them to a null appender
 > >         log4cplus::SharedAppenderPtr null_appender(
 > >             new log4cplus::NullAppender());
 > >         logger.removeAllAppenders();
 > >         logger.addAppender(null_appender);
 > >         buffer1.flush();
 > >         buffer2.flush();
 > > }}}
 > >   Maybe some more comments are needed.
 >
 > Added, it simply prevents the 'Foo' messages to be dumped to stdout
 (they need to go *somewhere*)

 Okay, but while trying to understand it, I found one minor and subtle
 issue: if we simply do flush here:
 {{{#!cpp
     ~LogBufferTest() {
         buffer1.flush();
         buffer2.flush();
     }
 }}}
 it would result in extending the vector while flush() iterating
 through that exact vector (because the logger still has the
 `BufferAppender`, which shares the same buffer), leading to a messy
 effect (crash in my case).  If flush() works on a swapped vector, we
 could avoid such disruption.

 > > - Related to the point of directly relying on log4cplus API, I wonder
 > >   whether the original intent is to exclude tests directly depending
 > >   on log4cplus from the unittests.  I think direct unit tests are a
 > >   good practice anyway, but maybe you want to ask Stephen about the
 > >   original plan.
 >
 > I guess this other point ties in more to whether we should forcibly hide
 the type as deep as possible. (but in that case the tests would still need
 to create them somehow)

 I think in this case the issue is to separate things (rather than
 "hide") specific to a particular library, especially if it could be
 switched to something else (like the case for botan vs openssl).  If
 these are cleanly separated, it will be easier to build/test
 everything for a particular set of environment.  But for this ticket I
 don't necessarily insist that.

 > > '''buffer_manager_impl.cc'''

 - processSpecification: It now seems to be back to the original
   version, so it's probably better to make it a full copy of it.  At
   least I guess we should remove this comment:
 {{{#!cpp
     // If this is an 'empty' specification, just set the root logger
 }}}

 > > '''cfgmgr.py'''
 > >
 > > - I don't understand the purpose of the changes (and there's no test
 > >   for these changes).  Please explain and/or add comments.
 > >
 >
 > Added comments (we need to trigger the process() call to flush the
 buffers in case there is no explicit config)

 Okay, and if I understand it correctly, this means cfgmgr will use the
 default of log library, not the default given in logging.spec, right?
 Shouldn't it be the latter?  Even though they are likely to be the
 same, they can be different. (Although, technically this doesn't seem
 to be an issue of this branch, but already exists in the current
 implementation).

 > > '''python log.cc'''
 > >
 > > - supporting keyworded parameters is nice, but we should probably test
 > >   it.
 >
 > added, but only whether they are recognized (and fail on wrong types); I
 don't believe we have any provisioning for checking whether they got
 interpreted as expected...

 It's probably okay in practice.

 '''log/README'''

 - I'd reorder some sentences, like this:
 {{{
 ... Note that if this option is used,
 the program SHOULD call one of the LoggerManager's process() calls (if
 you are using the built-in logging configuration handling in
 ModuleCCSession, this is automatically handled.)  If the program exits
 before this is done, all log messages are dumped in a shortened format
 to stdout (so that no messages get lost).
 }}}

-- 
Ticket URL: <https://bind10.isc.org/ticket/2445#comment:18>
BIND 10 Development <http://bind10.isc.org>
BIND 10 Development


More information about the bind10-tickets mailing list