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 anyQlogFileGroupinstance.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 customQlogFactoryimplementation.qlog_from_env(prefix)– Auto-configures using theQLOGDIRenvironment 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_startandserver_handshake_completeto identify NAT-traversal delays. - Path migrations – Events like
path_createdandpath_removedreveal multipath activity or dropped routes. - Loss and congestion –
packet_lostandcongestion_evententries indicate bandwidth or buffer misconfigurations. - Transport parameters – The initial
transport_parametersblock shows negotiated settings likemax_idle_timeoutandmax_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::new → create("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
qlogto yourirohdependencies inCargo.tomlto compile tracing support. - Set environment variables: Export
IROH_TEST_QLOG=1andQLOGDIRbefore running your application to activate file generation. - Use QlogFileGroup: Instantiate this helper from
iroh/src/test_utils/qlog.rsto manage trace file creation and naming conventions. - Configure the transport: Apply
qlog_from_env,qlog_from_path, orqlog_factorymethods iniroh/src/endpoint/quic.rsto embed tracing intoQuicTransportConfig. - 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →