Logging infrastructure#
SimGear provides a centralised logging infrastructure 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 (including 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. Logging to this file is
done at INFO level by default, to balance verbosity with usefulness of
post-hoc investigation of problems.
Runtime Configuration#
The log-level command allows changing logging settings dynamically. 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 create a simgear::BufferedLogCallback instance to
collect messages of a certain category, and show them in a UI. This is used
for the Scenery Download log found under the File menu, which 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.