1use crate::protocol::{BrowserContext, Event, ProfileContext, Span, SpanData, TraceContext};
4
5impl From<&Event> for Span {
6 fn from(event: &Event) -> Self {
7 let Event {
8 transaction,
9
10 platform,
11 timestamp,
12 start_timestamp,
13 received,
14 release,
15 environment,
16 tags,
17
18 measurements,
19 _metrics,
20 performance_issues_spans,
21 ..
22 } = event;
23
24 let trace = event.context::<TraceContext>();
25
26 let mut data = trace
28 .map(|c| c.data.clone().map_value(SpanData::from))
29 .unwrap_or_default();
30
31 let span_data = data.get_or_insert_with(Default::default);
33 span_data.segment_name = transaction.clone();
34 span_data.span_name = transaction.clone();
36 span_data.release = release.clone();
37 span_data.environment = environment.clone();
38 if let Some(browser) = event.context::<BrowserContext>() {
39 span_data.browser_name = browser.name.clone();
40 }
41 if let Some(client_sdk) = event.client_sdk.value() {
42 span_data.sdk_name = client_sdk.name.clone();
43 span_data.sdk_version = client_sdk.version.clone();
44 }
45 if let Some(request) = event.request.value()
46 && let Some(query) = request.query_string.value()
47 && let Some(qs) = query.to_query_string()
48 {
49 span_data.http_query = format!("?{qs}").into();
50 span_data.url_query = qs.into();
51 }
52
53 Self {
54 timestamp: timestamp.clone(),
55 start_timestamp: start_timestamp.clone(),
56 exclusive_time: trace.map(|c| c.exclusive_time.clone()).unwrap_or_default(),
57 op: trace.map(|c| c.op.clone()).unwrap_or_default(),
58 span_id: trace.map(|c| c.span_id.clone()).unwrap_or_default(),
59 parent_span_id: trace.map(|c| c.parent_span_id.clone()).unwrap_or_default(),
60 trace_id: trace.map(|c| c.trace_id.clone()).unwrap_or_default(),
61 segment_id: trace.map(|c| c.span_id.clone()).unwrap_or_default(),
62 is_segment: true.into(),
63 is_remote: true.into(),
68 status: trace.map(|c| c.status.clone()).unwrap_or_default(),
69 description: transaction.clone(),
70 tags: tags.clone().map_value(|t| t.into()),
71 origin: trace.map(|c| c.origin.clone()).unwrap_or_default(),
72 profile_id: event
73 .context::<ProfileContext>()
74 .map(|c| c.profile_id.clone())
75 .unwrap_or_default(),
76 data,
77 links: trace.map(|c| c.links.clone()).unwrap_or_default(),
78 sentry_tags: Default::default(),
79 received: received.clone(),
80 measurements: measurements.clone(),
81 platform: platform.clone(),
82 was_transaction: true.into(),
83 kind: Default::default(),
84 performance_issues_spans: performance_issues_spans.clone(),
85 other: Default::default(),
86 }
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use relay_protocol::Annotated;
93
94 use super::*;
95
96 #[test]
97 fn convert() {
98 let event = Annotated::<Event>::from_json(
99 r#"{
100 "type": "transaction",
101 "platform": "php",
102 "sdk": {"name": "sentry.php", "version": "1.2.3"},
103 "release": "myapp@1.0.0",
104 "environment": "prod",
105 "transaction": "my 1st transaction",
106 "contexts": {
107 "browser": {"name": "Chrome"},
108 "profile": {"profile_id": "a0aaaaaaaaaaaaaaaaaaaaaaaaaaaaab"},
109 "trace": {
110 "trace_id": "4C79F60C11214EB38604F4AE0781BFB2",
111 "span_id": "FA90FDEAD5F74052",
112 "type": "trace",
113 "origin": "manual",
114 "op": "myop",
115 "status": "ok",
116 "exclusive_time": 123.4,
117 "parent_span_id": "FA90FDEAD5F74051",
118 "data": {
119 "custom_attribute": 42
120 },
121 "links": [
122 {
123 "trace_id": "4c79f60c11214eb38604f4ae0781bfb2",
124 "span_id": "fa90fdead5f74052",
125 "sampled": true,
126 "attributes": {
127 "sentry.link.type": "previous_trace"
128 }
129 }
130 ]
131 }
132 },
133 "request": {
134 "url": "http://example.com/api/0/organizations/",
135 "method": "GET",
136 "query_string": "project=1&sort=date"
137 },
138 "measurements": {
139 "memory": {
140 "value": 9001.0,
141 "unit": "byte"
142 }
143 }
144 }"#,
145 )
146 .unwrap()
147 .into_value()
148 .unwrap();
149
150 let span_from_event = Span::from(&event);
151 insta::assert_debug_snapshot!(span_from_event, @r###"
152 Span {
153 timestamp: ~,
154 start_timestamp: ~,
155 exclusive_time: 123.4,
156 op: "myop",
157 span_id: SpanId("fa90fdead5f74052"),
158 parent_span_id: SpanId("fa90fdead5f74051"),
159 trace_id: TraceId("4c79f60c11214eb38604f4ae0781bfb2"),
160 segment_id: SpanId("fa90fdead5f74052"),
161 is_segment: true,
162 is_remote: true,
163 status: Ok,
164 description: "my 1st transaction",
165 tags: ~,
166 origin: "manual",
167 profile_id: EventId(
168 a0aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaab,
169 ),
170 data: SpanData {
171 app_start_type: ~,
172 gen_ai_request_max_tokens: ~,
173 gen_ai_pipeline_name: ~,
174 gen_ai_usage_total_tokens: ~,
175 gen_ai_usage_input_tokens: ~,
176 gen_ai_usage_input_tokens_cached: ~,
177 gen_ai_usage_input_tokens_cache_write: ~,
178 gen_ai_usage_input_tokens_cache_miss: ~,
179 gen_ai_usage_output_tokens: ~,
180 gen_ai_usage_output_tokens_reasoning: ~,
181 gen_ai_usage_output_tokens_prediction_accepted: ~,
182 gen_ai_usage_output_tokens_prediction_rejected: ~,
183 gen_ai_response_model: ~,
184 gen_ai_request_model: ~,
185 gen_ai_context_window_size: ~,
186 gen_ai_context_utilization: ~,
187 gen_ai_cost_total_tokens: ~,
188 gen_ai_cost_input_tokens: ~,
189 gen_ai_cost_output_tokens: ~,
190 gen_ai_input_messages: ~,
191 gen_ai_tool_call_arguments: ~,
192 gen_ai_tool_call_result: ~,
193 gen_ai_output_messages: ~,
194 gen_ai_response_object: ~,
195 gen_ai_response_streaming: ~,
196 gen_ai_response_tokens_per_second: ~,
197 gen_ai_response_time_to_first_token: ~,
198 gen_ai_tool_definitions: ~,
199 gen_ai_request_frequency_penalty: ~,
200 gen_ai_request_presence_penalty: ~,
201 gen_ai_request_seed: ~,
202 gen_ai_request_temperature: ~,
203 gen_ai_request_top_k: ~,
204 gen_ai_request_top_p: ~,
205 gen_ai_response_finish_reasons: ~,
206 gen_ai_response_id: ~,
207 gen_ai_provider_name: ~,
208 gen_ai_system_instructions: ~,
209 gen_ai_tool_name: ~,
210 gen_ai_operation_name: ~,
211 gen_ai_operation_type: ~,
212 gen_ai_agent_name: ~,
213 gen_ai_function_id: ~,
214 mcp_prompt_result: ~,
215 mcp_tool_result_content: ~,
216 browser_name: "Chrome",
217 code_filepath: ~,
218 code_lineno: ~,
219 code_function: ~,
220 code_namespace: ~,
221 db_operation: ~,
222 db_system: ~,
223 db_collection_name: ~,
224 environment: "prod",
225 release: LenientString(
226 "myapp@1.0.0",
227 ),
228 http_decoded_response_content_length: ~,
229 http_request_method: ~,
230 http_response_content_length: ~,
231 http_response_transfer_size: ~,
232 resource_render_blocking_status: ~,
233 server_address: ~,
234 cache_hit: ~,
235 cache_key: ~,
236 cache_item_size: ~,
237 http_response_status_code: ~,
238 thread_name: ~,
239 thread_id: ~,
240 segment_name: "my 1st transaction",
241 ui_component_name: ~,
242 url_scheme: ~,
243 user: ~,
244 user_email: ~,
245 user_full_name: ~,
246 user_geo_country_code: ~,
247 user_geo_city: ~,
248 user_geo_subdivision: ~,
249 user_geo_region: ~,
250 user_hash: ~,
251 user_id: ~,
252 user_name: ~,
253 user_roles: ~,
254 exclusive_time: ~,
255 profile_id: ~,
256 replay_id: ~,
257 sdk_name: "sentry.php",
258 sdk_version: "1.2.3",
259 frames_slow: ~,
260 frames_frozen: ~,
261 frames_total: ~,
262 frames_delay: ~,
263 messaging_destination_name: ~,
264 messaging_message_retry_count: ~,
265 messaging_message_receive_latency: ~,
266 messaging_message_body_size: ~,
267 messaging_message_id: ~,
268 messaging_operation_name: ~,
269 messaging_operation_type: ~,
270 user_agent_original: ~,
271 url_full: ~,
272 url_query: "project=1&sort=date",
273 http_query: "?project=1&sort=date",
274 client_address: ~,
275 route: ~,
276 previous_route: ~,
277 lcp_element: ~,
278 lcp_size: ~,
279 lcp_id: ~,
280 lcp_url: ~,
281 span_name: "my 1st transaction",
282 other: {
283 "custom_attribute": I64(
284 42,
285 ),
286 },
287 },
288 links: [
289 SpanLink {
290 trace_id: TraceId("4c79f60c11214eb38604f4ae0781bfb2"),
291 span_id: SpanId("fa90fdead5f74052"),
292 sampled: true,
293 attributes: {
294 "sentry.link.type": String(
295 "previous_trace",
296 ),
297 },
298 other: {},
299 },
300 ],
301 sentry_tags: ~,
302 received: ~,
303 measurements: Measurements(
304 {
305 "memory": Measurement {
306 value: 9001.0,
307 unit: Information(
308 Byte,
309 ),
310 },
311 },
312 ),
313 platform: "php",
314 was_transaction: true,
315 kind: ~,
316 performance_issues_spans: ~,
317 other: {},
318 }
319 "###);
320 }
321}