Skip to main content

qlog_rs/
logfile.rs

1use std::collections::HashMap;
2
3use chrono::{DateTime, FixedOffset};
4use serde::Serialize;
5use serde_with::skip_serializing_none;
6
7use crate::util::{is_empty_or_none, PathId, GroupId};
8
9#[cfg(feature = "moq-transfork")]
10use crate::moq_transfork::data::MOQ_VERSION_STRING;
11
12#[cfg(feature = "quic-10")]
13use crate::quic_10::data::QUIC_10_VERSION_STRING;
14
15#[derive(Serialize)]
16pub struct QlogFileSeq {
17	#[serde(flatten)]
18	log_file_details: LogFile,
19	trace: TraceSeq
20}
21
22impl QlogFileSeq {
23	pub fn new(log_file_details: LogFile, trace: TraceSeq) -> QlogFileSeq {
24		QlogFileSeq { log_file_details, trace }
25	}
26}
27
28#[skip_serializing_none]
29#[derive(Serialize)]
30pub struct LogFile {
31	/// Identifies the concrete log file schema
32	file_schema: String,
33	/// Indicates the serialization format using a media type
34	serialization_format: String,
35	title: Option<String>,
36	description: Option<String>
37}
38
39impl LogFile {
40	// TODO: Add support for other file schemas
41	// TODO: Add support for other serialization formats
42	pub fn new(title: Option<String>, description: Option<String>) -> LogFile {
43		LogFile {
44			file_schema: "urn:ietf:params:qlog:file:sequential".to_string(),
45			serialization_format: "application/qlog+json-seq".to_string(),
46			title,
47			description
48		}
49	}
50}
51
52#[skip_serializing_none]
53#[derive(Serialize)]
54pub struct TraceSeq {
55	title: Option<String>,
56	description: Option<String>,
57	common_fields: Option<CommonFields>,
58	vantage_point: Option<VantagePoint>,
59    /// Identifies concrete event namespaces and their associated types
60	event_schemas: Vec<String>
61}
62
63impl TraceSeq {
64	pub fn new(title: Option<String>, description: Option<String>, common_fields: Option<CommonFields>, vantage_point: Option<VantagePoint>) -> TraceSeq {
65        #[allow(unused_mut)]
66        let mut event_schemas: Vec<String> = Vec::default();
67
68        #[cfg(feature = "moq-transfork")]
69        event_schemas.push(format!("urn:ietf:params:qlog:events:{MOQ_VERSION_STRING}"));
70
71        #[cfg(feature = "quic-10")]
72        event_schemas.push(format!("urn:ietf:params:qlog:events:{QUIC_10_VERSION_STRING}"));
73
74		TraceSeq {
75            title,
76            description,
77            common_fields,
78            vantage_point,
79			event_schemas
80        }
81	}
82}
83
84#[skip_serializing_none]
85#[derive(Serialize)]
86pub struct CommonFields {
87	#[serde(skip_serializing_if = "is_empty_or_none")]
88	path: Option<PathId>,
89	time_format: Option<TimeFormat>,
90	reference_time: Option<ReferenceTime>,
91	group_id: Option<GroupId>,
92	#[serde(flatten)]						// Adds the custom fields directly to CommonFields when serializing
93	custom_fields: HashMap<String, String>
94}
95
96impl CommonFields {
97	pub fn new(path: Option<PathId>, time_format: Option<TimeFormat>, reference_time: Option<ReferenceTime>, group_id: Option<GroupId>, custom_fields: Option<HashMap<String, String>>) -> CommonFields {
98		let custom_fields = custom_fields.unwrap_or_default();
99
100		CommonFields { path, time_format, reference_time, group_id, custom_fields }
101	}
102}
103
104impl Default for CommonFields {
105	fn default() -> Self {
106		Self {
107			path: Some("".to_string()),
108			time_format: Some(TimeFormat::default()),
109			reference_time: Some(ReferenceTime::default()),
110			group_id: None,
111			custom_fields: HashMap::new()
112		}
113	}
114}
115
116#[derive(Default, Serialize)]
117#[serde(rename_all = "snake_case")]
118pub enum TimeFormat {
119	/// Relative to the ReferenceTime 'epoch' field
120	#[default]
121	RelativeToEpoch,
122	/// Delta-encoded value, based on the previously logged value
123	RelativeToPreviousEvent
124}
125
126#[skip_serializing_none]
127#[derive(Default, Serialize)]
128pub struct ReferenceTime {
129	clock_type: ClockType,
130	epoch: Epoch,
131	wall_clock_time: Option<DateTime<FixedOffset>>
132}
133
134impl ReferenceTime {
135	/// clock_type defaults to System when None
136	///
137	/// epoch defaults to "1970-01-01T00:00:00.000Z" when None
138	pub fn new(clock_type: Option<ClockType>, epoch: Option<Epoch>, wall_clock_time: Option<DateTime<FixedOffset>>) -> ReferenceTime {
139		let clock_type = clock_type.unwrap_or_default();
140		let epoch = epoch.unwrap_or_default();
141
142		if clock_type == ClockType::Monotonic && epoch != Epoch::Unknown {
143			panic!("When using the 'monotonic' clock type, the epoch field must have the value 'unknown'");
144		}
145
146		ReferenceTime { clock_type, epoch, wall_clock_time }
147	}
148}
149
150#[derive(Default, PartialEq, Eq, Serialize)]
151#[serde(rename_all = "snake_case")]
152pub enum ClockType {
153	#[default]
154	System,
155	Monotonic,
156	Other(String)
157}
158
159#[derive(PartialEq, Eq, Serialize)]
160#[serde(rename_all = "snake_case", untagged)]
161pub enum Epoch {
162	Rfc3339DateTime(DateTime<FixedOffset>),
163	Unknown
164}
165
166impl Default for Epoch {
167	fn default() -> Self {
168		Self::Rfc3339DateTime(DateTime::parse_from_rfc3339("1970-01-01T00:00:00.000Z").unwrap())
169	}
170}
171
172/// Vantage point from which a trace originates
173#[skip_serializing_none]
174#[derive(Serialize)]
175pub struct VantagePoint {
176	name: Option<String>,
177	// 'type' is a keyword in Rust
178	#[serde(rename = "type")]
179	vp_type: VantagePointType,
180	/// The direction of the data flow (e.g., Client: "packet sent" event goes in direction of the server, Server: "packet sent" event goes in direction of the client)
181	flow: Option<VantagePointType>
182}
183
184impl VantagePoint {
185	pub fn new(name: Option<String>, vp_type: VantagePointType, flow: Option<VantagePointType>) -> VantagePoint {
186		if vp_type == VantagePointType::Network && flow.is_none() {
187  			panic!("The 'flow' field is required if the type is 'network'");
188  		}
189
190		VantagePoint { name, vp_type, flow }
191	}
192}
193
194#[derive(PartialEq, Eq, Serialize)]
195#[serde(rename_all = "snake_case")]
196pub enum VantagePointType {
197	/// Initiates the connection
198	Client,
199	/// Accepts the connection
200	Server,
201	/// Observer in between client and server
202	Network,
203	Unknown
204}