README 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. This directory holds the first release of the logging system.
  2. Basic Ideas
  3. ===========
  4. The BIND-10 logging system merges two ideas:
  5. * A hierarchical logging system similar to that used in Java (i.e. log4j)
  6. * Separation of message use from message text
  7. Hierarchical Logging System
  8. ===========================
  9. When a program writes a message to the logging system, it does so using an
  10. instance of the Logger class. As well as performing the write of the message,
  11. the logger identifies the source of the message: different sources can write
  12. to different destinations and can log different severities of messages.
  13. For example, the "cache" logger could write messages of DEBUG severity or
  14. above to a file while all other components write messages of "INFO" severity
  15. or above to the Syslog file.
  16. The loggers are hierarchical in that each logger is the child of another
  17. logger. The top of the hierarchy is the root logger, which does not have
  18. a parent. The point of the hierarchy is that unless a logger is explicitly
  19. assigned an attribute (such as severity of message being logger), it picks
  20. it up from the parent. (In BIND-10, there is the root logger (named after
  21. the program) and every other logger is a child of that.) So in the example
  22. above, the INFO/Syslog attributes could be associated with the root logger
  23. while the DEBUG/file attributes are associated with the "cache" logger.
  24. Separation of Messages Use from Message Text
  25. ============================================
  26. By separating the use of the message from the text associated with this -
  27. in essence, defining message text in an external file - it is possible to
  28. replace the supplied text of the messages with a local language version.
  29. Each message is identified by an identifier e.g. "LOG_WRITE_ERROR".
  30. Within the program, this is the symbol passed to the logging system.
  31. The logger system uses the symbol as an index into a dictionary to
  32. retrieve the message associated with it (e.g. "unable to open %s for
  33. input"). It then substitutes any message parameters (in this example,
  34. the name of the file where the write operation failed) and logs it to
  35. the destination.
  36. In BIND-10, a the default text for each message is linked into the
  37. program. Each program is able to read a locally-defined message file
  38. when it starts, updating the stored definitions with site-specific text.
  39. When the message is logged, the updated text is output. However, the
  40. message identifier is always included in the output so that the origin
  41. of the message can be identified even if the text has been changed.
  42. Using The System
  43. ================
  44. The steps in using the system are:
  45. 1. Create a message file. This defines messages by an identification - a
  46. mnemonic for the message, the convention being that these are a few
  47. words separated by underscores - and text that explains the message in
  48. more detail. The file is described in more detail below.
  49. Ideally the file should have a file type of ".mes".
  50. 2. Run it through the message compiler to produce the .h and .cc files. This
  51. step should be included in the build process. (For an example of how to
  52. do this, see src/lib/nsas/Makefile.am.) During development though, the
  53. message compiler (found in the "src/lib/log/compiler" directory) will need
  54. to be run manually. The only argument is the name of the message file: it
  55. will produce as output two files in the default directory, having the same
  56. name as the input file but with file types of ".h" and ".cc".
  57. 3. Include the .h file in your source code to define message symbols, and
  58. make sure that the .cc file is compiled and linked into your program -
  59. static initialization will add the symbols to the global dictionary.
  60. 4. Declare loggers in your code and use them to log messages. This is
  61. described in more detail below.
  62. 5. To set the debug level and run-time message file, call initLogger (declared
  63. in logger_support.h) in the main program unit.
  64. Message Files
  65. =============
  66. File Contents and Format
  67. ------------------------
  68. A message file is a file containing message definitions. Typically there
  69. will be one message file for each component that declares message symbols.
  70. An example file could be:
  71. -- BEGIN --
  72. # Example message file
  73. $NAMESPACE isc::log
  74. % LOG_UNRECOGNISED_DIRECTIVE line %1: unrecognised directive '%2'
  75. A line starting with a dollar symbol was found, but the first word on the line
  76. (shown in the message) was not a recognised message compiler directive.
  77. % LOG_WRITE_ERROR error writing to %1: %2
  78. The specified error was encountered by the message compiler when writing to
  79. the named output file.
  80. -- END --
  81. Points to note:
  82. * Leading and trailing space are trimmed from the line. Although the above
  83. example has every line starting at column 1, the lines could be indented
  84. if desired.
  85. * Blank lines are ignored.
  86. * Lines starting with "#" are comments are are ignored. Comments must be on
  87. a line by themselves - inline comments will be interpreted as part of the
  88. text of the line.
  89. * Lines starting $ are directives. At present, just one directive is
  90. recognised:
  91. * $NAMESPACE, which has one argument: the namespace in which the symbols are
  92. created. In the absence of a $NAMESPACE directive, symbols will be put in
  93. the anonymous namespace.
  94. * Message lines. These start with a "%" and are followed by the message
  95. identification and the message text, the latter including zero or more
  96. replacement tokens, e.g.
  97. % LOG_WRITE_ERROR error writing to %1: %2
  98. * There may be zero or more spaces between the leading "%" and the message
  99. identification (which, in the example above, is the string
  100. "LOG_WRITE_ERROR").
  101. * The message identification can be any string of letters, digits and
  102. underscores, but should not start with a digit. The convention adopted
  103. in BIND 10 is for the first component (before the first underscore) to be
  104. a string indicating the origin of the message, and the remainder to
  105. describe the message. So in the example above, the LOG_ indicates that
  106. the error originated from the logging library and the "WRITE_ERROR"
  107. indicates that there was a problem in a write operation.
  108. * The rest of the line - from the first non-space character to the
  109. last non- space character - is taken exactly for the text
  110. of the message. There are no restrictions on what characters may
  111. be in this text, other than they be printable. (This means that
  112. both single-quote (') and double-quote (") characters are allowed.)
  113. The message text may include replacement tokens (the strings "%1",
  114. "%2" etc.). When a message is logged, these are replaced with the
  115. arguments passed to the logging call: %1 refers to the first argument,
  116. %2 to the second etc. Within the message text, the placeholders
  117. can appear in any order and placeholders can be repeated. Otherwise,
  118. the message is printed unmodified.
  119. * Remaining lines indicate an explanation for the preceding message. These
  120. are intended to be processed by a separate program and used to generate
  121. an error messages manual. They are ignored by the message compiler.
  122. Message Compiler
  123. ----------------
  124. The message compiler is a program built in the src/log/compiler directory.
  125. It is invoked by the command:
  126. message [-h] [-v] -p] <message-file>
  127. ("-v" prints the version number and exits; "-h" prints brief help text.) The
  128. compiler produces source files for C++ and Python.
  129. C++ Files
  130. ---------
  131. Without the "-p" option, the message compiler processes the message file
  132. to produce two files:
  133. 1) A C++ header file (called <message-file-name>.h) that holds lines of
  134. the form:
  135. namespace <namespace> {
  136. extern const isc::log::MessageID LOG_WRITE_ERROR;
  137. :
  138. }
  139. The symbols define the keys in the global message dictionary, with the
  140. namespace enclosing the symbols set by the $NAMESPACE directive.
  141. (This is the reason for the restriction on message identifiers - they
  142. have to be valid C++ symbol names.)
  143. 2) A C++ source file (called <message-file-name>.cc) that holds the definitions
  144. of the global symbols and code to insert the symbols and messages into the map.
  145. Symbols are defined to be equal to strings holding the identifier, e.g.
  146. extern const isc::log::MessageID LOG_WRITE_ERROR = "LOG_WRITE_ERROR";
  147. (The implementation allows symbols to be compared. However, use of strings
  148. should not be assumed - a future implementation may change this.)
  149. In addition, the file declares an array of identifiers/messages and an object
  150. to add them to the global dictionary:
  151. namespace {
  152. const char* values[] = {
  153. identifier1, text1,
  154. identifier2, text2,
  155. :
  156. NULL
  157. };
  158. const isc::log::MessageInitializer initializer(values);
  159. }
  160. The constructor of the MessageInitializer object retrieves the singleton
  161. global Dictionary object (created using standard methods to avoid the
  162. "static initialization fiasco") and adds each identifier and text to it.
  163. A check is made as each is added; if the identifier already exists, it is
  164. added to "overflow" vector; the vector is printed to the main logging output
  165. when logging is finally enabled (to indicate a programming error).
  166. Python Files
  167. ------------
  168. If the "-p" option is given, the compiler produces a Python module defining
  169. the messages. The format of this is:
  170. import isc.log
  171. :
  172. LOG_WRITE_ERROR = isc.log.create_message("LOG_WRITE_ERROR",
  173. "error writing to %1 : %2")
  174. (The definition is output on one line - it is split across two lines in this
  175. document for readability.)
  176. The module can be imported into other Python code, and messages logged
  177. in a similar way to C++ using the Python logging library.
  178. Using the Logging - C++
  179. =======================
  180. 1. Build message header file and source file as describe above.
  181. 2. The main program unit must include a call to isc::log::initLogger()
  182. (described in more detail below) to set the logging severity, debug log
  183. level, and external message file:
  184. a) The logging severity is one of the enum defined in logger.h, i.e.
  185. isc::log::DEBUG
  186. isc::log::INFO
  187. isc::log::WARN
  188. isc::log::ERROR
  189. isc::log::FATAL
  190. isc::log::NONE
  191. b) The debug log level is only interpreted when the severity is
  192. DEBUG and is an integer ranging from 0 to 99. 0 should be used
  193. for the highest-level debug messages and 99 for the lowest-level
  194. (and typically more verbose) messages.
  195. c) The external message file. If present, this is the same as a
  196. standard message file, although it should not include any
  197. directives. (A single directive of a particular type will be
  198. ignored; multiple directives will cause the read of the file to
  199. fail with an error.)
  200. The settings remain in effect until the logging configuration is read,
  201. and so provide the default logging during program initialization.
  202. 3. Declare a logger through which the message will be logged.
  203. isc::log::Logger logger("name");
  204. The string passed to the constructor is the name of the logger (it
  205. can be any string) and is used when configuring it. Loggers with
  206. the same name share the same configuration.
  207. 4. Issue logging calls using supplied macros in "log/macros.h", e.g.
  208. LOG_ERROR(logger, LOG_WRITE_ERROR).arg("output.txt");
  209. (The macros are more efficient that calls to the methods on the logger
  210. class: they avoid the overhead of evaluating the parameters to arg()
  211. if the settings are such that the message is not going to be output.)
  212. Using the Logging - Python
  213. ==========================
  214. 1. Build message module as describe above.
  215. 2. The main program unit must include a call to isc.log.init()
  216. (described in more detail below) to set the to set the logging
  217. severity, debug log level, and external message file:
  218. a) The logging severity is one of the strings:
  219. DEBUG
  220. INFO
  221. WARN
  222. ERROR
  223. FATAL
  224. NONE
  225. b) The debug log level is only interpreted when the severity is
  226. DEBUG and is an integer ranging from 0 to 99. 0 should be used
  227. for the highest-level debug messages and 99 for the lowest-level
  228. (and typically more verbose) messages.
  229. c) The external message file. If present, this is the same as a
  230. standard message file, although it should not include any
  231. directives. (Any that are there will be ignored.)
  232. The settings remain in effect until the logging configuration is read,
  233. and so provide the default logging during program initialization.
  234. 3. Declare a logger through which the message will be logged.
  235. isc.log.Logger logger("name")
  236. The string passed to the constructor is the name of the logger (it
  237. can be any string) and is used when configuring it. Loggers with
  238. the same name share the same configuration.
  239. 4. Issue calls to the logging methods:
  240. logger.error(LOG_WRITE_ERROR, "output.txt");
  241. Logging Initialization
  242. ======================
  243. In all cases, if an attempt is made to use a logging method before the logging
  244. has been initialized, the program will terminate with a LoggingNotInitialized
  245. exception.
  246. C++
  247. ---
  248. Logging Initialization is carried out by calling initLogger(). There are two
  249. variants to the call, one for use by production programs and one for use by
  250. unit tests.
  251. Variant #1, Used by Production Programs
  252. ---------------------------------------
  253. void isc::log::initLogger(const std::string& root,
  254. isc::log::Severity severity = isc::log::INFO,
  255. int dbglevel = 0, const char* file = NULL,
  256. bool buffer = false);
  257. This is the call that should be used by production programs:
  258. root
  259. Name of the program (e.g. "b10-auth"). This is also the name of the root
  260. logger and is used when configuring logging.
  261. severity
  262. Default severity that the program will start logging with. Although this may
  263. be overridden when the program obtains its configuration from the configuration
  264. database, this is the severity that it used until then. (This may be set by
  265. a command-line parameter.)
  266. dbglevel
  267. The debug level used if "severity" is set to isc::log::DEBUG.
  268. file
  269. The name of a local message file. This will be read and its definitions used
  270. to replace the compiled-in text of the messages.
  271. buffer
  272. If set to true, initial log messages will be internally buffered, until the
  273. first time a logger specification is processed. This way the program can
  274. use logging before even processing its logging configuration. As soon as any
  275. specification is processed (even an empty one), the buffered log messages will
  276. be flushed according to the specification. Note that if this option is used,
  277. the program SHOULD call one of the LoggerManager's process() calls (if you
  278. are using the built-in logging configuration handling in ModuleCCSession,
  279. this is automatically handled). If the program exits before this is done,
  280. all log messages are dumped in a raw format to stdout (so that no messages
  281. get lost).
  282. Variant #2, Used by Unit Tests
  283. ------------------------------
  284. void isc::log::initLogger()
  285. This is the call that should be used by unit tests. In this variant, all the
  286. options are supplied by environment variables. (It should not be used for
  287. production programs to avoid the chance that the program operation is affected
  288. by inadvertently-defined environment variables.)
  289. The environment variables are:
  290. B10_LOGGER_ROOT
  291. Sets the "root" for the unit test. If not defined, the name "bind10" is used.
  292. B10_LOGGER_SEVERITY
  293. The severity to set for the root logger in the unit test. Valid values are
  294. "DEBUG", "INFO", "WARN", "ERROR", "FATAL" and "NONE". If not defined, "INFO"
  295. is used.
  296. B10_LOGGER_DBGLEVEL
  297. If B10_LOGGER_SEVERITY is set to "DEBUG", the debug level. This can be a
  298. number between 0 and 99, and defaults to 0.
  299. B10_LOGGER_LOCALMSG
  300. If defined, points to a local message file. The default is not to use a local
  301. message file.
  302. B10_LOGGER_DESTINATION
  303. The location to which log message are written. This can be one of:
  304. stdout Message are written to stdout
  305. stderr Messages are written to stderr
  306. syslog[:facility] Messages are written to syslog. If the optional
  307. "facility" is used, the messages are written using
  308. that facility. (This defaults to "local0" if not
  309. specified.)
  310. Anything else Interpreted as the name of a file to which output
  311. is appended. If the file does not exist, a new one
  312. is opened.
  313. In the case of "stdout", "stderr" and "syslog", they must be written exactly
  314. as is - no leading or trailing spaces, and in lower-case.
  315. Python
  316. ------
  317. To be supplied
  318. Severity Guidelines
  319. ===================
  320. When using logging, the question arises, what severity should a message
  321. be logged at? The following is a suggestion - as always, the decision
  322. must be made in the context of which the message is logged.
  323. One thing that should always be borne in mind is whether the logging
  324. could be used as a vector for a DOS attack. For example, if a warning
  325. message is logged every time an invalid packet is received, an attacker
  326. could simply send large numbers of invalid packets. (Of course, warnings
  327. could be disabled (or just warnings for that that particular logger),
  328. but nevertheless the message is an attack vector.)
  329. FATAL
  330. -----
  331. The program has encountered an error that is so severe that it cannot
  332. continue (or there is no point in continuing). When a fatal error
  333. has been logged, the program will usually exit immediately (or shortly
  334. afterwards) after dumping some diagnostic information.
  335. ERROR
  336. -----
  337. Something has happened such that the program can continue but the
  338. results for the current (or future) operations cannot be guaranteed to
  339. be correct, or the results will be correct but the service is impaired.
  340. For example, the program started but attempts to open one or more network
  341. interfaces failed.
  342. WARN
  343. ----
  344. An unusual event happened. Although the program will continue working
  345. normally, the event was sufficiently out of the ordinary to warrant
  346. drawing attention to it. For example, at program start-up a zone was
  347. loaded that contained no resource records,
  348. INFO
  349. ----
  350. A normal but significant event has occurred that should be recorded,
  351. e.g. the program has started or is just about to terminate, a new zone
  352. has been created, etc.
  353. DEBUG
  354. -----
  355. This severity is only enabled on for debugging purposes. A debug level is
  356. associated with debug messages, level 0 (the default) being for high-level
  357. messages and level 99 (the maximum) for the lowest level. How the
  358. messages are distributed between the levels is up to the developer.
  359. So if debugging the NSAS (for example), a level 0 message might record
  360. the creation of a new zone, a level 10 recording a timeout when trying
  361. to get a nameserver address, but a level 50 would record every query for
  362. an address. (And we might add level 70 to record every update of the RTT.)
  363. Note that like severities, levels are cumulative; so if level 25 is
  364. set as the debug level, all debug levels from 0 to 25 will be output.
  365. In fact, it is probably easier to visualise the debug levels as part of
  366. the severity system:
  367. FATAL High
  368. ERROR
  369. WARN
  370. INFO
  371. DEBUG level 0
  372. DEBUG level 1
  373. :
  374. DEBUG level 99 Low
  375. When a particular severity is set, it - and all severities and/or debug
  376. levels above it - will be logged.
  377. To try to ensure that the information from different modules is roughly
  378. comparable for the same debug level, a set of standard debug levels has
  379. been defined for common type of debug output. However, modules are free
  380. to set their own debug levels or define additional ones.
  381. Logging Sources v Logging Severities
  382. ------------------------------------
  383. When logging events, make a distinction between events related to the
  384. server and events related to DNS messages received. Caution needs to
  385. be exercised with the latter as, if the logging is enabled in the normal
  386. course of events, such logging could be a denial of service vector. For
  387. example, suppose that the main authoritative service logger were to
  388. log both zone loading and unloading as INFO and a warning message if
  389. it received an invalid packet. An attacker could make the INFO messages
  390. unusable by flooding the server with malformed packets.
  391. There are two approaches to get round this:
  392. a) Make the logging of packet-dependent events a DEBUG-severity message.
  393. DEBUG is not enabled by default, so these events will not be recorded
  394. unless DEBUG is specifically chosen.
  395. b) Record system-related and packet-related messages via different loggers
  396. (e.g. in the example given, server events could be logged using the
  397. logger "auth" and packet-related events at that level logged using the
  398. logger "pkt-auth".) As the loggers are independent and the severity
  399. levels independent, fine-tuning of what and what is not recorded can
  400. be achieved.
  401. Notes
  402. =====
  403. The message compiler is written in C++ (instead of Python) because it
  404. contains a component that reads the message file. This component is used
  405. in both the message compiler and the server; in the server it is used
  406. when the server starts up (or when triggered by a command) to read in
  407. a message file to overwrite the internal dictionary. Writing it in C++
  408. means there is only one piece of code that does this functionality.