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}