Logging infrastructure#
SimGear provides a centralised logging infrastrucutre with runtime configuration of logging levels according to categories. Multiple log consumers can be installed, each with their own logging settings.
Log consumers (simgear::LogCallback) run in a dedicated thread, so that their IO doesn’t block
threads creating log entries. This allows for safe disk or network IO in a log
consumer. Messages are passed from arbitrary threads via a thread-safe queue, so the logging consumers
see atomic, time-stamped entries. All consumers process an entry, before the next entry is processed.
Various console consumers are defined, linked to standard output. The exact
behaviour of these is platform-dependent: the POSIX platforms (incuding macOS) simply
output to stderr, while on Windows output to the debug console (OutputDebugStringA) is supported
by setting the environment variable SG_WINDEBUG to something true-ish, and otherwise,
the Windows terminal console is used.
By default FlightGear also logs to a file in FG_HOME, called fgfs.log, which is
rotated on each startup. This file logs by default at level INFO, to balance size of the files,
with usefulness of post-hoc investigation of problems.
Runtime Configuration#
The log-level command allows changing logging settings dynmically. The follows arguments are supported:
- tag
indicates which callback(s) to modify. Default value is
console- all
priority value for the
SG_ALLcategory- <name>
priority value for the category
<name>. This can be repeated for multiple categories.
Additional file logs can be created using the log-to-file command.
The property /sim/log-file-line configures if log entries include the file and source line of the log
entry or not.
Implementation Details#
To avoid locking overhead on the common path, the log consumer thread is stopped and restarted when configuration changes occur. This is effective since configuration changes are very rare, and the logging data is otherwise read-only and hence thread-safe.
The logging frontend is the SG_LOG macro, which calls the logstream::would_log() predicate,
and if this passes, the logstream::log() method. logstream::would_log()
allows fast rejection of non-logged
entries before the expensive formatting incurred by std::ostringstream takes place.
Internally, logstream::would_log() uses the lowest (most permissive) log level of all the
registered callbacks. This means that if no callback is interested in say SG_DEBUG messages,
they will be skipped before the work of building the message occurs.
The current filtering state of a log callback is tracked via the simgear::LogLevels class, which has
a threshold priority for each defined category.
Categories with no explicit priority use the SG_ALL priority automatically.
Startup Logging#
During startup, log messages are buffered in the logging system, so that the full log contents
are available when additional log consumers are registered. Once the main loop reaches initialization
stage 1000, startup logging is disabled via a call to
logstream::setStartupLoggingEnabled(), and the buffer is cleared.
Buffered log callbacks#
Various places created a simgear::BufferedLogCallback to collect messages of a certain category, and show
them in a UI. This is used for the TerraSync log (collects TerraSync and scenery messages),
and for the Nasal console. The callback
handles thread-safety, and the standard filtering mechanism means only the requested subset of messages is collected.
Log Delta#
Todo
Document and discuss the log delta system.