Difference between revisions of "Error Logging System"
Gregor Deed (talk | contribs) |
m |
||
(One intermediate revision by one other user not shown) | |||
Line 1: | Line 1: | ||
{{ViewerArchNav}} | {{ViewerArchNav}} | ||
{{Obsolete|The facilities here are still supported for backwards compatibility, but are not preferred: there are newer and richer macros described in [[Logging System Overview]]}} | |||
This design deals with the error logging facilities in C++. These are the facilities supplied with llerror.h and llerrorstream.h. (llerrorbuffer.h is essentially private). | This design deals with the error logging facilities in C++. These are the facilities supplied with llerror.h and llerrorstream.h. (llerrorbuffer.h is essentially private). | ||
Line 43: | Line 45: | ||
for( i=... ) | for( i=... ) | ||
{ | { | ||
llinfos << i << " "; // llendl missing | llinfos << i << " "; // ERROR! llendl missing | ||
} | } | ||
llinfos << llendl; | llinfos << llendl; |
Latest revision as of 07:55, 4 January 2012
This article is out of date!
- The facilities here are still supported for backwards compatibility, but are not preferred: there are newer and richer macros described in Logging System Overview
This design deals with the error logging facilities in C++. These are the facilities supplied with llerror.h and llerrorstream.h. (llerrorbuffer.h is essentially private).
Goals
The error logging system aims at making logging much more flexible at run time. The decision to log or not is highly configurable, and controllable while running. One should be able to enable or disable messages based on source file, class, function, and level. This enables easier debugging as developers can turn messages on as needed to see what is going on without recompiling or restarting. It will also help in that messages which are no longer useful can be disabled and so not flood the logs.
A secondary goal is the regularization of all messages so that the class and function are printed out automatically at the start of all messages. This greatly aids in both find the source of messages, and in filtering log files.
Of course, if all message logging code is to be compiled in, another goal must be to make skipping suppressed log messages as efficient as possible.
Log Site
Efficiency is handled by having two small static bools generated at each log call site. These keep track of if we should log this call, and if we have made the expensive determination to do so yet. Thus, roughly:
llinfos << "Hi there" << llendl;
will expand via the new #define definitions of llinfos
and llendl
to something like:
{ static bool _cached = false; static bool _should_log = false; if (!_cached) { _cached = true; _should_log = LLErrorSystem::shouldLog(LLErrorSystem::INFO, typeid(LOG_CLASS), __FUNCTION__, __FILE__, __LINE__, &_cached); } if (_should_log) { LLErrorSystem::output() << "Hi there" << std::endl; } }
The function LLErrorSystem::shouldLog
does the work of finding out if this function, file, line and/or level indicates that this message should or should not be logged. It remembers the pointer to the _cached
variable so that LLErrorSystem
can invalidate it (set it to false) if the logging configuration is ever changed (via a live file, or via HTTP).
Note: The actual expansion of those defines is smaller than the code shown, and both smaller and more efficient than the old expansion for those same defines.
Caveat: As a consequence, each message must immediately be terminated with llendl
. For example, the following code would work with normal iostream but not with llinfos:
for( i=... ) { llinfos << i << " "; // ERROR! llendl missing } llinfos << llendl;
Class Modification
In order to capture class information, the following line needs to be added to a class declaration:
CLASS_LOG_TYPE(LLFoo);
which will expand to
typedef LLFoo LOG_CLASS;
When added, logging from that class' functions will automatically get the class and function name prepended, and can be enabled or disabled by class or function. For existing classes, that don't have this, the logging functionality will stay the same, though they still can be enabled and disabled by source file.
Log Control
The logging setup is controlled by a live file. It is an LLSD/XML file that contains the list of what should be logged at which level. An example:
<llsd> <map> <key>default-level</key> <string>INFO</string> <key>print-location</key> <boolean>false</boolean> <key>settings</key <array> <map> <key>level</key><string>INFO</string> <key>classes</key> <array> <string>LLPumpIO</string> <string>LLIOPipe</string> <string>LLBufferArray</string> <string>LLChannelDescriptors</string> </array> </map> <map> <key>level</key><string>DEBUG</string> <key>files</key> <array> <string>llbutton.cpp</string> <string>llcheckboxctrl.cpp</string> <string>llclipboard.cpp</string> <string>llcombobox.cpp</string> <string>llalertdialog.cpp</string> </array> </map> <map> <key>level</key><string>INFO</string> <key>classes</key> <array> <string>LLHTTPNode</string> <string>LLHTTPPipe</string> <string>LLHTTPNodeForFactory</string> </array> </map> </array> </map> </llsd>
Notice that one can have multiple sections with the same level. This facilitates having logical groupings in the control file and enabling them all at once. This convenience introduces ambiguity into the file, since a class or a file can appear more than once, at the same or different levels. The rule shall be last matching entry wins, but class is always considered before file.
This file is live, and so can be edited while code is running, and the logging will change within a few seconds. For the viewer, this file will be in app_settings.