Skip to content

Commit 61323d0

Browse files
committed
feat: initial qlog support in iroh
1 parent 51ba699 commit 61323d0

File tree

5 files changed

+232
-2
lines changed

5 files changed

+232
-2
lines changed

Cargo.lock

Lines changed: 80 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

iroh/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ metrics = ["iroh-metrics/metrics", "iroh-relay/metrics", "portmapper/metrics"]
151151
test-utils = ["iroh-relay/test-utils", "iroh-relay/server", "dep:axum"]
152152
discovery-local-network = ["dep:swarm-discovery"]
153153
discovery-pkarr-dht = ["pkarr/dht"]
154+
qlog = ["quinn/qlog"]
154155

155156
[package.metadata.docs.rs]
156157
all-features = true

iroh/src/endpoint.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1333,7 +1333,7 @@ mod tests {
13331333
discovery::static_provider::StaticProvider,
13341334
endpoint::{ConnectOptions, Connection},
13351335
protocol::{AcceptError, ProtocolHandler, Router},
1336-
test_utils::{run_relay_server, run_relay_server_with},
1336+
test_utils::{QlogFileGroup, run_relay_server, run_relay_server_with},
13371337
};
13381338

13391339
const TEST_ALPN: &[u8] = b"n0/iroh/test";
@@ -1363,9 +1363,12 @@ mod tests {
13631363
let server_secret_key = SecretKey::generate(&mut rng);
13641364
let server_peer_id = server_secret_key.public();
13651365

1366+
let qlog = QlogFileGroup::from_env("endpoint_connect_close");
1367+
13661368
// Wait for the endpoint to be started to make sure it's up before clients try to connect
13671369
let ep = Endpoint::empty_builder(RelayMode::Custom(relay_map.clone()))
13681370
.secret_key(server_secret_key)
1371+
.transport_config(qlog.server("server")?)
13691372
.alpns(vec![TEST_ALPN.to_vec()])
13701373
.insecure_skip_relay_cert_verify(true)
13711374
.bind()
@@ -1406,6 +1409,7 @@ mod tests {
14061409
let ep = Endpoint::empty_builder(RelayMode::Custom(relay_map))
14071410
.alpns(vec![TEST_ALPN.to_vec()])
14081411
.insecure_skip_relay_cert_verify(true)
1412+
.transport_config(qlog.client("client")?)
14091413
.bind()
14101414
.await?;
14111415
info!("client connecting");

iroh/src/test_utils.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
//! Internal utilities to support testing.
22
use std::net::Ipv4Addr;
33

4-
pub use dns_and_pkarr_servers::DnsPkarrServer;
54
use iroh_base::RelayUrl;
65
use iroh_relay::{
76
RelayConfig, RelayMap, RelayQuicConfig,
@@ -12,6 +11,10 @@ use iroh_relay::{
1211
};
1312
use tokio::sync::oneshot;
1413

14+
pub use self::{dns_and_pkarr_servers::DnsPkarrServer, qlog::QlogFileGroup};
15+
16+
mod qlog;
17+
1518
/// A drop guard to clean up test infrastructure.
1619
///
1720
/// After dropping the test infrastructure will asynchronously shutdown and release its

iroh/src/test_utils/qlog.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
//! Utils for emitting qlog files from iroh endpoint.
2+
3+
use std::path::{Path, PathBuf};
4+
#[cfg(feature = "qlog")]
5+
use std::time::Instant;
6+
7+
use n0_error::Result;
8+
use quinn::TransportConfig;
9+
#[cfg(feature = "qlog")]
10+
use quinn_proto::{QlogConfig, VantagePointType};
11+
12+
/// Builder to create one or more related qlog configs.
13+
///
14+
/// This struct is available independently of feature flags, but if the "qlog" feature is not enabled
15+
/// it does not do anything.
16+
#[derive(Debug)]
17+
pub struct QlogFileGroup {
18+
#[cfg(feature = "qlog")]
19+
directory: PathBuf,
20+
#[cfg(feature = "qlog")]
21+
title: String,
22+
#[cfg(feature = "qlog")]
23+
start: Instant,
24+
}
25+
26+
impl QlogFileGroup {
27+
/// Creates a new [`QlogFileGroup] that is only enabled if feature flags and environment variables match.
28+
///
29+
/// The qlog files will be written to `CARGO_MANIFEST_DIR/qlog`.
30+
///
31+
/// The [`QlogFileGroup] can be used independent of feature flags, but it will only emit qlog files
32+
/// if the "qlog" feature is enabled and the environment variable IROH_TEST_QLOG is set to 1.
33+
pub fn from_env(title: impl ToString) -> Self {
34+
let directory = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("qlog");
35+
Self::new(directory, title)
36+
}
37+
38+
/// Creates a new [`QlogFileGroup`] that writes qlog files to the specified directory.
39+
///
40+
/// The [`QlogFileGroup] can be used independent of feature flags, but it will only emit qlog files
41+
/// if the "qlog" feature is enabled and the environment variable IROH_TEST_QLOG is set to 1.
42+
pub fn new(directory: impl AsRef<Path>, title: impl ToString) -> Self {
43+
#[cfg(not(feature = "qlog"))]
44+
let this = {
45+
let _ = directory;
46+
let _ = title;
47+
Self {}
48+
};
49+
50+
#[cfg(feature = "qlog")]
51+
let this = Self {
52+
title: title.to_string(),
53+
directory: directory.as_ref().to_owned(),
54+
start: Instant::now(),
55+
};
56+
57+
this
58+
}
59+
60+
/// Creates a [`TransportConfig`] that emits qlog files with a client vantage point, if enabled.
61+
///
62+
/// If the "qlog" feature is enabled, and the environment varialbe IROH_TEST_QLOG is set to "1",
63+
/// this returns a transport config that writes qlog configs to the configured output directory.
64+
/// Otherwise, a default transport config is returned.
65+
pub fn client(&self, name: impl ToString) -> Result<TransportConfig> {
66+
#[cfg(not(feature = "qlog"))]
67+
let config = {
68+
let _ = name;
69+
TransportConfig::default()
70+
};
71+
72+
#[cfg(feature = "qlog")]
73+
let config = if std::env::var("IROH_TEST_QLOG").ok().as_deref() == Some("1") {
74+
self.transport_config(name.to_string(), VantagePointType::Client)?
75+
} else {
76+
TransportConfig::default()
77+
};
78+
Ok(config)
79+
}
80+
81+
/// Creates a [`TransportConfig`] that emits qlog files with a server vantage point, if enabled.
82+
///
83+
/// If the "qlog" feature is enabled, and the environment varialbe IROH_TEST_QLOG is set to "1",
84+
/// this returns a transport config that writes qlog configs to the configured output directory.
85+
/// Otherwise, a default transport config is returned.
86+
pub fn server(&self, name: impl ToString) -> Result<TransportConfig> {
87+
#[cfg(not(feature = "qlog"))]
88+
let config = {
89+
let _ = name;
90+
TransportConfig::default()
91+
};
92+
93+
#[cfg(feature = "qlog")]
94+
let config = if std::env::var("IROH_TEST_QLOG").ok().as_deref() == Some("1") {
95+
self.transport_config(name.to_string(), VantagePointType::Server)?
96+
} else {
97+
TransportConfig::default()
98+
};
99+
Ok(config)
100+
}
101+
102+
/// Creates a qlog config with a client vantage point.
103+
#[cfg(feature = "qlog")]
104+
pub fn client_config(&self, name: impl ToString) -> Result<QlogConfig> {
105+
self.qlog_config(name.to_string(), VantagePointType::Client)
106+
}
107+
108+
/// Creates a qlog config with a server vantage point.
109+
#[cfg(feature = "qlog")]
110+
pub fn server_config(&self, name: impl ToString) -> Result<QlogConfig> {
111+
self.qlog_config(name.to_string(), VantagePointType::Server)
112+
}
113+
114+
#[cfg(feature = "qlog")]
115+
fn transport_config(
116+
&self,
117+
name: String,
118+
vantage_point: VantagePointType,
119+
) -> Result<TransportConfig> {
120+
let mut transport_config = TransportConfig::default();
121+
let qlog = self.qlog_config(name, vantage_point)?;
122+
transport_config.qlog_stream(qlog.into_stream());
123+
Ok(transport_config)
124+
}
125+
126+
#[cfg(feature = "qlog")]
127+
fn qlog_config(&self, name: String, vantage_point: VantagePointType) -> Result<QlogConfig> {
128+
let full_name = format!("{}.{}", self.title, name);
129+
let file_name = format!("{full_name}.qlog");
130+
let file_path = self.directory.join(file_name);
131+
std::fs::create_dir_all(file_path.parent().unwrap())?;
132+
let file = std::fs::File::create(file_path)?;
133+
let writer = std::io::BufWriter::new(file);
134+
135+
let mut qlog = quinn::QlogConfig::default();
136+
qlog.vantage_point(vantage_point, Some(name.clone()))
137+
.start_time(self.start)
138+
.writer(Box::new(writer))
139+
.title(Some(full_name));
140+
Ok(qlog)
141+
}
142+
}

0 commit comments

Comments
 (0)