1use crate::otel_to_sentry_v2;
2use crate::otel_trace::Span as OtelSpan;
3use crate::v2_to_v1;
4use opentelemetry_proto::tonic::common::v1::InstrumentationScope;
5use opentelemetry_proto::tonic::resource::v1::Resource;
6use relay_event_schema::protocol::Span as EventSpan;
7
8pub fn otel_to_sentry_span(
27 otel_span: OtelSpan,
28 resource: Option<&Resource>,
29 scope: Option<&InstrumentationScope>,
30) -> EventSpan {
31 let span_v2 = otel_to_sentry_v2::otel_to_sentry_span(otel_span, resource, scope);
32 v2_to_v1::span_v2_to_span_v1(span_v2)
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38 use relay_protocol::{Annotated, SerializableAnnotated};
39
40 #[test]
41 fn parse_span() {
42 let json = r#"{
43 "traceId": "89143b0763095bd9c9955e8175d1fb23",
44 "spanId": "e342abb1214ca181",
45 "parentSpanId": "0c7a7dea069bf5a6",
46 "name": "middleware - fastify -> @fastify/multipart",
47 "kind": 2,
48 "startTimeUnixNano": "1697620454980000000",
49 "endTimeUnixNano": "1697620454980078800",
50 "attributes": [
51 {
52 "key": "http.route", "value": {
53 "stringValue": "/home"
54 }
55 },
56 {
57 "key": "http.request.method",
58 "value": {
59 "stringValue": "GET"
60 }
61 },
62 {
63 "key": "sentry.environment",
64 "value": {
65 "stringValue": "test"
66 }
67 },
68 {
69 "key": "fastify.type",
70 "value": {
71 "stringValue": "middleware"
72 }
73 },
74 {
75 "key": "plugin.name",
76 "value": {
77 "stringValue": "fastify -> @fastify/multipart"
78 }
79 },
80 {
81 "key": "hook.name",
82 "value": {
83 "stringValue": "onResponse"
84 }
85 },
86 {
87 "key": "sentry.sample_rate",
88 "value": {
89 "intValue": "1"
90 }
91 },
92 {
93 "key": "sentry.parentSampled",
94 "value": {
95 "boolValue": true
96 }
97 },
98 {
99 "key": "sentry.exclusive_time",
100 "value": {
101 "doubleValue": 1000.000000
102 }
103 }
104 ],
105 "droppedAttributesCount": 0,
106 "events": [],
107 "droppedEventsCount": 0,
108 "status": {
109 "code": 0,
110 "message": "test"
111 },
112 "links": [],
113 "droppedLinksCount": 0
114 }"#;
115
116 let resource = serde_json::from_value(serde_json::json!({
117 "attributes": [{
118 "key": "service.name",
119 "value": {"intValue": 42},
120 }]
121 }))
122 .unwrap();
123
124 let scope = InstrumentationScope {
125 name: "Eins Name".to_owned(),
126 version: "123.42".to_owned(),
127 attributes: Vec::new(),
128 dropped_attributes_count: 12,
129 };
130
131 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
132 let event_span: EventSpan = otel_to_sentry_span(otel_span, Some(&resource), Some(&scope));
133 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
134 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
135 {
136 "timestamp": 1697620454.980079,
137 "start_timestamp": 1697620454.98,
138 "exclusive_time": 1000.0,
139 "op": "http.server",
140 "span_id": "e342abb1214ca181",
141 "parent_span_id": "0c7a7dea069bf5a6",
142 "trace_id": "89143b0763095bd9c9955e8175d1fb23",
143 "is_remote": false,
144 "status": "ok",
145 "description": "GET /home",
146 "data": {
147 "sentry.environment": "test",
148 "sentry.name": "middleware - fastify -> @fastify/multipart",
149 "fastify.type": "middleware",
150 "hook.name": "onResponse",
151 "http.request.method": "GET",
152 "http.route": "/home",
153 "instrumentation.name": "Eins Name",
154 "instrumentation.version": "123.42",
155 "plugin.name": "fastify -> @fastify/multipart",
156 "resource.service.name": 42,
157 "sentry.parentSampled": true,
158 "sentry.sample_rate": 1,
159 "sentry.status.message": "test"
160 },
161 "links": [],
162 "kind": "server"
163 }
164 "#);
165 }
166
167 #[test]
168 fn parse_span_with_exclusive_time_attribute() {
169 let json = r#"{
170 "traceId": "89143b0763095bd9c9955e8175d1fb23",
171 "spanId": "e342abb1214ca181",
172 "parentSpanId": "0c7a7dea069bf5a6",
173 "name": "middleware - fastify -> @fastify/multipart",
174 "kind": 1,
175 "startTimeUnixNano": "1697620454980000000",
176 "endTimeUnixNano": "1697620454980078800",
177 "attributes": [
178 {
179 "key": "sentry.exclusive_time",
180 "value": {
181 "doubleValue": 3200.0
182 }
183 }
184 ]
185 }"#;
186 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
187 let event_span: EventSpan = otel_to_sentry_span(otel_span, None, None);
188 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
189 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
190 {
191 "timestamp": 1697620454.980079,
192 "start_timestamp": 1697620454.98,
193 "exclusive_time": 3200.0,
194 "op": "default",
195 "span_id": "e342abb1214ca181",
196 "parent_span_id": "0c7a7dea069bf5a6",
197 "trace_id": "89143b0763095bd9c9955e8175d1fb23",
198 "is_remote": false,
199 "status": "ok",
200 "description": "middleware - fastify -> @fastify/multipart",
201 "data": {
202 "sentry.name": "middleware - fastify -> @fastify/multipart"
203 },
204 "links": [],
205 "kind": "internal"
206 }
207 "#);
208 }
209
210 #[test]
211 fn parse_span_no_exclusive_time_attribute() {
212 let json = r#"{
213 "traceId": "89143b0763095bd9c9955e8175d1fb23",
214 "spanId": "e342abb1214ca181",
215 "parentSpanId": "0c7a7dea069bf5a6",
216 "name": "middleware - fastify -> @fastify/multipart",
217 "kind": 1,
218 "startTimeUnixNano": "1697620454980000000",
219 "endTimeUnixNano": "1697620454980078800"
220 }"#;
221 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
222 let event_span: EventSpan = otel_to_sentry_span(otel_span, None, None);
223 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
224 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
225 {
226 "timestamp": 1697620454.980079,
227 "start_timestamp": 1697620454.98,
228 "exclusive_time": 0.0788,
229 "op": "default",
230 "span_id": "e342abb1214ca181",
231 "parent_span_id": "0c7a7dea069bf5a6",
232 "trace_id": "89143b0763095bd9c9955e8175d1fb23",
233 "is_remote": false,
234 "status": "ok",
235 "description": "middleware - fastify -> @fastify/multipart",
236 "data": {
237 "sentry.name": "middleware - fastify -> @fastify/multipart"
238 },
239 "links": [],
240 "kind": "internal"
241 }
242 "#);
243 }
244
245 #[test]
246 fn parse_span_with_db_attributes() {
247 let json = r#"{
248 "traceId": "89143b0763095bd9c9955e8175d1fb23",
249 "spanId": "e342abb1214ca181",
250 "parentSpanId": "0c7a7dea069bf5a6",
251 "name": "database query",
252 "kind": 3,
253 "startTimeUnixNano": "1697620454980000000",
254 "endTimeUnixNano": "1697620454980078800",
255 "attributes": [
256 {
257 "key" : "db.system",
258 "value": {
259 "stringValue": "mysql"
260 }
261 },
262 {
263 "key" : "db.name",
264 "value": {
265 "stringValue": "database"
266 }
267 },
268 {
269 "key" : "db.type",
270 "value": {
271 "stringValue": "sql"
272 }
273 },
274 {
275 "key" : "db.statement",
276 "value": {
277 "stringValue": "SELECT \"table\".\"col\" FROM \"table\" WHERE \"table\".\"col\" = %s"
278 }
279 }
280 ]
281 }"#;
282 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
283 let event_span: EventSpan = otel_to_sentry_span(otel_span, None, None);
284 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
285 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
286 {
287 "timestamp": 1697620454.980079,
288 "start_timestamp": 1697620454.98,
289 "exclusive_time": 0.0788,
290 "op": "db",
291 "span_id": "e342abb1214ca181",
292 "parent_span_id": "0c7a7dea069bf5a6",
293 "trace_id": "89143b0763095bd9c9955e8175d1fb23",
294 "is_remote": false,
295 "status": "ok",
296 "description": "SELECT \"table\".\"col\" FROM \"table\" WHERE \"table\".\"col\" = %s",
297 "data": {
298 "db.system": "mysql",
299 "sentry.name": "database query",
300 "db.name": "database",
301 "db.statement": "SELECT \"table\".\"col\" FROM \"table\" WHERE \"table\".\"col\" = %s",
302 "db.type": "sql"
303 },
304 "links": [],
305 "kind": "client"
306 }
307 "#);
308 }
309
310 #[test]
311 fn parse_span_with_db_attributes_and_description() {
312 let json = r#"{
313 "traceId": "89143b0763095bd9c9955e8175d1fb23",
314 "spanId": "e342abb1214ca181",
315 "parentSpanId": "0c7a7dea069bf5a6",
316 "name": "database query",
317 "kind": 3,
318 "startTimeUnixNano": "1697620454980000000",
319 "endTimeUnixNano": "1697620454980078800",
320 "attributes": [
321 {
322 "key" : "db.name",
323 "value": {
324 "stringValue": "database"
325 }
326 },
327 {
328 "key" : "db.type",
329 "value": {
330 "stringValue": "sql"
331 }
332 },
333 {
334 "key" : "db.statement",
335 "value": {
336 "stringValue": "SELECT \"table\".\"col\" FROM \"table\" WHERE \"table\".\"col\" = %s"
337 }
338 },
339 {
340 "key": "sentry.description",
341 "value": {
342 "stringValue": "index view query"
343 }
344 }
345 ]
346 }"#;
347 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
348 let event_span: EventSpan = otel_to_sentry_span(otel_span, None, None);
349 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
350 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
351 {
352 "timestamp": 1697620454.980079,
353 "start_timestamp": 1697620454.98,
354 "exclusive_time": 0.0788,
355 "op": "default",
356 "span_id": "e342abb1214ca181",
357 "parent_span_id": "0c7a7dea069bf5a6",
358 "trace_id": "89143b0763095bd9c9955e8175d1fb23",
359 "is_remote": false,
360 "status": "ok",
361 "description": "index view query",
362 "data": {
363 "sentry.name": "database query",
364 "db.name": "database",
365 "db.statement": "SELECT \"table\".\"col\" FROM \"table\" WHERE \"table\".\"col\" = %s",
366 "db.type": "sql"
367 },
368 "links": [],
369 "kind": "client"
370 }
371 "#);
372 }
373
374 #[test]
375 fn parse_span_with_http_attributes() {
376 let json = r#"{
377 "traceId": "89143b0763095bd9c9955e8175d1fb23",
378 "spanId": "e342abb1214ca181",
379 "parentSpanId": "0c7a7dea069bf5a6",
380 "name": "http client request",
381 "kind": 2,
382 "startTimeUnixNano": "1697620454980000000",
383 "endTimeUnixNano": "1697620454980078800",
384 "attributes": [
385 {
386 "key" : "http.request.method",
387 "value": {
388 "stringValue": "GET"
389 }
390 },
391 {
392 "key" : "url.path",
393 "value": {
394 "stringValue": "/api/search?q=foobar"
395 }
396 }
397 ]
398 }"#;
399 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
400 let event_span: EventSpan = otel_to_sentry_span(otel_span, None, None);
401 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
402 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
403 {
404 "timestamp": 1697620454.980079,
405 "start_timestamp": 1697620454.98,
406 "exclusive_time": 0.0788,
407 "op": "http.server",
408 "span_id": "e342abb1214ca181",
409 "parent_span_id": "0c7a7dea069bf5a6",
410 "trace_id": "89143b0763095bd9c9955e8175d1fb23",
411 "is_remote": false,
412 "status": "ok",
413 "description": "GET /api/search?q=foobar",
414 "data": {
415 "sentry.name": "http client request",
416 "http.request.method": "GET",
417 "url.path": "/api/search?q=foobar"
418 },
419 "links": [],
420 "kind": "server"
421 }
422 "#);
423 }
424
425 #[test]
426 fn parse_array_attribute() {
427 let json = r#"{
428 "traceId": "4c79f60c11214eb38604f4ae0781bfb2",
429 "spanId": "fa90fdead5f74052",
430 "parentSpanId": "fa90fdead5f74051",
431 "startTimeUnixNano": "123000000000",
432 "endTimeUnixNano": "123500000000",
433 "name": "cmd.run",
434 "status": {"code": 0},
435 "attributes": [
436 {
437 "key": "process.args",
438 "value": {
439 "arrayValue": {
440 "values": [
441 {"stringValue": "node"},
442 {"stringValue": "--require"},
443 {"stringValue": "preflight.cjs"}
444 ]
445 }
446 }
447 },
448 {
449 "key": "process.info",
450 "value": {
451 "arrayValue": {
452 "values": [
453 {"intValue": 41},
454 {
455 "arrayValue": {
456 "values": [
457 {"intValue": 42}
458 ]}
459 }
460 ]
461 }
462 }
463 }
464 ]
465 }"#;
466
467 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
468 let event_span = otel_to_sentry_span(otel_span, None, None);
469
470 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
471 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
472 {
473 "timestamp": 123.5,
474 "start_timestamp": 123.0,
475 "exclusive_time": 500.0,
476 "op": "default",
477 "span_id": "fa90fdead5f74052",
478 "parent_span_id": "fa90fdead5f74051",
479 "trace_id": "4c79f60c11214eb38604f4ae0781bfb2",
480 "is_remote": false,
481 "status": "ok",
482 "description": "cmd.run",
483 "data": {
484 "sentry.name": "cmd.run",
485 "process.args": "[\"node\",\"--require\",\"preflight.cjs\"]",
486 "process.info": "[41]"
487 },
488 "links": []
489 }
490 "#);
491 }
492
493 #[test]
495 fn parse_sentry_attributes() {
496 let json = r#"{
497 "traceId": "4c79f60c11214eb38604f4ae0781bfb2",
498 "spanId": "fa90fdead5f74052",
499 "parentSpanId": "fa90fdead5f74051",
500 "startTimeUnixNano": "123000000000",
501 "endTimeUnixNano": "123500000000",
502 "name": "myname",
503 "status": {"code": 0, "message": "foo"},
504 "attributes": [
505 {
506 "key" : "browser.name",
507 "value": {
508 "stringValue": "Chrome"
509 }
510 },
511 {
512 "key" : "sentry.description",
513 "value": {
514 "stringValue": "mydescription"
515 }
516 },
517 {
518 "key" : "sentry.environment",
519 "value": {
520 "stringValue": "prod"
521 }
522 },
523 {
524 "key" : "sentry.op",
525 "value": {
526 "stringValue": "myop"
527 }
528 },
529 {
530 "key" : "sentry.platform",
531 "value": {
532 "stringValue": "php"
533 }
534 },
535 {
536 "key" : "sentry.profile_id",
537 "value": {
538 "stringValue": "a0aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaab"
539 }
540 },
541 {
542 "key" : "sentry.release",
543 "value": {
544 "stringValue": "myapp@1.0.0"
545 }
546 },
547 {
548 "key" : "sentry.sdk.name",
549 "value": {
550 "stringValue": "sentry.php"
551 }
552 },
553 {
554 "key" : "sentry.segment.id",
555 "value": {
556 "stringValue": "FA90FDEAD5F74052"
557 }
558 },
559 {
560 "key" : "sentry.segment.name",
561 "value": {
562 "stringValue": "my 1st transaction"
563 }
564 },
565 {
566 "key": "sentry.metrics_summary.some_metric",
567 "value": {
568 "arrayValue": {
569 "values": [
570 {
571 "kvlistValue": {
572 "values": [
573 {
574 "key": "min",
575 "value": {
576 "doubleValue": 1.0
577 }
578 },
579 {
580 "key": "max",
581 "value": {
582 "doubleValue": 2.0
583 }
584 },
585 {
586 "key": "sum",
587 "value": {
588 "doubleValue": 3.0
589 }
590 },
591 {
592 "key": "count",
593 "value": {
594 "intValue": "2"
595 }
596 },
597 {
598 "key": "tags",
599 "value": {
600 "kvlistValue": {
601 "values": [
602 {
603 "key": "environment",
604 "value": {
605 "stringValue": "test"
606 }
607 }
608 ]
609 }
610 }
611 }
612 ]
613 }
614 }
615 ]
616 }
617 }
618 }
619 ]
620 }"#;
621
622 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
623 let event_span = otel_to_sentry_span(otel_span, None, None);
624
625 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
626 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
627 {
628 "timestamp": 123.5,
629 "start_timestamp": 123.0,
630 "exclusive_time": 500.0,
631 "op": "myop",
632 "span_id": "fa90fdead5f74052",
633 "parent_span_id": "fa90fdead5f74051",
634 "trace_id": "4c79f60c11214eb38604f4ae0781bfb2",
635 "segment_id": "fa90fdead5f74052",
636 "is_remote": false,
637 "status": "ok",
638 "description": "mydescription",
639 "profile_id": "a0aaaaaaaaaaaaaaaaaaaaaaaaaaaaab",
640 "data": {
641 "browser.name": "Chrome",
642 "sentry.environment": "prod",
643 "sentry.release": "myapp@1.0.0",
644 "sentry.segment.name": "my 1st transaction",
645 "sentry.sdk.name": "sentry.php",
646 "sentry.name": "myname",
647 "sentry.metrics_summary.some_metric": "[]",
648 "sentry.status.message": "foo"
649 },
650 "links": [],
651 "platform": "php"
652 }
653 "#);
654 }
655
656 #[test]
657 fn parse_span_is_remote() {
658 let json = r#"{
659 "traceId": "89143b0763095bd9c9955e8175d1fb23",
660 "spanId": "e342abb1214ca181",
661 "parentSpanId": "0c7a7dea069bf5a6",
662 "startTimeUnixNano": "123000000000",
663 "endTimeUnixNano": "123500000000",
664 "flags": 768
665 }"#;
666 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
667 let event_span: EventSpan = otel_to_sentry_span(otel_span, None, None);
668 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
669 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
670 {
671 "timestamp": 123.5,
672 "start_timestamp": 123.0,
673 "exclusive_time": 500.0,
674 "op": "default",
675 "span_id": "e342abb1214ca181",
676 "parent_span_id": "0c7a7dea069bf5a6",
677 "trace_id": "89143b0763095bd9c9955e8175d1fb23",
678 "is_remote": true,
679 "status": "ok",
680 "data": {},
681 "links": []
682 }
683 "#);
684 }
685
686 #[test]
687 fn parse_span_is_not_remote() {
688 let json = r#"{
689 "traceId": "89143b0763095bd9c9955e8175d1fb23",
690 "spanId": "e342abb1214ca181",
691 "parentSpanId": "0c7a7dea069bf5a6",
692 "startTimeUnixNano": "123000000000",
693 "endTimeUnixNano": "123500000000",
694 "flags": 256
695 }"#;
696 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
697 let event_span: EventSpan = otel_to_sentry_span(otel_span, None, None);
698 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
699 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
700 {
701 "timestamp": 123.5,
702 "start_timestamp": 123.0,
703 "exclusive_time": 500.0,
704 "op": "default",
705 "span_id": "e342abb1214ca181",
706 "parent_span_id": "0c7a7dea069bf5a6",
707 "trace_id": "89143b0763095bd9c9955e8175d1fb23",
708 "is_remote": false,
709 "status": "ok",
710 "data": {},
711 "links": []
712 }
713 "#);
714 }
715
716 #[test]
717 fn extract_span_kind() {
718 let json = r#"{
719 "traceId": "89143b0763095bd9c9955e8175d1fb23",
720 "spanId": "e342abb1214ca181",
721 "parentSpanId": "0c7a7dea069bf5a6",
722 "startTimeUnixNano": "123000000000",
723 "endTimeUnixNano": "123500000000",
724 "kind": 3
725 }"#;
726 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
727 let event_span: EventSpan = otel_to_sentry_span(otel_span, None, None);
728 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
729 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
730 {
731 "timestamp": 123.5,
732 "start_timestamp": 123.0,
733 "exclusive_time": 500.0,
734 "op": "default",
735 "span_id": "e342abb1214ca181",
736 "parent_span_id": "0c7a7dea069bf5a6",
737 "trace_id": "89143b0763095bd9c9955e8175d1fb23",
738 "is_remote": false,
739 "status": "ok",
740 "data": {},
741 "links": [],
742 "kind": "client"
743 }
744 "#);
745 }
746
747 #[test]
748 fn parse_link() {
749 let json = r#"{
750 "traceId": "3c79f60c11214eb38604f4ae0781bfb2",
751 "spanId": "e342abb1214ca181",
752 "links": [
753 {
754 "traceId": "4c79f60c11214eb38604f4ae0781bfb2",
755 "spanId": "fa90fdead5f74052",
756 "attributes": [
757 {
758 "key": "str_key",
759 "value": {
760 "stringValue": "str_value"
761 }
762 },
763 {
764 "key": "bool_key",
765 "value": {
766 "boolValue": true
767 }
768 },
769 {
770 "key": "int_key",
771 "value": {
772 "intValue": "123"
773 }
774 },
775 {
776 "key": "double_key",
777 "value": {
778 "doubleValue": 1.23
779 }
780 }
781 ],
782 "flags": 1
783 }
784 ]
785 }"#;
786 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
787 let event_span: EventSpan = otel_to_sentry_span(otel_span, None, None);
788 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
789
790 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
791 {
792 "timestamp": 0.0,
793 "start_timestamp": 0.0,
794 "exclusive_time": 0.0,
795 "op": "default",
796 "span_id": "e342abb1214ca181",
797 "trace_id": "3c79f60c11214eb38604f4ae0781bfb2",
798 "is_remote": false,
799 "status": "ok",
800 "data": {},
801 "links": [
802 {
803 "trace_id": "4c79f60c11214eb38604f4ae0781bfb2",
804 "span_id": "fa90fdead5f74052",
805 "sampled": true,
806 "attributes": {
807 "bool_key": true,
808 "double_key": 1.23,
809 "int_key": 123,
810 "str_key": "str_value"
811 }
812 }
813 ]
814 }
815 "#);
816 }
817
818 #[test]
819 fn parse_span_error_status() {
820 let json = r#"{
821 "traceId": "89143b0763095bd9c9955e8175d1fb23",
822 "spanId": "e342abb1214ca181",
823 "status": {
824 "code": 2,
825 "message": "2 is the error status code"
826 }
827 }"#;
828 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
829 let event_span = otel_to_sentry_span(otel_span, None, None);
830 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
831 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
832 {
833 "timestamp": 0.0,
834 "start_timestamp": 0.0,
835 "exclusive_time": 0.0,
836 "op": "default",
837 "span_id": "e342abb1214ca181",
838 "trace_id": "89143b0763095bd9c9955e8175d1fb23",
839 "is_remote": false,
840 "status": "internal_error",
841 "data": {
842 "sentry.status.message": "2 is the error status code"
843 },
844 "links": []
845 }
846 "#);
847 }
848}