BIND 10 #438: C++ Logging

BIND 10 Development do-not-reply at isc.org
Tue Jan 4 14:48:27 UTC 2011


#438: C++ Logging
--------------------------+-------------------------------------------------
      Reporter:  stephen  |        Owner:  smann    
          Type:  task     |       Status:  reviewing
      Priority:  major    |    Milestone:           
     Component:  logging  |   Resolution:           
      Keywords:           |    Sensitive:  0        
Estimatedhours:  0.0      |        Hours:  0        
      Billable:  1        |   Totalhours:  0        
      Internal:  0        |  
--------------------------+-------------------------------------------------
Changes (by stephen):

  * owner:  stephen => smann


Comment:

 ''Comments quoted are taken from the relevant documents''

 > Is there a trace component (I couldn't find one, but I'm sure that I
 could have missed it)?
 > If there is, perhaps point to it here. If there isn't, I wonder if it
 might not be implemented
 > in a way similar to DEBUG?

 (I assume here that trace means query tracing.)

 Messages are output via "loggers" and a program may have multiple loggers,
 each with a unique name.  In cases where a component corresponds to a
 specific module, all messages within the module are output using a single
 logger named after the module.  Trace messages - which can be generated in
 any part of the program - will use a single global logger (named "trace").

 The problem with using levels is that enabling a specific level enables
 all levels above it (e.g. enabling ERROR also enables FATAL).  If TRACE
 were lower than DEBUG, enabling TRACE would enable all DEBUG messages; if
 higher, then enabling DEBUG would enable all TRACE messages.  (The idea
 that enabling a level enables all levels above it is common in logging
 packages such as log4cxx; following the paradigm allows use of such
 packages.)

 The suggested method is a compromise.  It allows for different levels of
 trace messages, e.g. basic tracing at level INFO, more detailed tracing at
 various DEBUG levels. If some further discrimination is required (e.g.
 enable TRACE only in module XYX), the solution would be to have several
 differently-named trace loggers and enable/disable them as required.


 > Is it obvious how a logger gets the data from the config database?

 At the moment, no.  The configuration data is in JSON format, so use of a
 C++ JSON parser would seem to be mandated.  However this introduces yet
 another package dependency in BIND.  Perhaps a better way would be to use
 an embedded Python module.  The configuration would only need to be read
 at startup or when the configuration changes, so performance is not an
 issue.

 > It isn't clear to me, given the explanation in parentheses, why the
 number of arguments is
 > limited to 4.

 It's a case of possible method of implementation getting into the design
 :-)

 With multiple destinations there is a possibility that different
 destinations want the data in different formats.  So one destination might
 want a message fully expanded with the text of the message.  Another might
 just want the error code and the arguments.  As noted in the text, the
 message is passed around as a single string.  So the idea is that the
 identifier and all arguments are converted to strings and these are
 concatenated (using a suitable separator character).  This single string
 is then passed to the relevant modules which can separate them and, if
 required, look up the message text and do the formatting.

 The conversion of numeric arguments to strings is easily done in C++ using
 inline template methods, e.g.
 {{{
 template <typename T1, typename T2, typename T3>
 void info(T1 code, T2 arg1, T3 arg2) {
    string result = boost::lexical_cast<string>(code) + string('\0') +
                    boost::lexical_cast<string>(arg1) + string('\0') +
                    boost::lexical_cast<string>(arg2);
       :
 }}}
 (with the obvious restriction that the strings may not contain the
 embedded null).

 More signatures can be added but if more information is required, a
 solution would be to define a message with a general argument (e.g.
 "EXCEPTION, exception occurred, reason %s"), formal all the arguments as a
 text string and pass that text string as a single argument.

 The restriction on four arguments is basically down to the number of
 template signatures.  I've suggested a limit of four arguments (which
 means a total of twenty method definitions - four for DEBUG, four for INFO
 etc.) but the number could be higher.

 > But the appender must conform to the Logger API as its input source,
 i.e. the message
 > object. Or am I missing something?

 As the internals of the logger are so heavily dependent on the package
 being used, it seemed easiest to leave appender implementation as package
 dependent - at least for now.  I will re-visit this when I get round to
 implementing an appender; it may be that an API can be defined at that
 point.

 > For what it's worth, I am a big fan of using #defines and numerical
 codes for
 > this sort of thing. static const char* isn't always the right thing to
 do (after
 > all, there are times when it is appropriate to use goto statements) and
 in this
 > case, the use of the internal #define code hides the underlying
 numerical value
 > (which could be organized or not) and the code could be translated into
 a string
 > in something like an enum.

 I prefer the idea of a number as well.  However, in this case I am coming
 round to the idea that a string is the better idea, principally due to the
 need for message replacement.

 If the code (used in the program) is just the message ID string, it is
 easy for the user to provide message replacements.  They provide a file
 mapping message ID to replacement text.  Internally, the message
 ID/message text translation is stored using a std::map, and so lookup is
 easy.

 If we use numerical codes, either the user must associate the replacement
 message with a number (which is somewhat unfriendly), or they still
 associate it with the message ID and we have to provide a way of
 associating the ID with a number. This means that instead of just a simple
 ID to text lookup being required, we require:
  * Given ID access text (to allow the user to do message replacement)
  * Given numerical code access ID (to include the ID in the log message)
  * Given numerical code access text (to include the text in the log
 message)

 It's this additional complexity that is making me prefer the string
 solution.

-- 
Ticket URL: <http://bind10.isc.org/ticket/438#comment:3>
BIND 10 Development <http://bind10.isc.org>
BIND 10 Development


More information about the bind10-tickets mailing list