# How to Debug Issues in Iroh: A Complete Tracing and Diagnostics Guide

> Debug Iroh issues effectively with this guide on tracing and diagnostics. Learn to enable verbose logging and capture detailed network events for quick problem resolution.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: how-to-guide
- Published: 2026-07-12

---

**Enable verbose logging by setting `RUST_LOG=iroh=debug` (or `trace`) before running your binary, and initialize a `tracing_subscriber` in your code to capture detailed spans and events from the QUIC and relay networking stack.**

Iroh is a Rust-based peer-to-peer networking stack maintained by **n0-computer/iroh** that provides QUIC hole-punching, relay servers, and DNS resolution. When you need to debug issues in iroh, the library exposes comprehensive diagnostics through the **tracing** crate, emitting structured events for every connection, packet, and state transition. This guide covers the specific techniques and source locations that reveal what is happening inside the networking stack.

## Understanding Iroh's Tracing Architecture

The foundation of debugging in iroh relies on the **tracing** crate, which instruments the entire codebase with spans and events. Every critical operation—from endpoint creation to relay handling—emits diagnostic output that you can capture and analyze.

Key instrumentation points in the source code include:

- **Connection lifecycle**: The [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs) file uses `tracing::{event, warn}` to report failures and state transitions during connection establishment.
- **Transport layer**: [`iroh/src/endpoint/quic.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/quic.rs) logs warnings with `tracing::warn` for QUIC-specific errors.
- **Relay operations**: [`iroh-relay/src/server/http_server.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/server/http_server.rs) emits detailed spans for HTTP-based relay traffic.
- **DNS resolution**: [`iroh-dns-server/src/main.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/src/main.rs) initializes its own subscriber with `tracing_subscriber::fmt::init()`.

These components feed into a unified subscriber layer that formats output based on your configured log level.

## Command-Line Debugging with RUST_LOG

The simplest method to debug issues in iroh involves setting the **RUST_LOG** environment variable before executing your binary or test suite.

Enable basic informational logging for all crates:

```bash
RUST_LOG=info cargo run --example echo

```

For granular debugging, use hierarchical filtering to target specific modules while keeping cargo output minimal:

```bash
RUST_LOG=iroh=debug,cargo=info cargo run --example listen

```

To see every trace event during test execution, including internal state changes:

```bash
RUST_LOG=trace cargo test -- --nocapture

```

The `--nocapture` flag ensures that `tracing` events and `println!` output appear in the terminal, revealing the exact order of network operations.

## Programmatic Configuration for Embedded Applications

When embedding iroh as a library in a larger application, you must initialize the tracing subscriber programmatically. The [`iroh/examples/echo.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/echo.rs) file demonstrates this pattern with `tracing_subscriber::fmt::init()` at the entry point.

Implement custom initialization with environment-based filtering:

```rust
use tracing_subscriber::{fmt, EnvFilter};

fn init_tracing() {
    fmt::Subscriber::builder()
        .with_env_filter(EnvFilter::from_default_env())
        .init();
}

```

After initialization, all spans from [`iroh/src/protocol.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/protocol.rs) and other modules will stream to your configured output, including recorded fields like ALPN identifiers and connection IDs.

## Running Integration Tests with Full Diagnostics

The integration test suite in [`iroh/tests/integration.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/tests/integration.rs) exercises the complete stack including relays, DNS, and QUIC transport. Running these tests with debug logging provides real-world visibility into client-server interactions.

Execute the integration tests with verbose output:

```bash
RUST_LOG=iroh=debug cargo test -p iroh --test integration -- --nocapture

```

This command prints connection IDs, handshake details, and protocol negotiations as the test sets up client and server endpoints, making it invaluable for understanding complex interaction patterns.

## Using n0_tracing_test for Test Assertions

