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