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 "status": "ok",
144 "description": "GET /home",
145 "data": {
146 "sentry.environment": "test",
147 "sentry.name": "middleware - fastify -> @fastify/multipart",
148 "fastify.type": "middleware",
149 "hook.name": "onResponse",
150 "http.request.method": "GET",
151 "http.route": "/home",
152 "instrumentation.name": "Eins Name",
153 "instrumentation.version": "123.42",
154 "plugin.name": "fastify -> @fastify/multipart",
155 "resource.service.name": 42,
156 "sentry.origin": "auto.otlp.spans",
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 "status": "ok",
199 "description": "middleware - fastify -> @fastify/multipart",
200 "data": {
201 "sentry.name": "middleware - fastify -> @fastify/multipart",
202 "sentry.origin": "auto.otlp.spans"
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 "status": "ok",
234 "description": "middleware - fastify -> @fastify/multipart",
235 "data": {
236 "sentry.name": "middleware - fastify -> @fastify/multipart",
237 "sentry.origin": "auto.otlp.spans"
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 "status": "ok",
295 "description": "SELECT \"table\".\"col\" FROM \"table\" WHERE \"table\".\"col\" = %s",
296 "data": {
297 "db.system": "mysql",
298 "sentry.name": "database query",
299 "db.name": "database",
300 "db.statement": "SELECT \"table\".\"col\" FROM \"table\" WHERE \"table\".\"col\" = %s",
301 "db.type": "sql",
302 "sentry.origin": "auto.otlp.spans"
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 "status": "ok",
360 "description": "index view query",
361 "data": {
362 "sentry.name": "database query",
363 "db.name": "database",
364 "db.statement": "SELECT \"table\".\"col\" FROM \"table\" WHERE \"table\".\"col\" = %s",
365 "db.type": "sql",
366 "sentry.origin": "auto.otlp.spans"
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 "status": "ok",
412 "description": "GET /api/search?q=foobar",
413 "data": {
414 "sentry.name": "http client request",
415 "http.request.method": "GET",
416 "sentry.origin": "auto.otlp.spans",
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 "status": "ok",
481 "description": "cmd.run",
482 "data": {
483 "sentry.name": "cmd.run",
484 "process.args": "[\"node\",\"--require\",\"preflight.cjs\"]",
485 "process.info": "[41]",
486 "sentry.origin": "auto.otlp.spans"
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 "status": "ok",
637 "description": "mydescription",
638 "profile_id": "a0aaaaaaaaaaaaaaaaaaaaaaaaaaaaab",
639 "data": {
640 "browser.name": "Chrome",
641 "sentry.environment": "prod",
642 "sentry.release": "myapp@1.0.0",
643 "sentry.segment.name": "my 1st transaction",
644 "sentry.sdk.name": "sentry.php",
645 "sentry.name": "myname",
646 "sentry.metrics_summary.some_metric": "[]",
647 "sentry.origin": "auto.otlp.spans",
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_segment": true,
679 "status": "ok",
680 "data": {
681 "sentry.is_remote": true,
682 "sentry.origin": "auto.otlp.spans"
683 },
684 "links": []
685 }
686 "#);
687 }
688
689 #[test]
690 fn parse_span_is_not_remote() {
691 let json = r#"{
692 "traceId": "89143b0763095bd9c9955e8175d1fb23",
693 "spanId": "e342abb1214ca181",
694 "parentSpanId": "0c7a7dea069bf5a6",
695 "startTimeUnixNano": "123000000000",
696 "endTimeUnixNano": "123500000000",
697 "flags": 256
698 }"#;
699 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
700 let event_span: EventSpan = otel_to_sentry_span(otel_span, None, None);
701 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
702 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
703 {
704 "timestamp": 123.5,
705 "start_timestamp": 123.0,
706 "exclusive_time": 500.0,
707 "op": "default",
708 "span_id": "e342abb1214ca181",
709 "parent_span_id": "0c7a7dea069bf5a6",
710 "trace_id": "89143b0763095bd9c9955e8175d1fb23",
711 "status": "ok",
712 "data": {
713 "sentry.is_remote": false,
714 "sentry.origin": "auto.otlp.spans"
715 },
716 "links": []
717 }
718 "#);
719 }
720
721 #[test]
722 fn extract_span_kind() {
723 let json = r#"{
724 "traceId": "89143b0763095bd9c9955e8175d1fb23",
725 "spanId": "e342abb1214ca181",
726 "parentSpanId": "0c7a7dea069bf5a6",
727 "startTimeUnixNano": "123000000000",
728 "endTimeUnixNano": "123500000000",
729 "kind": 3
730 }"#;
731 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
732 let event_span: EventSpan = otel_to_sentry_span(otel_span, None, None);
733 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
734 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
735 {
736 "timestamp": 123.5,
737 "start_timestamp": 123.0,
738 "exclusive_time": 500.0,
739 "op": "default",
740 "span_id": "e342abb1214ca181",
741 "parent_span_id": "0c7a7dea069bf5a6",
742 "trace_id": "89143b0763095bd9c9955e8175d1fb23",
743 "status": "ok",
744 "data": {
745 "sentry.origin": "auto.otlp.spans"
746 },
747 "links": [],
748 "kind": "client"
749 }
750 "#);
751 }
752
753 #[test]
754 fn parse_link() {
755 let json = r#"{
756 "traceId": "3c79f60c11214eb38604f4ae0781bfb2",
757 "spanId": "e342abb1214ca181",
758 "links": [
759 {
760 "traceId": "4c79f60c11214eb38604f4ae0781bfb2",
761 "spanId": "fa90fdead5f74052",
762 "attributes": [
763 {
764 "key": "str_key",
765 "value": {
766 "stringValue": "str_value"
767 }
768 },
769 {
770 "key": "bool_key",
771 "value": {
772 "boolValue": true
773 }
774 },
775 {
776 "key": "int_key",
777 "value": {
778 "intValue": "123"
779 }
780 },
781 {
782 "key": "double_key",
783 "value": {
784 "doubleValue": 1.23
785 }
786 }
787 ],
788 "flags": 1
789 }
790 ]
791 }"#;
792 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
793 let event_span: EventSpan = otel_to_sentry_span(otel_span, None, None);
794 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
795
796 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
797 {
798 "timestamp": 0.0,
799 "start_timestamp": 0.0,
800 "exclusive_time": 0.0,
801 "op": "default",
802 "span_id": "e342abb1214ca181",
803 "trace_id": "3c79f60c11214eb38604f4ae0781bfb2",
804 "status": "ok",
805 "data": {
806 "sentry.origin": "auto.otlp.spans"
807 },
808 "links": [
809 {
810 "trace_id": "4c79f60c11214eb38604f4ae0781bfb2",
811 "span_id": "fa90fdead5f74052",
812 "sampled": true,
813 "attributes": {
814 "bool_key": true,
815 "double_key": 1.23,
816 "int_key": 123,
817 "str_key": "str_value"
818 }
819 }
820 ]
821 }
822 "#);
823 }
824
825 #[test]
826 fn parse_span_error_status() {
827 let json = r#"{
828 "traceId": "89143b0763095bd9c9955e8175d1fb23",
829 "spanId": "e342abb1214ca181",
830 "status": {
831 "code": 2,
832 "message": "2 is the error status code"
833 }
834 }"#;
835 let otel_span: OtelSpan = serde_json::from_str(json).unwrap();
836 let event_span = otel_to_sentry_span(otel_span, None, None);
837 let annotated_span: Annotated<EventSpan> = Annotated::new(event_span);
838 insta::assert_json_snapshot!(SerializableAnnotated(&annotated_span), @r#"
839 {
840 "timestamp": 0.0,
841 "start_timestamp": 0.0,
842 "exclusive_time": 0.0,
843 "op": "default",
844 "span_id": "e342abb1214ca181",
845 "trace_id": "89143b0763095bd9c9955e8175d1fb23",
846 "status": "internal_error",
847 "data": {
848 "sentry.origin": "auto.otlp.spans",
849 "sentry.status.message": "2 is the error status code"
850 },
851 "links": []
852 }
853 "#);
854 }
855}