Many library tests utilize the `#[n0_tracing_test::traced_test]` attribute macro defined in [`iroh/src/test_utils.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/test_utils.rs). This macro automatically provisions a temporary subscriber that captures all events during test execution.

You can find usage examples in [`iroh/src/socket/transports/relay/actor.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/transports/relay/actor.rs), where the macro enables assertions about log output during NAT traversal scenarios. This approach is particularly useful when writing unit tests that must verify specific warning or error conditions occur under expected failure modes.

## Common Debugging Scenarios and Solutions

Target these specific modules when troubleshooting particular issues:

- **Connection failures**: Check [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs) for `tracing::warn!("Failure while handling connection")` messages. Use `RUST_LOG=iroh=trace` to see the full connection lifecycle.
- **Relay lookup returning no candidates**: Look for `tracing::info!("home relay found")` in [`iroh/src/protocol.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/protocol.rs) to verify relay discovery. Filter with `RUST_LOG=iroh=debug`.
- **Hole-punching stalls**: Examine `tracing::debug!` messages in [`iroh/src/socket/transports/relay/actor.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/transports/relay/actor.rs) around NAT traversal logic. Use `RUST_LOG=iroh=debug,iroh-relay=trace`.
- **DNS resolution errors**: Inspect the request handling path in [`iroh-dns-server/src/main.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/src/main.rs) with `RUST_LOG=iroh-dns-server=info`.

## Advanced Tips for Interactive Debugging

**Inspect span fields** to verify protocol negotiation. When [`iroh/src/protocol.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/protocol.rs) creates spans with `tracing::Span::current().record("alpn", ...)`, the recorded fields appear in log output, revealing which application protocol is being used (lines 640–650).

**Filter by module** to reduce noise. Instead of enabling trace for all crates, narrow your focus:

```bash
RUST_LOG=iroh::socket=trace cargo run --example your-example

```

This targets only the socket layer in `iroh/src/socket/`, excluding verbose output from dependencies.

**Capture full event streams** in production debugging by combining `tracing-subscriber` with JSON formatting:

```rust
use tracing_subscriber::{fmt, EnvFilter};

fmt::Subscriber::builder()
    .json()
    .with_env_filter(EnvFilter::from_default_env())
    .init();

```

## Summary

- **Enable tracing** via `RUST_LOG=iroh=debug` or initialize `tracing_subscriber::fmt` programmatically in your application entry point.
- **Target specific modules** like `iroh::socket`, `iroh::endpoint`, or `iroh-relay` to filter noise and focus on relevant subsystems.
- **Use integration tests** with `--nocapture` to observe real-world client-server interactions in [`iroh/tests/integration.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/tests/integration.rs).
- **Leverage `n0_tracing_test`** when writing unit tests to assert against specific log events and error conditions.
- **Inspect span fields** in [`iroh/src/protocol.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/protocol.rs) to verify ALPN identifiers and connection metadata during protocol negotiation.

## Frequently Asked Questions

### How do I enable debug logging in a binary that uses the iroh library?

Set the `RUST_LOG` environment variable to `iroh=debug` or `iroh=trace` before running your binary. For embedded applications, call `tracing_subscriber::fmt::init()` at startup to capture all events from the iroh crate according to the source code in [`iroh/examples/echo.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/echo.rs).

### Why can't I see any log output when running cargo test?

By default, Rust's test harness captures stdout and stderr. Add the `--nocapture` flag to your test command (e.g., `cargo test -- --nocapture`) to ensure `tracing` events and print statements appear in your terminal output, as demonstrated in the integration tests.

### What is the difference between using RUST_LOG and the n0_tracing_test macro?

`RUST_LOG` configures the global tracing subscriber for binaries and general debugging, while `#[n0_tracing_test::traced_test]` creates a temporary subscriber scoped to a specific test function, allowing you to write assertions about log output without affecting the global logger state.

### Where should I look in the source code to understand why my connection is failing?

Check [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs) for `tracing::warn` calls that indicate connection lifecycle failures, and examine [`iroh/src/socket/transports/relay/actor.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/transports/relay/actor.rs) for NAT traversal and relay-specific diagnostics that explain hole-punching stalls.