12个重大版本
0.13.0 | 2024年4月15日 |
---|---|
0.12.0 | 2024年2月2日 |
0.11.0 | 2024年1月9日 |
0.10.0 | 2023年11月10日 |
0.1.0 | 2019年12月11日 |
#757 in 网络编程
16,595每月下载量
在3个包中使用(通过 quiche)
135KB
2.5K SLoC
qlog包是qlog 主日志模式、QUIC事件定义和HTTP/3和QPACK事件定义的实现。该包提供了一个可用于具有事件的跟踪的数据模型。它支持序列化和反序列化,但将日志I/O选择推迟到应用程序中。
该包使用Serde在Rust和JSON之间进行转换。
概述
qlog是一种分层日志格式,大致结构为
- 日志
- 跟踪
- 事件
- 跟踪
实际上,一个QUIC连接映射到一个包含一个或多个事件的单独跟踪文件。应用程序可以决定是否将不同连接的跟踪组合到同一个日志中。
带有标准JSON的缓冲跟踪
Trace
是一个单独的JSON对象。它包含元数据,如捕获的 VantagePoint
和 Configuration
,以及 Event
数组中的协议事件数据。
JSON跟踪允许应用程序在最终序列化为完整的JSON对象之前向其中追加事件。
创建跟踪
let mut trace = qlog::Trace::new(
qlog::VantagePoint {
name: Some("Example client".to_string()),
ty: qlog::VantagePointType::Client,
flow: None,
},
Some("Example qlog trace".to_string()),
Some("Example qlog trace description".to_string()),
Some(qlog::Configuration {
time_offset: Some(0.0),
original_uris: None,
}),
None,
);
向跟踪添加事件
Qlog Event
对象添加到 qlog::Trace.events
。
以下示例演示了如何记录包含单个加密帧的 qlog QUIC packet_sent
事件。它构建了 Event
所需的元素,然后使用 push_event()
将其附加到跟踪中。
let scid = [0x7e, 0x37, 0xe4, 0xdc, 0xc6, 0x68, 0x2d, 0xa8];
let dcid = [0x36, 0xce, 0x10, 0x4e, 0xee, 0x50, 0x10, 0x1c];
let pkt_hdr = qlog::events::quic::PacketHeader::new(
qlog::events::quic::PacketType::Initial,
0, // packet_number
None, // flags
None, // token
None, // length
Some(0x00000001), // version
Some(&scid),
Some(&dcid),
);
let frames = vec![qlog::events::quic::QuicFrame::Crypto {
offset: 0,
length: 0,
}];
let raw = qlog::events::RawInfo {
length: Some(1251),
payload_length: Some(1224),
data: None,
};
let event_data =
qlog::events::EventData::PacketSent(qlog::events::quic::PacketSent {
header: pkt_hdr,
frames: Some(frames.into()),
is_coalesced: None,
retry_token: None,
stateless_reset_token: None,
supported_versions: None,
raw: Some(raw),
datagram_id: None,
});
trace.push_event(qlog::events::Event::with_time(0.0, event_data));
序列化
qlog crate 仅与 serde_json
进行过测试,但是其他序列化目标可能也可以工作。
例如,序列化上面创建的跟踪
serde_json::to_string_pretty(&trace).unwrap();
将生成以下内容
{
"vantage_point": {
"name": "Example client",
"type": "client"
},
"title": "Example qlog trace",
"description": "Example qlog trace description",
"configuration": {
"time_offset": 0.0
},
"events": [
{
"time": 0.0,
"name": "transport:packet_sent",
"data": {
"header": {
"packet_type": "initial",
"packet_number": 0,
"version": "1",
"scil": 8,
"dcil": 8,
"scid": "7e37e4dcc6682da8",
"dcid": "36ce104eee50101c"
},
"raw": {
"length": 1251,
"payload_length": 1224
},
"frames": [
{
"frame_type": "crypto",
"offset": 0,
"length": 0
}
]
}
}
]
}
流式传输跟踪 JSON 文本序列(JSON-SEQ)
为了支持 qlog 的流式序列化,draft-ietf-quic-qlog-main-schema-01 引入了对 RFC 7464 JSON 文本序列(JSON-SEQ)的支持。qlog crate 支持此格式并提供帮助流式传输的实用工具。
TraceSeq
包含元数据,如捕获的 VantagePoint
和配置 Configuration
。然而,协议事件数据作为包含记录分隔符字符、序列化的 Event
和换行符的单独行进行处理。
创建 TraceSeq
let mut trace = qlog::TraceSeq::new(
qlog::VantagePoint {
name: Some("Example client".to_string()),
ty: qlog::VantagePointType::Client,
flow: None,
},
Some("Example qlog trace".to_string()),
Some("Example qlog trace description".to_string()),
Some(qlog::Configuration {
time_offset: Some(0.0),
original_uris: None,
}),
None,
);
创建具有 Write
特性的对象
let mut file = std::fs::File::create("foo.sqlog").unwrap();
创建一个 QlogStreamer
并使用 start_log()
方法开始将序列化到 foo.sqlog
let mut streamer = qlog::QlogStreamer::new(
qlog::QLOG_VERSION.to_string(),
Some("Example qlog".to_string()),
Some("Example qlog description".to_string()),
None,
std::time::Instant::now(),
trace,
qlog::EventImportance::Base,
Box::new(file),
);
streamer.start_log().ok();
添加简单事件
一旦开始记录,您就可以流式传输事件。简单事件可以使用 add_event()
方法一步写入
let event_data = qlog::events::EventData::MetricsUpdated(
qlog::events::quic::MetricsUpdated {
min_rtt: Some(1.0),
smoothed_rtt: Some(1.0),
latest_rtt: Some(1.0),
rtt_variance: Some(1.0),
pto_count: Some(1),
congestion_window: Some(1234),
bytes_in_flight: Some(5678),
ssthresh: None,
packets_in_flight: None,
pacing_rate: None,
},
);
let event = qlog::events::Event::with_time(0.0, event_data);
streamer.add_event(event).ok();
添加带有帧的事件
某些事件包含可选的 QUIC 帧数组。如果事件有 Some(Vec<QuicFrame>)
,即使它是空的,流式传输器也会进入一个帧序列化模式,必须在记录其他事件之前完成。
在这个示例中,创建了一个具有空帧数组的 PacketSent
事件,稍后写入帧
let scid = [0x7e, 0x37, 0xe4, 0xdc, 0xc6, 0x68, 0x2d, 0xa8];
let dcid = [0x36, 0xce, 0x10, 0x4e, 0xee, 0x50, 0x10, 0x1c];
let pkt_hdr = qlog::events::quic::PacketHeader::with_type(
qlog::events::quic::PacketType::OneRtt,
0,
Some(0x00000001),
Some(&scid),
Some(&dcid),
);
let event_data =
qlog::events::EventData::PacketSent(qlog::events::quic::PacketSent {
header: pkt_hdr,
frames: Some(vec![]),
is_coalesced: None,
retry_token: None,
stateless_reset_token: None,
supported_versions: None,
raw: None,
datagram_id: None,
};
let event = qlog::events::Event::with_time(0.0, event_data);
streamer.add_event(event).ok();
在这个示例中,QUIC 数据包中包含的帧是 PING 和 PADDING。每个帧都使用 add_frame()
方法写入。使用 finish_frames()
完成帧写入。
let ping = qlog::events::quic::QuicFrame::Ping;
let padding = qlog::events::quic::QuicFrame::Padding;
streamer.add_frame(ping, false).ok();
streamer.add_frame(padding, false).ok();
streamer.finish_frames().ok();
一旦所有事件都已写入,可以使用 finish_log()
将日志最终化。
streamer.finish_log().ok();
序列化
JSON序列化在调用QlogStreamer
的方法时发生。无需额外步骤。
依赖项
~2.4–3.5MB
~69K SLoC