> This page is for version 5.0 (Latest) (default).
> For other versions, use one of these documentation indexes:
> - 5.0 (Latest) (default): https://docs.nvidia.com/holoscan/sdk-user-guide/5-0-latest/llms.txt
> - ltsb-2.0: https://docs.nvidia.com/holoscan/sdk-user-guide/ltsb-2-0/llms.txt
> - 4.6: https://docs.nvidia.com/holoscan/sdk-user-guide/4-6/llms.txt
> - 4.5: https://docs.nvidia.com/holoscan/sdk-user-guide/4-5/llms.txt
> - 4.4: https://docs.nvidia.com/holoscan/sdk-user-guide/4-4/llms.txt
> - 4.3: https://docs.nvidia.com/holoscan/sdk-user-guide/4-3/llms.txt
> - Archives: https://docs.nvidia.com/holoscan/sdk-user-guide/archives/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/holoscan/sdk-user-guide/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/holoscan/sdk-user-guide/_mcp/server.

Holoscan SDK 5.0 ships a standalone, GXF-independent logger. It keeps the
familiar `HOLOSCAN_LOG_*` macro surface, formats messages with the
[fmt library](https://fmt.dev/latest/syntax.html), 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:

```cpp
#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:

```cpp
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:

| Action                                                             | Automatic enabled (default)                    | Automatic disabled                              |
| ------------------------------------------------------------------ | ---------------------------------------------- | ----------------------------------------------- |
| Call `compile()` without a helper                                  | No automatic compile-result dump               | Same                                            |
| Call `log_diagnostics(plan)`                                       | Prints retained errors and warnings            | Same                                            |
| Trigger an existing startup/runtime failure report without helpers | SDK prints the report                          | SDK suppresses the report                       |
| Explicitly report session diagnostics                              | Prints them; may duplicate an automatic report | Prints them                                     |
| `MemoryBuffer::resize()` cannot release or allocate storage        | SDK reports the failure and returns the Error  | Same Error and ownership; SDK report suppressed |
| Fatal invariant immediately before `std::terminate()`              | Attempts original ERROR, then terminates       | Same; fatal messages bypass this setting        |

```cpp
#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:

```cpp
#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

```bash
# Suppress eligible automatic Core reports; explicit helpers and fatal reports remain.
export HOLOSCAN_AUTOMATIC_DIAGNOSTIC_LOGGING=OFF
```

| Environment value               | Effective policy                                |
| ------------------------------- | ----------------------------------------------- |
| `ON`, in any ASCII letter case  | Enabled, even after API `false`                 |
| `OFF`, in any ASCII letter case | Disabled, even after API `true`                 |
| Unset or empty                  | API preference, initially `true`                |
| Any other nonempty value        | API 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

```cpp
// 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](/holoscan/sdk-user-guide/debugging-and-testing/understanding-failures)
and [Running and stopping](/holoscan/sdk-user-guide/getting-started/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:

```cpp
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:

```bash
export HOLOSCAN_LOG_LEVEL=TRACE
```

> **Warning**
>
> **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:

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

| Preset    | Pattern                                                |
| --------- | ------------------------------------------------------ |
| `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:

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

> **Tip**
>
> 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](/holoscan/sdk-user-guide/getting-started/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](/holoscan/sdk-user-guide/debugging-and-testing/testing).