123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- This directory holds the first release of the logging system.
- The steps in using the system are:
- 1. Create a message file. Ideally it should have a file type of ".msg".
- 2. Run it through the message compiler to produce the .h and .cc files.
- 3. Include the .h file in your source code to define message symbols. Also
- include the file logger.h to create loggers and to log error messages.
- The ".cc" file needs to be linked into the program - static initialization
- will add the symbols to the global dictionary.
- Outstanding
- ===========
- * Ability to configure system according to configuration database.
- * Writing of suitable appenders and formatters.
- * Overrididing message at run-time with a localised file.
- * Update the build procedure to create .cc and .h files from the .msg file
- during the build process. (Requires that the message compiler is built first.)
- Message Storage
- ===============
- Each message is identified by a string identifier, e.g. "INVFILNAM", which is
- associated with some text (e.g. "%s is an invalid file name"). These are stored
- in a single std::map, in a class called the Dictionary.
- The message identifier (along with parameters) is passed through the logging
- system to an appender, which uses the identifier to look up the message in
- the dictionary. The message is then formatted and written out.
- Message Files
- =============
- 1) File Contents and Format
- A message file is a file containing message definitions. Typically there will
- be one message file for each component that declares message symbols.
- A example file could be:
- -- BEGIN --
- # Example message file
- # $ID:$
- $PREFIX TEST_
- TEST1 message %s is much too large
- + This message is a test for the general message code
- UNKNOWN unknown message
- + Issued when the message is unknown.
- -- END --
- Point to note:
- * Leading and trailing space are trimmed from the line.
- * Blank lines are ignored
- * Lines starting with "#" are comments are are ignored.
- * Lines starting $ are directives. At present, the only directive recognised
- is $PREFIX, which has one argument: the string used to prefix symbols. If
- there is no facility directive, there is no prefix to the symbols. (Prefixes
- are explained below.)
- * Lines starting + indicate an explanation for the preceding message. These
- are processed by a separate program and used to generate an error messages
- manual. However they are treated like comments here.
- * Message lines. These comprise a symbol name and a message, which may
- include one or more instances of the "%s" the C-style substitution string.
- (The logging system only recognises the "%s" string.)
- 2) Message Compiler
- The message compiler is a program built in the src/log/compiler directory. It
- processes the message file to produce two files:
- 1) A C++ header file (called <message-file-name>.h) that holds lines of the
- form:
- namespace {
- isc::log::MessageID PREFIX_IDENTIFIER = "IDENTIFIER";
- :
- }
- The symbols define the keys in the global message dictionary. At present
- they are defined as std::strings, but a future implementation could redefine
- them as numeric values.
- The "PREFIX_" part of the symbol name is the string defined in the $PREFIX
- the argument to the directive. So "$PREFIX MSG_" would prefix the identifer
- ABC with "MSG_" to give the symbol MSG_ABC. Similarly "$PREFIX E" would
- prefix it with "E" to give the symbol EABC.
- 2) A C++ source file (called <message-file-name>.cpp) that holds the code to
- insert the symbols and messages into the map.
- This file declares an array of identifiers/messages in the form:
- namespace {
- const char* values[] = {
- identifier1, text1,
- identifier2, text2,
- :
- NULL
- };
- }
- (A more complex structure to group identifiers and their messages could be
- imposed, but as the array is generated by code and will be read by code,
- it is not needed.)
- It then declares an object that will add information to the global dictionary:
- MessageInitializer <message-file-name>_<time>(values);
- (Declaring the object as "static" or in the anonymous namespace runs the risk
- of it being optimised away when the module is compiled with optimisation. But
- giving it a standard name would cause a clash when multiple files are used,
- hence an attempt at disambiguation.)
- The constructor of the MessageInitializer object retrieves the singleton
- global Dictionary object (created using standard methods to avoid the
- "static initialization fiasco") and adds each identifier and text to it.
- A check is made as each is added; if the identifier already exists, it is
- added to "overflow" vector; the vector is printed to the main logging output
- when logging is finally enabled (to indicate a programming error).
- Using the Logging
- =================
- To use the current version of the logging:
- 1. Build message header file and source file as describe above.
- 2. In the code that needs to do logging, declare a logger with a given name,
- e.g.
- #include <log/logger.h>
- :
- isc::log::Logger logger("myname"); // "myname" can be anything
- 3. Issue logging calls using methods on logger, e.g.
- logger.error(DPS_NSTIMEOUT, "isc.org");
- (where, in the example above we might have defined the symbol in the message
- file with something along the lines of:
- $PREFIX DPS_
- :
- NSTIMEOUT queries to all nameservers for %s have timed out
- As noted above, presently the only logging is to the console using the default
- log4cxx format (which is somewhat awkward to read).
- Notes
- =====
- The message compiler is written in C++ (instead of Python) because it contains
- a component that reads the message file. This component is used in both the
- message compiler and the server; in the server it is used when the server
- starts up (or when triggered by a command) to read in a message file to
- overwrite the internal dictionary. Writing it in C++ means there is only
- one piece of code that does this functionality.
|