Logging System Overview
This document explains how to use the Second Life logging system in the SL Viewer and Simulator, in executable and runtime configuration.
Overview
Enable all developer functionality by including "llerror.h" (in llcommon). Configure runtime settings with the log control file, as described in Runtime control.
To get the maximum utility out of the log system, add the following to the private section of your class declaration:
<cpp>LOG_CLASS(LLFoo);</cpp>
For example:
class LLFoo
{
LOG_CLASS(LLFoo);
public:
...
};
Output
The Viewer saves log output to LL_PATH_APP_SETTINGS/logs/SecondLife.log
, where LL_PATH_APP_SETTINGS is:
%APPDATA%\SecondLife\
on Windows.$HOME/Library/Application Support/SecondLife/
on MacOS.$HOME/.secondlife/
on Linux.
The server sends log output to /var/log/indra.log
.
Logging macros
To selectively enable or disable log messages at runtime, use log message tag strings (a "broad" scope tag and a "narrow" scope tag). For an example, see the <key>tags</key> block in Runtime control.
If you really don't want to tag a message, pass NULL to the macro. However, the overhead of logging a message is small.
Basic macros
Use the base macros as follows:
<cpp>LL_DEBUGS("BroadScopeTag") << "A debug level log message" << LL_ENDL; // This is a macro, not a string stream. The "LL_ENDL" is required. LL_INFOS("BroadScopeTag") << "An info level log message" << LL_ENDL; LL_WARNS("BroadScopeTag") << "A warning level log message" << LL_ENDL; LL_ERRS("BroadScopeTag") << "An error level log message" << LL_ENDL; // LL_ERRS forces a crash of the viewer.</cpp>
The logging macros behave like iostreams. You can appended anything to them that can be appended to an iostream.
Dual tag macros
With dual tag macros, you to bind two string tags to a log message: a "broad" scope tag and a "narrow" scope tag, useful to specify a high granularity and a low granularity tag for a log message. For example, you could specify "AppInit" and "SystemInfo" tags, where "AppInit" is the broad scope tag for the long application initialization process of which the narrow scope tag "SystemInfo" printing is a small portion.
<cpp>LL_DEBUGS2("BroadScopeTag", "NarrowScopeTag") << "A debug level log message" << LL_ENDL; // NOTE: This is a macro, not a string stream. The "LL_ENDL" is required. LL_INFOS2("BroadScopeTag", "NarrowScopeTag") << "An info level log message" << LL_ENDL; LL_WARNS2("BroadScopeTag", "NarrowScopeTag") << "A warning level log message" << LL_ENDL; LL_ERRS2("BroadScopeTag", "NarrowScopeTag") << "An error level log message" << LL_ENDL; // NOTE: LL_ERRS2 force a crash of the viewer.</cpp>
Tag formatting
Format macro tags in "camel case", such as "RenderInit" and "TextureCache".
Deprecated macros
The old "llerrs", "llwarns", "llinfos", "lldebugs", and "llendl" logging macros are deprecated, but still work for backwards compatibility. The new ones are capitalized per our compiler preprocessor directives naming conventions in the Coding Standards.
Logging a repeating message only once
Sometimes log messages will get triggered repeatedly, filling the log file with the same exact message string over and over. To avoid cases like this, use the "ONCE" macros These macros print the message the first time, prepending "ONCE: " to the message string. Subsequently, when it is triggered with the same message it will print only the 10th, 50th, or every 100th time thereafter. Then, it updates you by prepending "ONCE (Nth time seen): " to the log message, where N is the number of times it has been seen.
Note: This works on unique final log messages, so log messages that depend on one or more variables have the final output treated as different messages if any part of the string changes. Thus, you should not use ONCE macros for log messages that will print many times but with different messages, as this would be slightly slower and would slowly fill up a std::map with memory occupying junk.
<cpp>LL_DEBUGS_ONCE("BroadScopeTag") << "A debug level log message" << LL_ENDL; // NOTE: This is a macro, not a string stream. The "LL_ENDL" is required. LL_INFOS_ONCE("BroadScopeTag") << "An info level log message" << LL_ENDL; LL_WARNS_ONCE("BroadScopeTag") << "A warning level log message" << LL_ENDL;
LL_DEBUGS2_ONCE("BroadScopeTag", "NarrowScopeTag") << "A debug level log message" << LL_ENDL; // NOTE: This is a macro, not a string stream. The "LL_ENDL" is required. LL_INFOS2_ONCE("BroadScopeTag", "NarrowScopeTag") << "An info level log message" << LL_ENDL; LL_WARNS2_ONCE("BroadScopeTag", "NarrowScopeTag") << "A warning level log message" << LL_ENDL;</cpp>
For example, the following log message:
<cpp>LL_WARNS_ONCE("Drawable") << "Drawable becomes static with active parent!" << LL_ENDL;</cpp>
Will print the following messages to SecondLife.log
if it kept getting called repeatedly:
WARNING: LLDrawable::makeStatic: ONCE: Drawable becomes static with active parent! WARNING: LLDrawable::makeStatic: ONCE (10th time seen): Drawable becomes static with active parent! WARNING: LLDrawable::makeStatic: ONCE (50th time seen): Drawable becomes static with active parent! WARNING: LLDrawable::makeStatic: ONCE (100th time seen): Drawable becomes static with active parent! WARNING: LLDrawable::makeStatic: ONCE (200th time seen): Drawable becomes static with active parent! ...
Runtime control
Configuration runtime settings with the log control file, an XML file that the application loads immediately
after initialization. First, it tries to load logcontrol-dev.xml
. If that file doesn't exist, then it loads logcontrol.xml
instead. The application checks the file for changes every couple of seconds and reloads it if necessary.
Log control file location:
- Viewer:
app_settings
directory - Server:
/opt/linden/etc/
directory (<branch>/etc/
on Windows).
The following is a sample logcontrol.xml
file that sets the default loggging level to WARN and then conditionally turns on some warnings at INFO level and DEBUG level based on "function name", "class name", "file name", and "tag".
<llsd>
<map>
<!-- default-level can be ALL, DEBUG, INFO, WARN, ERROR, or NONE -->
<key>default-level</key> <string>WARN</string>
<key>print-location</key> <boolean>false</boolean>
<key>settings</key
<array>
<map>
<key>level</key><string>INFO</string>
<key>functions</key>
<array>
<string>LLAgentInfo::handleImageRequest</string>
</array>
<key>classes</key>
<array>
<string>LLBufferArray</string>
</array>
<key>files</key>
<array>
<!-- entries in 'files' match partial pathnames:
everything below indra/ -->
<string>newview/lldrawpoolavatar.cpp</string>
</array>
<key>tags</key>
<array>
<string>AppInit</string>
<string>TextureCache</string>
</array>
</map>
<map>
<key>level</key><string>DEBUG</string>
<key>functions</key>
<array>
</array>
<key>classes</key>
<array>
</array>
<key>files</key>
<array>
<string>llui/llbutton.cpp</string>
<string>llui/llcombobox.cpp</string>
</array>
<key>tags</key>
<array>
<string>RenderInit</string>
</array>
</map>
</array>
</map>
</llsd>
Logging granularity conflict resolution
The logging system decides in the following order what logging granularity to apply to a message:
- Default log level
- BroadScopeTag
- File Name
- Class Name
- Function Name
- NarrowScopeTag
The application uses the highest matching priority level found in the log control file to determine whether to print a particular log message. For example, if a given log message is of a class set in the log control file to print all LL_DEBUGS() and below, but there is an entry for its function name that states to print only LL_WARNS() and below, then the system will print only LL_WARNS() and LL_ERRS().
Continuing a log message after further computation
Use the LL_CONT macro if you need to do computation in the middle of a log message:
LL_DEBUGS("AgentGesture") << "the agent " << agend_id;
switch (f)
{
case FOP_SHRUGS: LL_CONT << "shrugs"; break;
case FOP_TAPS: LL_CONT << "points at " << who; break;
case FOP_SAYS: LL_CONT << "says " << message; break;
}
LL_CONT << " for " << t << " seconds" << LL_ENDL;
Make sure to remember the LL_ENDL at the end and ONLY at the end.
Any computation embedded as above is done only if the message will be logged.
This means that you can also use the macro functionality to conditionally call separate functions only if that granularity of logging is enabled, such as in the following example from llfeaturemanager.cpp
:
LL_DEBUGS("RenderInit") << "After applying mask " << mask.mName;
// Will conditionally call dump only if the above message will be logged, thanks
// to it being wrapped by the LL_DEBUGS and LL_ENDL macros.
dump();
LL_CONT << LL_ENDL;