Logging
Holoscan SDK 5.0 ships a standalone, GXF-independent logger. It keeps the
familiar HOLOSCAN_LOG_* macro surface, formats messages with the
fmt library, and is backed by spdlog
(holoscan::logger::SpdlogLogger) — an implementation detail that may be
swapped for a faster backend without changing the interface. You include it
with #include <holoscan/logger/logger.hpp>.
- How to log messages with the
HOLOSCAN_LOG_*macros and fmt format strings - How to set the runtime log level with
holoscan::set_log_level()and theHOLOSCAN_LOG_LEVELenvironment variable - How to change the output pattern with
holoscan::set_log_pattern()andHOLOSCAN_LOG_FORMAT - How to compile out low-severity log calls with
HOLOSCAN_LOG_ACTIVE_LEVEL
Logging a message
Each severity level has a macro: HOLOSCAN_LOG_TRACE, HOLOSCAN_LOG_DEBUG,
HOLOSCAN_LOG_INFO, HOLOSCAN_LOG_WARN, HOLOSCAN_LOG_ERROR, and
HOLOSCAN_LOG_CRITICAL. The first argument is an fmt format string; the
remaining arguments fill its {} placeholders. The macros automatically
capture the source file, line, and function name via std::source_location.
This is the complete hello_logger example app:
Because logger.hpp includes <fmt/ranges.h>, standard containers such as
std::vector and std::array format directly:
Free-function equivalents exist when a macro is inconvenient (for example, in
a context where you do not want source-location capture): holoscan::log_trace,
log_debug, log_info, log_warn, log_error, and log_critical take the
same fmt format string and arguments but do not record file/line/function.
Explicit diagnostics and automatic reports
Automatic Core warning/error reporting is enabled by default (true).
set_automatic_diagnostic_logging_enabled() controls eligible existing SDK reports.
log_diagnostics() is an explicit caller request and works with either setting.
Normally, there is no reason to call the setter with true at startup.
Assuming no environment override and logger filtering permits output:
The getter returns the effective automatic policy, before normal logger
filtering. Setting true allows future automatic reports; it does not print
saved diagnostics, replay a failure, or enable automatic compile-result dumps.
Already-running sessions sample the setting when they attempt later reports.
The process-wide API is thread-safe and nonthrowing; configure it at startup when practical.
The opt-out covers Core-owned warning/error reports from Graph composition, lifecycle, startup and runtime, memory/CUDA resources, and clocks. It preserves structured diagnostics, caller-supplied sinks, exceptions, failure outcomes, and cleanup. Ordinary DEBUG/TRACE progress and explicit application/operator logging keep their normal logger controls. Other libraries’ logs are independent.
Fatal invariant messages immediately before std::terminate() are outside this
opt-out. They attempt their original ERROR message even with API false or
launch-time OFF; normal logger filtering still applies. Formatting or sink
failure is contained and cannot prevent termination. These paths return no Error
and offer no retained record that log_diagnostics() could replay after abort.
For example, with an existing allocator that can reject the request:
Both allocation rejection and failure to release previous storage follow this
policy. Failed release preserves the existing backing and does not try a new
allocation. A valid launch-time ON override still enables automatic reports.
Environment override
Whitespace and numeric Boolean spellings are not accepted. Invalid-setting feedback is bounded and respects normal WARN filtering. The environment is read once, at the first setter, getter, or automatic-report policy access. Later environment changes have no effect. Explicit helpers do not initialize this policy or generate its invalid-setting warning.
Report retained diagnostics explicitly
The span overload accepts log_diagnostics(records, plan.graph()). Errors
render at ERROR before warnings at WARN, retaining original record indices and
causal links within one 16384-byte text budget (logger prefixes excluded).
Empty records and filtered severities print nothing. Formatting and sink
exceptions are contained. The helpers neither consume diagnostics nor install
future reporting; repeated explicit calls can print the same records again.
Use format_diagnostics() when you want text for your own UI or telemetry.
Existing failed-run summaries retain their ERROR severity, content, and timing
after cleanup and terminal-state publication. Warnings included in that legacy
summary remain part of its ERROR report. To avoid duplicate terminal output,
keep automatic reporting or disable it and explicitly report the retained
session after wait(). Some lower-level logs have no corresponding retained
record: the helper prints the records supplied to it, not a replay of all logs.
These helpers and controls are C++ APIs in SDK 5.0. Python bindings are not provided by this change. See Understanding failures and Running and stopping.
Setting the log level
holoscan::LogLevel defines seven levels, in increasing severity: TRACE,
DEBUG, INFO, WARN, ERROR, CRITICAL, and OFF (which disables
output). Set the global runtime threshold in code:
Read the current level back with holoscan::log_level().
At runtime, the HOLOSCAN_LOG_LEVEL environment variable — one of TRACE,
DEBUG, INFO, WARN, ERROR, CRITICAL, OFF — lets you raise verbosity
without recompiling:
The environment variable wins. When HOLOSCAN_LOG_LEVEL is set, it
overrides any level your application passes to set_log_level(). If a
deployment seems to ignore your in-code level, check the environment first.
With neither set_log_level() nor HOLOSCAN_LOG_LEVEL, the current SDK
backend starts at INFO.
Formatting the output
holoscan::set_log_pattern() controls what each log line looks like. It
accepts a preset name or a custom spdlog pattern string:
The HOLOSCAN_LOG_FORMAT environment variable overrides the in-code pattern
and accepts the presets SHORT, DEFAULT, LONG, FULL, or a custom spdlog
pattern string.
Compile-time filtering
The runtime level decides whether a message is printed; the compile-time
level decides whether the call is compiled at all. Define
HOLOSCAN_LOG_ACTIVE_LEVEL (for example, via CMake’s
target_compile_definitions) to one of the numeric level macros —
HOLOSCAN_LOG_LEVEL_TRACE (0) through HOLOSCAN_LOG_LEVEL_OFF (6) — and any
HOLOSCAN_LOG_* macro below that level expands to a no-op, eliminating even
the argument evaluation:
If left undefined, all levels are compiled in (the default is
HOLOSCAN_LOG_LEVEL_TRACE). The installed SDK never imposes a compile-time
threshold on your code — the policy is caller-owned, so your build settings
alone decide what gets stripped.
For log statements on a latency-critical compute() path, prefer
HOLOSCAN_LOG_TRACE/HOLOSCAN_LOG_DEBUG plus a release-build
HOLOSCAN_LOG_ACTIVE_LEVEL of INFO or higher: the calls vanish entirely
from the release binary instead of paying a runtime level check.
Linking
The logger is a small, separately linkable piece of the SDK. logger.hpp has
a public header-only dependency on fmt, which find_package(holoscan) resolves
for you; the hello_logger example exists precisely to smoke-test this against
an installed SDK. See
Compile and check
for general build setup.
Recap
- Log with
HOLOSCAN_LOG_TRACE/DEBUG/INFO/WARN/ERROR/CRITICAL(...)using fmt{}format strings; file, line, and function are captured automatically. - Set the runtime threshold with
holoscan::set_log_level(LogLevel::...); read it withholoscan::log_level(). HOLOSCAN_LOG_LEVELandHOLOSCAN_LOG_FORMATenvironment variables override the in-code level and pattern.holoscan::set_log_pattern()acceptsSHORT,DEFAULT,LONG,FULL, or a custom spdlog pattern.- Define
HOLOSCAN_LOG_ACTIVE_LEVELat build time to compile out low-severity log calls entirely.
Next: Testing.