Crate qlog

source ·
Expand description

The qlog crate is an implementation of the qlog main logging schema, QUIC event definitions, and HTTP/3 and QPACK event definitions. The crate provides a qlog data model that can be used for traces with events. It supports serialization and deserialization but defers logging IO choices to applications.

Serialization operates in either a buffered mode or a streaming mode.

The crate uses Serde for conversion between Rust and JSON.

§Overview

qlog is a hierarchical logging format, with a rough structure of:

  • Log
    • Trace(s)
      • Event(s)

In practice, a single QUIC connection maps to a single Trace file with one or more Events. Applications can decide whether to combine Traces from different connections into the same Log.

§Buffered Traces with standard JSON

A Trace is a single JSON object. It contains metadata such as the VantagePoint of capture and the Configuration, and protocol event data in the Event array.

JSON Traces allow applications to appends events to them before eventually being serialized as a complete JSON object.

§Creating a Trace

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,
);

§Adding events to a Trace

Qlog Event objects are added to qlog::Trace.events.

The following example demonstrates how to log a qlog QUIC packet_sent event containing a single Crypto frame. It constructs the necessary elements of the Event, then appends it to the trace with 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,
    Some(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,
        send_at_time: None,
        trigger: None,
    });

trace.push_event(qlog::events::Event::with_time(0.0, event_data));

§Serializing

The qlog crate has only been tested with serde_json, however other serializer targets might work.

For example, serializing the trace created above:

serde_json::to_string_pretty(&trace).unwrap();

which would generate the following:

{
  "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
          }
        ]
      }
    }
  ]
}

§Streaming Traces with JSON-SEQ

To help support streaming serialization of qlogs, draft-ietf-quic-qlog-main-schema-01 introduced support for RFC 7464 JSON Text Sequences (JSON-SEQ). The qlog crate supports this format and provides utilities that aid streaming.

A TraceSeq contains metadata such as the VantagePoint of capture and the Configuration. However, protocol event data is handled as separate lines containing a record separator character, a serialized Event, and a newline.

§Creating a 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,
);

Create an object with the Write trait:

let mut file = std::fs::File::create("foo.sqlog").unwrap();

Create a QlogStreamer and start serialization to foo.sqlog using start_log():

let mut streamer = qlog::streamer::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::events::EventImportance::Base,
    Box::new(file),
);

streamer.start_log().ok();

§Adding events

Once logging has started you can stream events. Events are written in one step using one of add_event(), add_event_with_instant(), add_event_now(), add_event_data_with_instant(), or add_event_data_now() :


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,
    Some(0),
    Some(0x00000001),
    Some(&scid),
    Some(&dcid),
);

let ping = qlog::events::quic::QuicFrame::Ping {
    length: None,
    payload_length: None,
};
let padding = qlog::events::quic::QuicFrame::Padding {
    length: None,
    payload_length: 1234,
};

let event_data =
    qlog::events::EventData::PacketSent(qlog::events::quic::PacketSent {
        header: pkt_hdr,
        frames: Some(vec![ping, padding].into()),
        is_coalesced: None,
        retry_token: None,
        stateless_reset_token: None,
        supported_versions: None,
        raw: None,
        datagram_id: None,
        send_at_time: None,
        trigger: None,
    });

let event = qlog::events::Event::with_time(0.0, event_data);

streamer.add_event(event).ok();

Once all events have been written, the log can be finalized with finish_log():

streamer.finish_log().ok();

§Serializing

Serialization to JSON occurs as methods on the QlogStreamer are called. No additional steps are required.

Modules§

Structs§

Enums§

Constants§

Type Aliases§