How to Debug Connection Issues Using iroh’s Qlog Integration

Enable the qlog Cargo feature, set the IROH_TEST_QLOG=1 and QLOGDIR environment variables, and instantiate a QlogFileGroup to capture detailed QUIC protocol traces that reveal handshake failures, path migrations, and congestion events.

The iroh networking library from n0-computer provides built-in Qlog support that records granular QUIC transport events into standardized JSON files. When you need to debug connection issues using iroh's qlog integration, you can generate protocol-level traces that show exactly when packets are sent, received, or lost, enabling precise diagnosis of stalled or unstable connections.

Enable the Qlog Feature

Qlog support is conditionally compiled via the qlog feature flag. To access the tracing APIs, add the feature to your iroh dependency in Cargo.toml:

iroh = { version = "0.XX", features = ["qlog"] }

The codebase guards Qlog functionality with #[cfg(feature = "qlog")] checks. In iroh/src/endpoint/quic.rs (lines 9‑11), the transport builder conditionally exposes debugging methods, with the full implementation of builder helpers appearing at lines 549‑579.

Configure Environment Variables

Runtime Qlog generation requires two environment variables:

  • IROH_TEST_QLOG=1 – Required to enable trace generation for any QlogFileGroup instance.
  • QLOGDIR – Specifies the output directory for trace files. If unset, no logs are emitted. The directory is created automatically if it does not exist.

The validation logic resides in iroh/src/test_utils/qlog.rs. The QlogFileGroup::create method checks IROH_TEST_QLOG at lines 62‑84, while the internal factory reads QLOGDIR to determine the output path.

Implement Qlog in Your Code

Create a QlogFileGroup

The QlogFileGroup struct (defined in iroh/src/test_utils/qlog.rs, lines 30‑45) manages trace file creation and naming conventions. Instantiate it programmatically or from environment variables:

use iroh::test_utils::QlogFileGroup;

// Explicit path and prefix
let qlog = QlogFileGroup::new("./qlog", "my_connection");

// Or read from QLOGDIR environment variable
let qlog = QlogFileGroup::from_env("my_connection");

Configure the Transport

Inject the Qlog configuration into your QuicTransportConfig using three builder methods available in iroh/src/endpoint/quic.rs (lines 549‑579):

  • qlog_factory(factory) – Supply a custom QlogFactory implementation.
  • qlog_from_env(prefix) – Auto-configures using the QLOGDIR environment variable.
  • qlog_from_path(path, prefix) – Writes traces to a specific directory.

The transfer example in iroh/examples/transfer.rs demonstrates typical usage:

use iroh::endpoint::{Endpoint, QuicTransportConfig};

let transport = QuicTransportConfig::builder()
    .qlog_from_env("transfer")
    .build();

let endpoint = Endpoint::builder()
    .transport_config(transport)
    .bind()?;

Generate and Analyze Traces

Run your application with the required environment variables:

export IROH_TEST_QLOG=1
export QLOGDIR=/tmp/iroh_qlog
cargo run --features qlog --example transfer

This produces timestamped JSON files such as /tmp/iroh_qlog/transfer.client-2023-05-01-12-00-00.qlog. Each file follows the Qlog specification and contains arrays of events including:

  • Handshake stages – Look for client_handshake_start and server_handshake_complete to identify NAT-traversal delays.
  • Path migrations – Events like path_created and path_removed reveal multipath activity or dropped routes.
  • Loss and congestionpacket_lost and congestion_event entries indicate bandwidth or buffer misconfigurations.
  • Transport parameters – The initial transport_parameters block shows negotiated settings like max_idle_timeout and max_concurrent_multipath_paths.

Analyze these files using the Qlog viewer or cattrace to visualize the connection lifecycle.

Code Examples

Minimal Qlog-Enabled Client

use iroh::endpoint::{Endpoint, QuicTransportConfig};
use iroh::test_utils::QlogFileGroup;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    // Initialize Qlog group writing to ./qlog
    let qlog = QlogFileGroup::new("./qlog", "demo");
    
    // Create transport configuration with Qlog enabled
    let transport = qlog.create("client")?;
    
    // Build and bind endpoint
    let endpoint = Endpoint::builder()
        .transport_config(transport)
        .bind()?;
    
    // Connection will now generate trace files
    let _conn = endpoint.connect("iroh://example.com:443".parse()?)?;
    println!("Connection traced to ./qlog");
    Ok(())
}

This pattern—QlogFileGroup::newcreate("client")Endpoint::builder().transport_config(...)—is the standard approach for capturing client-side traces.

Environment-Based Configuration (No Code Changes)

For existing applications that already call qlog_from_env, simply set variables before running:

export IROH_TEST_QLOG=1
export QLOGDIR=./my_qlog
cargo run --features qlog --bin my_app

The application automatically picks up the configuration via qlog_from_env as implemented in iroh/src/endpoint/quic.rs.

Custom Qlog Factory (Advanced)

For custom trace handling, implement the QlogFactory trait:

use std::sync::Arc;
use iroh::endpoint::{QuicTransportConfig, QlogFactory};

fn custom_factory() -> Arc<dyn QlogFactory> {
    // Example: Upload traces to S3 or filter specific events
    Arc::new(my_qlog::MyQlogFactory::new())
}

let cfg = QuicTransportConfig::builder()
    .qlog_factory(custom_factory())
    .build();

Summary

  • Enable the feature: Add qlog to your iroh dependencies in Cargo.toml to compile tracing support.
  • Set environment variables: Export IROH_TEST_QLOG=1 and QLOGDIR before running your application to activate file generation.
  • Use QlogFileGroup: Instantiate this helper from iroh/src/test_utils/qlog.rs to manage trace file creation and naming conventions.
  • Configure the transport: Apply qlog_from_env, qlog_from_path, or qlog_factory methods in iroh/src/endpoint/quic.rs to embed tracing into QuicTransportConfig.
  • Analyze traces: Examine generated JSON files for handshake stages, path migrations, and congestion events to identify connection failures.

Frequently Asked Questions

What file format does iroh use for Qlog traces?

iroh generates standard Qlog files containing JSON arrays of QUIC protocol events. These follow the Qlog specification and can be viewed with compatible tools like the Qlog viewer or cattrace. Each file includes timestamps, packet numbers, and transport parameters exchanged during the connection lifecycle.

Why are no Qlog files being generated despite enabling the feature?

First, verify that IROH_TEST_QLOG=1 is set in your environment, as the QlogFileGroup::create method (in iroh/src/test_utils/qlog.rs, lines 62‑84) explicitly checks this variable before creating writers. Second, confirm that QLOGDIR points to a valid path with write permissions. Finally, ensure your code actually uses a QlogFileGroup or calls qlog_from_env/qlog_from_path when building the QuicTransportConfig; simply enabling the feature does not automatically trace all connections.

Can I use Qlog tracing in production environments?

While technically possible by enabling the feature and setting the environment variables, Qlog tracing generates significant I/O overhead by writing detailed JSON event streams to disk. The implementation in iroh/src/endpoint/quic.rs is designed for debugging and testing scenarios. For production use, consider implementing a custom QlogFactory that filters events or streams to remote aggregation services rather than local files.

How do I interpret path migration events in the Qlog output?

Look for path_created and path_removed entries in the trace file. According to the iroh implementation, these events fire when the QUIC stack establishes or tears down network paths during multipath operation. If you see rapid successions of path_created followed immediately by path_removed, this indicates unstable network conditions or NAT rebinding issues that may cause connection stalls.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →