telemetry_rust/test/jaegar.rs
1//! Data structures for deserializing and traversing Jaeger API responses.
2
3use opentelemetry_api::trace::{SpanId, TraceId};
4use serde::{
5 Deserialize, Deserializer, Serialize, Serializer, de::Error as DeserializationError,
6};
7use serde_json::Value;
8use std::collections::HashMap;
9
10fn trace_from_hex<'de, D>(deserializer: D) -> Result<TraceId, D::Error>
11where
12 D: Deserializer<'de>,
13{
14 let hex: &str = Deserialize::deserialize(deserializer)?;
15 match TraceId::from_hex(hex) {
16 Ok(trace_id) => Ok(trace_id),
17 Err(error) => Err(D::Error::custom(error)),
18 }
19}
20
21fn span_from_hex<'de, D>(deserializer: D) -> Result<SpanId, D::Error>
22where
23 D: Deserializer<'de>,
24{
25 let hex: &str = Deserialize::deserialize(deserializer)?;
26 match SpanId::from_hex(hex) {
27 Ok(trace_id) => Ok(trace_id),
28 Err(error) => Err(D::Error::custom(error)),
29 }
30}
31
32fn as_hex<T, S>(val: &T, serializer: S) -> Result<S::Ok, S::Error>
33where
34 T: std::fmt::LowerHex,
35 S: Serializer,
36{
37 serializer.serialize_str(&format!("{val:x}"))
38}
39
40/// Response structure for Jaeger trace query API.
41///
42/// This structure represents the response from Jaeger's trace query API,
43/// containing trace data along with pagination information and potential errors.
44#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
45#[serde(rename_all = "camelCase")]
46pub struct TraceResponse {
47 /// Any errors that occurred during the trace query
48 pub errors: Option<Vec<Error>>,
49 /// The actual trace data returned by the query
50 pub data: Option<Vec<TraceData>>,
51 /// Total number of traces matching the query criteria
52 pub total: i64,
53 /// Maximum number of traces to return in this response
54 pub limit: i64,
55 /// Number of traces to skip from the beginning of the result set
56 pub offset: i64,
57}
58
59/// Error information from Jaeger trace operations.
60///
61/// Represents an error condition that occurred during trace query or processing
62/// operations in Jaeger, containing both an error code and descriptive message.
63#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
64#[serde(rename_all = "camelCase")]
65pub struct Error {
66 /// Numeric error code identifying the type of error
67 pub code: i64,
68 /// Human-readable error message describing what went wrong
69 pub msg: String,
70}
71
72impl std::fmt::Display for Error {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 write!(f, "{} - {}", self.code, self.msg)
75 }
76}
77
78impl std::error::Error for Error {}
79
80/// Complete trace data for a single distributed trace.
81///
82/// This structure contains all the information for a complete trace, including
83/// all spans that are part of the trace, process information, and any warnings
84/// that occurred during trace collection.
85#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
86#[serde(rename_all = "camelCase")]
87pub struct TraceData {
88 /// The unique identifier for this trace
89 #[serde(
90 rename = "traceID",
91 deserialize_with = "trace_from_hex",
92 serialize_with = "as_hex"
93 )]
94 pub trace_id: TraceId,
95 /// All spans that belong to this trace
96 pub spans: Vec<Span>,
97 /// Process information mapped by process ID
98 pub processes: HashMap<String, Process>,
99 /// Any warnings generated during trace collection
100 pub warnings: Option<Vec<String>>,
101}
102
103impl TraceData {
104 /// Finds a span within this trace by its operation name.
105 ///
106 /// # Arguments
107 ///
108 /// * `operation_name` - The name of the operation to search for
109 ///
110 /// # Returns
111 ///
112 /// The first span found with the matching operation name, or `None` if not found
113 ///
114 /// # Example
115 ///
116 /// ```rust
117 /// use telemetry_rust::test::jaegar::{Span, TraceData};
118 /// use telemetry_rust::test::{SpanId, TraceId};
119 ///
120 /// fn verify_span<'td>(
121 /// trace_data: &'td TraceData,
122 /// operation_name: &str,
123 /// trace_id: TraceId,
124 /// parent_span_id: SpanId,
125 /// service_name: &str,
126 /// ) -> Option<&'td Span> {
127 /// let span = trace_data.find_span(operation_name)?;
128 ///
129 /// assert_eq!(span.trace_id, trace_id);
130 ///
131 /// let parent_reference = span.get_parent_reference().unwrap();
132 /// let process_id = &span.process_id;
133 ///
134 /// assert_eq!(parent_reference.trace_id, trace_id);
135 /// assert_eq!(parent_reference.span_id, parent_span_id);
136 ///
137 /// let main_process = trace_data.processes.get(process_id).unwrap();
138 /// assert_eq!(main_process.service_name, service_name);
139 ///
140 /// Some(span)
141 /// }
142 /// ```
143 pub fn find_span(&self, operation_name: &str) -> Option<&Span> {
144 self.spans
145 .iter()
146 .find(|&span| span.operation_name == operation_name)
147 }
148}
149
150/// Individual span within a distributed trace.
151///
152/// A span represents a single operation within a trace, containing timing information,
153/// metadata, references to other spans, and associated process information.
154#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
155#[serde(rename_all = "camelCase")]
156pub struct Span {
157 /// The trace ID this span belongs to
158 #[serde(
159 rename = "traceID",
160 deserialize_with = "trace_from_hex",
161 serialize_with = "as_hex"
162 )]
163 pub trace_id: TraceId,
164 /// Unique identifier for this span
165 #[serde(
166 rename = "spanID",
167 deserialize_with = "span_from_hex",
168 serialize_with = "as_hex"
169 )]
170 pub span_id: SpanId,
171 /// Human-readable name of the operation this span represents
172 pub operation_name: String,
173 /// References to other spans (e.g., parent-child relationships)
174 pub references: Vec<Reference>,
175 /// Timestamp when this span started (in microseconds since epoch)
176 pub start_time: i64,
177 /// Duration of this span in microseconds
178 pub duration: i64,
179 /// Key-value tags providing metadata about this span
180 pub tags: Vec<Tag>,
181 /// Log entries recorded during this span's execution
182 pub logs: Vec<Log>,
183 /// ID of the process that created this span
184 #[serde(rename = "processID")]
185 pub process_id: String,
186 /// Any warnings associated with this span
187 #[serde(default)]
188 pub warnings: Option<Vec<String>>,
189}
190
191impl Span {
192 /// Finds a reference of the specified type within this span.
193 ///
194 /// # Arguments
195 ///
196 /// * `ref_type` - The type of reference to search for (e.g., "CHILD_OF", "FOLLOWS_FROM")
197 ///
198 /// # Returns
199 ///
200 /// The first reference found with the matching type, or `None` if not found
201 pub fn find_reference(&self, ref_type: &str) -> Option<&Reference> {
202 self.references
203 .iter()
204 .find(|&refer| refer.ref_type == ref_type)
205 }
206
207 /// Gets the parent reference for this span.
208 ///
209 /// This is a convenience method that specifically looks for a "CHILD_OF" reference,
210 /// which indicates the parent span in the trace hierarchy.
211 ///
212 /// # Returns
213 ///
214 /// The parent reference if this span has one, or `None` if this is a root span
215 ///
216 /// # Example
217 ///
218 /// ```rust
219 /// use telemetry_rust::test::jaegar::Span;
220 /// use telemetry_rust::test::{SpanId, TraceId};
221 ///
222 /// // Example of using get_parent_reference in span verification:
223 /// fn verify_parent_relationship(
224 /// span: &Span,
225 /// expected_trace_id: TraceId,
226 /// expected_parent_span_id: SpanId,
227 /// ) {
228 /// let parent_reference = span.get_parent_reference().unwrap();
229 /// assert_eq!(parent_reference.trace_id, expected_trace_id);
230 /// assert_eq!(parent_reference.span_id, expected_parent_span_id);
231 /// }
232 /// ```
233 pub fn get_parent_reference(&self) -> Option<&Reference> {
234 self.find_reference("CHILD_OF")
235 }
236}
237
238/// Reference between spans in a trace.
239///
240/// Represents a relationship between two spans, such as parent-child relationships
241/// or follows-from relationships that indicate causal dependencies.
242#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
243#[serde(rename_all = "camelCase")]
244pub struct Reference {
245 /// Type of reference (e.g., "CHILD_OF", "FOLLOWS_FROM")
246 pub ref_type: String,
247 /// Trace ID of the referenced span
248 #[serde(
249 rename = "traceID",
250 deserialize_with = "trace_from_hex",
251 serialize_with = "as_hex"
252 )]
253 pub trace_id: TraceId,
254 /// Span ID of the referenced span
255 #[serde(
256 rename = "spanID",
257 deserialize_with = "span_from_hex",
258 serialize_with = "as_hex"
259 )]
260 pub span_id: SpanId,
261}
262
263/// Key-value tag metadata attached to spans.
264///
265/// Tags provide additional context and metadata about spans, such as HTTP status codes,
266/// database names, or other application-specific information.
267#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
268#[serde(rename_all = "camelCase")]
269pub struct Tag {
270 /// The tag key/name
271 pub key: String,
272 /// The data type of the tag value
273 #[serde(rename = "type")]
274 pub type_field: String,
275 /// The tag value (can be various types: string, number, boolean, etc.)
276 pub value: Value,
277}
278
279/// Log event recorded during span execution.
280///
281/// Represents a structured log event that occurred during the span's lifetime,
282/// containing a timestamp and associated field data.
283#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
284#[serde(rename_all = "camelCase")]
285pub struct Log {
286 /// Timestamp when this log event occurred (in microseconds since epoch)
287 pub timestamp: i64,
288 /// Key-value fields providing details about the log event
289 pub fields: Vec<LogEntry>,
290}
291
292/// Individual field within a log event.
293///
294/// Represents a single key-value pair within a log event, providing structured
295/// data about what occurred during the span execution.
296#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
297#[serde(rename_all = "camelCase")]
298pub struct LogEntry {
299 /// The field key/name
300 pub key: String,
301 /// The data type of the field value
302 #[serde(rename = "type")]
303 pub entry_type: String,
304 /// The field value (can be various types: string, number, boolean, etc.)
305 pub value: Value,
306}
307
308/// Process information for spans in a trace.
309///
310/// Represents information about the process that generated spans,
311/// including service identification and process-level metadata.
312#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
313#[serde(rename_all = "camelCase")]
314pub struct Process {
315 /// Name of the service that generated the spans
316 pub service_name: String,
317 /// Tags providing additional metadata about the process
318 pub tags: Vec<Tag>,
319}