Commit 2ebf994d authored by Stephen Morris's avatar Stephen Morris
Browse files

[trac558] Update message compiler

Change of the message compiler to avoid a possible clash of names
in generated symbols.  Also update documentation.
parent 2ad85af5
......@@ -195,24 +195,37 @@ sortedIdentifiers(MessageDictionary* dictionary) {
/// for "namspace a" and the other for "namespace b".
///
/// This function returns the set of namespace components as a vector of
/// strings.
/// strings. A vector of one element, containing the empty string, is returned
/// if the anonymous namespace is specified.
///
/// \param ns Argument to $NAMESPACE (passed by valid, as we will be modifying
/// \param ns Argument to $NAMESPACE (passed by value, as we will be modifying
/// it.)
vector<string>
splitNamespace(string ns) {
// Namespaces components are separated by double colon characters - convert
// to single colons.
size_t dcolon;
while ((dcolon = ns.find("::")) != string::npos) {
ns.replace(dcolon, 2, ":");
vector<string> components;
if (ns == "::") {
// Unnamed namespace
components.push_back("");
} else {
// Namespaces components are separated by double colon characters -
//convert to single colons.
size_t dcolon;
while ((dcolon = ns.find("::")) != string::npos) {
ns.replace(dcolon, 2, ":");
}
// ... and return the vector of namespace components split on the single
// colon.
components = isc::strutil::tokens(ns, ":");
}
// ... and return the vector of namespace components split on the single
// colon.
return isc::strutil::tokens(ns, ":");
return components;
}
......@@ -222,8 +235,16 @@ splitNamespace(string ns) {
void
writeOpeningNamespace(ostream& output, vector<string>& ns) {
if (!ns.empty()) {
for (int i = 0; i < ns.size(); ++i) {
output << "namespace " << ns[i] << " {\n";
if (ns[0].empty()) {
// Empty namespace
output << "namespace {\n";
} else {
// Output namespaces in correct order
for (int i = 0; i < ns.size(); ++i) {
output << "namespace " << ns[i] << " {\n";
}
}
output << "\n";
}
......@@ -236,8 +257,12 @@ writeOpeningNamespace(ostream& output, vector<string>& ns) {
void
writeClosingNamespace(ostream& output, vector<string>& ns) {
if (!ns.empty()) {
for (int i = ns.size() - 1; i >= 0; --i) {
output << "} // namespace " << ns[i] << "\n";
if (ns[0].empty()) {
output << "} // Unnamed namespace\n";
} else {
for (int i = ns.size() - 1; i >= 0; --i) {
output << "} // namespace " << ns[i] << "\n";
}
}
output << "\n";
}
......@@ -305,13 +330,17 @@ writeHeaderFile(const string& file, const string& prefix, const string& ns,
writeClosingNamespace(hfile, ns_components);
// Now create the reference to the message initializer to ensure that
// it gets run at program startup.
// it gets run at program startup. Note that even the instantiator
// object is given its own unique name - multiple message header files
// might be included in the file, and identical multiple static names
// would clash.
hfile << "namespace isc {\n" <<
"namespace log {\n" <<
"\n" <<
"extern MessageInitializer " << mi_name << ";\n" <<
"static MessageInstantiator m(&" << mi_name << ");\n" <<
"static MessageInstantiator instantiate_" << mi_name << "(\n" <<
" &" << mi_name << ");\n" <<
"\n" <<
"} // namespace log\n" <<
"} // namespace isc\n" <<
......
......@@ -13,19 +13,19 @@ Hierarchical Logging System
When a program writes a message to the logging system, it does so using an
instance of the Logger class. As well as performing the write of the message,
the logger identifies the source of the message: different sources can write
to different destinations and can log different severities of messages. For
example, the "cache" logger could write messages of DEBUG severity or above
to a file while all other components write messages of "INFO" severity or above
to the Syslog file.
to different destinations and can log different severities of messages.
For example, the "cache" logger could write messages of DEBUG severity or
above to a file while all other components write messages of "INFO" severity
or above to the Syslog file.
The loggers are hierarchical in that each logger is the child of another logger.
The top of the hierarchy is the root logger, which does not have a parent. The
point of the hierarchy is that unless a logger is explicitly assigned an
attribute (such as severity of message being logger), it picks it up from the
parent. (In BIND-10, there is the root logger (named after the program) and
every other logger is a child of that.) So in the example above, the
INFO/Syslog attributes could be associated with the root logger while the
DEBUG/file attributes are associated with the "cache" logger.
The loggers are hierarchical in that each logger is the child of another
logger. The top of the hierarchy is the root logger, which does not have
a parent. The point of the hierarchy is that unless a logger is explicitly
assigned an attribute (such as severity of message being logger), it picks
it up from the parent. (In BIND-10, there is the root logger (named after
the program) and every other logger is a child of that.) So in the example
above, the INFO/Syslog attributes could be associated with the root logger
while the DEBUG/file attributes are associated with the "cache" logger.
Separation of Messages Definitions And Text
......@@ -38,10 +38,11 @@ retrieve the message associated with it (e.g. "unable to open %s for input").
substitutes any message parameters (in this example, the string that is an
invalid filename) and logs it to the destination.
In the BIND-10 system, a set of default messages are linked into the program.
At run-time. each program reads a message file, updating the stored definitions;
this updated text is logged. However, to aid support, the message identifier
so in the example above, the message finally logged would be something like:
In the BIND-10 system, a set of default messages are linked into the
program. At run-time. each program reads a message file, updating the
stored definitions; this updated text is logged. However, to aid support,
the message identifier so in the example above, the message finally logged
would be something like:
OPENIN, unable to open a.txt for input
......@@ -57,11 +58,11 @@ The steps in using the system are:
Ideally the file should have a file type of ".msg".
2. Run it through the message compiler to produce the .h and .cc files. It
is intended that this step be included in the build process. However, for
not run the compiler (found in the "compiler" subdirectory) manually. The
only argument is the name of the message file: it will produce as output
two files, having the same name as the input file but with file types of
".h" and ".cc".
is intended that this step be included in the build process. However,
for not run the compiler (found in the "compiler" subdirectory) manually.
The only argument is the name of the message file: it will produce as
output two files, having the same name as the input file but with file
types of ".h" and ".cc".
The compiler is built in the "compiler" subdirectory of the "src/lib/log"
directory.
......@@ -70,13 +71,13 @@ The steps in using the system are:
make sure that the .cc file is compiled and linked into your program -
static initialization will add the symbols to the global dictionary.
4. Declare loggers in your code and use them to log messages. This is described
in more detail below.
4. Declare loggers in your code and use them to log messages. This is
described in more detail below.
5. To set the debug level and run-time message file, call runTimeInit (declared
in logger_support.h) in the main program unit. This is a temporary solution
for Year 2, and will be replaced at a later date, the information coming from
the configuration database.
for Year 2, and will be replaced at a later date, the information coming
from the configuration database.
Message Files
......@@ -84,9 +85,9 @@ Message Files
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. An
example file could be:
A message file is a file containing message definitions. Typically there
will be one message file for each component that declares message symbols.
An example file could be:
-- BEGIN --
......@@ -94,6 +95,7 @@ example file could be:
# $ID:$
$PREFIX TEST_
$NAMESPACE isc::log
TEST1 message %s is much too large
+ This message is a test for the general message code
......@@ -104,8 +106,8 @@ UNKNOWN unknown message
Points to note:
* Leading and trailing space are trimmed from the line. Although the above
exampl,e has every line starting at column 1, the lines could be indented if
desired.
exampl,e has every line starting at column 1, the lines could be indented
if desired.
* Blank lines are ignored.
......@@ -113,16 +115,22 @@ Points to note:
a line by themselves - inline comments will be interpreted as part of the
text of the line.
* 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 $ are directives. At present, two directives are recognised:
* $PREFIX, which has one argument: the string used to prefix symbols. If
absent, there is no prefix to the symbols. (Prefixes are explained below.)
* $NAMESPACE, which has one argument: the namespace in which the symbols are
created. (Specifying the argument as a double colon - i.e. "$NAMESPACE
::" puts the symbol definitions in the unnamed namespace. And not
including a $NAMESPACE directive will result in the symbols note being
put in any namespace.
* Lines starting + indicate an explanation for the preceding message. These
are intended to be processed by a separate program and used to generate an
error messages manual. However they are treated like comments by the message
compiler. As with comments, these must be on a line by themselves; if inline,
the text (including the leading "+") will be interpreted as part of the line.
are intended to be processed by a separate program and used to generate
an error messages manual. However they are treated like comments by the
message compiler. As with comments, these must be on a line by themselves;
if inline, the text (including the leading "+") will be interpreted as
part of the line.
* Message lines. These comprise a symbol name and a message, which may
include zero or more printf-style tokens. Symbol names will be upper-cased
......@@ -132,12 +140,17 @@ Points to note:
Message Compiler
----------------
The message compiler is a program built in the src/log/compiler directory.
It processes the message file to produce two files:
It is invoked by the command:
message [-h] [-v] <message-file>
("-v" prints the version number and exits; "-h" prints brief help text.)
The message compiler 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 {
namespace <namespace> {
isc::log::MessageID PREFIX_IDENTIFIER = "IDENTIFIER";
:
}
......@@ -146,12 +159,17 @@ 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 namespace enclosing the symbols is set by the $NAMESPACE directive.
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. If no $PREFIX is given, no
prefix appears (so the symbol in this example would be ABC).
The header file also includes a couple of lines to ensure that the message
text is included in the final program image.
2) A C++ source file (called <message-file-name>.cc) that holds the code to
insert the symbols and messages into the map.
......@@ -201,8 +219,7 @@ To use the current version of the logging:
isc::log::RootLoggerName("b10-auth");
It should be declared outside an execution unit to allow other statically-
declared loggers to pick it up.
This can be declared inside or outside an execution unit.
2. In the code that needs to do logging, declare a logger with a given name,
e.g.
......@@ -281,7 +298,7 @@ the program started but attempts to open one or more network interfaces failed.
WARN
----
An unusual event happened. Although the program will continue working
normally, the event was sufficiently out of the ordinary to warrant drawings
normally, the event was sufficiently out of the ordinary to warrant drawing
attention to it. For example, at program start-up a zone was loaded that
contained no resource records,
......
// File created from messagedef.mes on Mon Feb 7 11:18:04 2011
// File created from messagedef.mes on Mon Feb 7 11:47:45 2011
#include <cstddef>
#include <log/message_initializer.h>
......@@ -29,7 +29,7 @@ const char* values[] = {
namespace isc {
namespace log {
MessageInitializer messagedef_cc_Mon_Feb__7_11_18_04_2011(values);
MessageInitializer messagedef_cc_Mon_Feb__7_11_47_45_2011(values);
} // namespace log
} // namespace isc
......
// File created from messagedef.mes on Mon Feb 7 11:18:04 2011
// File created from messagedef.mes on Mon Feb 7 11:47:45 2011
#ifndef __MESSAGEDEF_H
#define __MESSAGEDEF_H
......@@ -31,8 +31,9 @@ static const isc::log::MessageID MSG_WRITERR = "WRITERR";
namespace isc {
namespace log {
extern MessageInitializer messagedef_cc_Mon_Feb__7_11_18_04_2011;
static MessageInstantiator m(&messagedef_cc_Mon_Feb__7_11_18_04_2011);
extern MessageInitializer messagedef_cc_Mon_Feb__7_11_47_45_2011;
static MessageInstantiator instantiate_messagedef_cc_Mon_Feb__7_11_47_45_2011(
&messagedef_cc_Mon_Feb__7_11_47_45_2011);
} // namespace log
} // namespace isc
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment