README.txt 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. Logging Messages
  2. Message Storage
  3. ===============
  4. Each message is identified by a string identifier, e.g. "INVFILNAM", which is
  5. associated with some text (e.g. "%s is an invalid file name"). These are stored
  6. in a single std::map, in a class called the Dictionary.
  7. The message identifier (along with parameters) is passed through the logging
  8. system to an appender, which uses the identifier to look up the message in
  9. the dictionary. The message is then formatted and written out.
  10. Message File
  11. ============
  12. A message file is a file containing message definitions. Typically there will
  13. be one message file for each component that declares message symbols.
  14. A example file could
  15. be:
  16. # Example message file
  17. # $ID:$
  18. $PREFIX TEST_
  19. TEST1 message %s is much too large
  20. + This message is a test for the general message code
  21. UNKNOWN unknown message
  22. + Issued when the message is unknown.
  23. Point to note:
  24. * Leading and trailing space are trimmed from the line.
  25. * Blank lines are ignored
  26. * Lines starting with "#" are comments are are ignored.
  27. * Lines starting $ are directives. At present, the only directive recognised
  28. is $PREFIX, which has one argument: the string used to prefix symbols. If
  29. there is no facility directive, there is no prefix to the symbols.
  30. * Lines starting + indicate an explanation for the preceding message. These
  31. are processed by a separate program and used to generate an error messages
  32. manual. However they are treated like comments here.
  33. * Message lines. These comprise a symbol name and a message (which includes
  34. C-style substitution strings).
  35. Message Compiler
  36. ================
  37. The message compiler produces two files:
  38. 1) A C++ header file (called <message-file-name>.h) that holds lines of the
  39. form:
  40. namespace {
  41. const char* PREFIX_IDENTIFIER = "identifier";
  42. :
  43. }
  44. These are just convenience symbols to avoid the need to type in the string in
  45. quotes. PREFIX_ is the string in the $PREFIX directive and is used to avoid
  46. potential clashes with system-defined symbols.
  47. 2) A C++ source file (called <message-file-name>.cpp) that holds the code
  48. to insert the symbols and messages into the map.
  49. This file declares an array of identifiers/messages in the form:
  50. namespace {
  51. const char* messages = {
  52. identifier1, text1,
  53. identifier2, text2,
  54. :
  55. NULL
  56. };
  57. }
  58. (A more complex structure to group identifiers and their messages could be
  59. imposed, but as the array is generated by code and will be read by code,
  60. it is not needed.)
  61. It then declares an object that will add information to the global dictionary:
  62. DictionaryAppender <message-file-name>_<prefix>_<time>(messages);
  63. (Declaring the object as "static" or in the anonymous namespace runs the risk
  64. of it being optimised away when the module is compiled with optimisation. But
  65. giving it a standard name would cause a clash when multiple files are used,
  66. hence an attempt at disambiguation.)
  67. The constructor of the DictionaryAppender object retrieves the singleton
  68. global Dictionary object (created using standard methods to avoid the "static
  69. initialization fiasco") and adds each identifier and text to the member
  70. std::map. A check is made as each is added; if the identifier already exists,
  71. it is added to "overflow" vector; the vector is printed to the main logging
  72. output when logging is finally enabled (to indicate a programming error).
  73. User Message Files
  74. ==================
  75. During logging initialization, a search is made for a user message file in a
  76. specific location. (The specification of the location has yet to be decided -
  77. it will probably be a command-line option.) These messages are read into a
  78. local Dictionary object (which performs the same checks as the global
  79. Dictionary for duplicate messages).
  80. The local Dictionary is then merged with the global Dictionary. In this case
  81. though, warnings are issued where a message does not replace one in the global
  82. Dictionary.
  83. An additional check that could be done is to compare the user message string
  84. with the main message string and to check that they have the same number of
  85. "%s" components. This will avoid potential problems in message formatting. (As
  86. noted in another design document, the intention within logging is to convert
  87. all parameters to strings at the point at which the logging call is made.)
  88. Message Compiler Implementation
  89. ===============================
  90. The fact that user files are read in at run-time implies that the code that
  91. reads the files should be C++. An implication of this is that the message
  92. compiler should be written in C++ (instead of Python, which is probably
  93. better for the task) to avoid two sets of code where message files are parsed.