Skip to navigation

Logging

View as Markdown

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>.

You will learn
  • 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 the HOLOSCAN_LOG_LEVEL environment variable
  • How to change the output pattern with holoscan::set_log_pattern() and HOLOSCAN_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:

#include <holoscan/logger/logger.hpp>
int main() {
holoscan::set_log_level(holoscan::LogLevel::INFO);
const int answer = 42;
HOLOSCAN_LOG_INFO("hello from the holoscan logger: answer={}", answer);
return 0;
}

Because logger.hpp includes <fmt/ranges.h>, standard containers such as std::vector and std::array format directly:

std::vector<int> ids{3, 5, 8};
HOLOSCAN_LOG_DEBUG("active ids: {}", ids);

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:

ActionAutomatic enabled (default)Automatic disabled
Call compile() without a helperNo automatic compile-result dumpSame
Call log_diagnostics(plan)Prints retained errors and warningsSame
Trigger an existing startup/runtime failure report without helpersSDK prints the reportSDK suppresses the report
Explicitly report session diagnosticsPrints them; may duplicate an automatic reportPrints them
MemoryBuffer::resize() cannot release or allocate storageSDK reports the failure and returns the ErrorSame Error and ownership; SDK report suppressed
Fatal invariant immediately before std::terminate()Attempts original ERROR, then terminatesSame; fatal messages bypass this setting
#include <holoscan/core/diagnostic.hpp>
// Optional: present ordinary returned/retained failures yourself.
holoscan::set_automatic_diagnostic_logging_enabled(false);
const bool automatic = holoscan::automatic_diagnostic_logging_enabled();

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:

#include <holoscan/core/diagnostic.hpp>
#include <holoscan/memory/memory_buffer.hpp>
holoscan::set_automatic_diagnostic_logging_enabled(false);
holoscan::MemoryBuffer buffer;
// allocator is the application's std::shared_ptr<holoscan::Allocator>.
auto resized = buffer.resize(allocator, 4096, holoscan::MemoryStorageType::kSystem);
if (!resized) {
// Inspect resized.error() and choose whether to display it.
}

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

# Suppress eligible automatic Core reports; explicit helpers and fatal reports remain.
export HOLOSCAN_AUTOMATIC_DIAGNOSTIC_LOGGING=OFF
Environment valueEffective policy
ON, in any ASCII letter caseEnabled, even after API false
OFF, in any ASCII letter caseDisabled, even after API true
Unset or emptyAPI preference, initially true
Any other nonempty valueAPI preference; at most one normal WARN attempt

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

// graph is the application graph, already constructed.
const auto plan = holoscan::compile(graph);
holoscan::log_diagnostics(plan); // Outside !plan.ok(): include accepted-plan warnings.
if (!plan.ok()) return 1;

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:

holoscan::set_log_level(holoscan::LogLevel::WARN);

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:

export HOLOSCAN_LOG_LEVEL=TRACE

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:

holoscan::set_log_pattern("SHORT");

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.

PresetPattern
SHORT[%^%l%$] %v
DEFAULT[%^%l%$] [%s:%#] %v
LONG[%Y-%m-%d %H:%M:%S.%e] [%n] [%^%l%$] [%s:%#] %v
FULL[%Y-%m-%d %H:%M:%S.%e] [%t] [%n] [%^%l%$] [%s:%#] %v

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:

target_compile_definitions(my_app PRIVATE
HOLOSCAN_LOG_ACTIVE_LEVEL=HOLOSCAN_LOG_LEVEL_INFO) # strips TRACE and DEBUG

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 with holoscan::log_level().
  • HOLOSCAN_LOG_LEVEL and HOLOSCAN_LOG_FORMAT environment variables override the in-code level and pattern.
  • holoscan::set_log_pattern() accepts SHORT, DEFAULT, LONG, FULL, or a custom spdlog pattern.
  • Define HOLOSCAN_LOG_ACTIVE_LEVEL at build time to compile out low-severity log calls entirely.

Next: Testing.