@@ -1503,4 +1503,185 @@ mod json_serde {
1503
1503
}
1504
1504
}
1505
1505
}
1506
+ #[ cfg( feature = "metrics" ) ]
1507
+ mod metrics_with_nan {
1508
+ use super :: * ;
1509
+ use opentelemetry_proto:: tonic:: metrics:: v1:: summary_data_point:: ValueAtQuantile ;
1510
+ use opentelemetry_proto:: tonic:: metrics:: v1:: Summary ;
1511
+ use opentelemetry_proto:: tonic:: metrics:: v1:: SummaryDataPoint ;
1512
+
1513
+ fn value_with_nan ( ) -> ExportMetricsServiceRequest {
1514
+ ExportMetricsServiceRequest {
1515
+ resource_metrics : vec ! [ ResourceMetrics {
1516
+ resource: Some ( Resource {
1517
+ attributes: vec![ KeyValue {
1518
+ key: String :: from( "service.name" ) ,
1519
+ value: None ,
1520
+ } ] ,
1521
+ dropped_attributes_count: 0 ,
1522
+ } ) ,
1523
+ scope_metrics: vec![ ScopeMetrics {
1524
+ scope: None ,
1525
+ metrics: vec![ Metric {
1526
+ name: String :: from( "example_metric" ) ,
1527
+ description: String :: from( "A sample metric with NaN values" ) ,
1528
+ unit: String :: from( "1" ) ,
1529
+ metadata: vec![ ] ,
1530
+ data: Some (
1531
+ opentelemetry_proto:: tonic:: metrics:: v1:: metric:: Data :: Summary (
1532
+ Summary {
1533
+ data_points: vec![ SummaryDataPoint {
1534
+ attributes: vec![ ] ,
1535
+ start_time_unix_nano: 0 ,
1536
+ time_unix_nano: 0 ,
1537
+ count: 100 ,
1538
+ sum: 500.0 ,
1539
+ quantile_values: vec![
1540
+ ValueAtQuantile {
1541
+ quantile: 0.5 ,
1542
+ value: f64 :: NAN ,
1543
+ } ,
1544
+ ValueAtQuantile {
1545
+ quantile: 0.9 ,
1546
+ value: f64 :: NAN ,
1547
+ } ,
1548
+ ] ,
1549
+ flags: 0 ,
1550
+ } ] ,
1551
+ } ,
1552
+ ) ,
1553
+ ) ,
1554
+ } ] ,
1555
+ schema_url: String :: new( ) ,
1556
+ } ] ,
1557
+ schema_url: String :: new( ) ,
1558
+ } ] ,
1559
+ }
1560
+ }
1561
+
1562
+ // language=json
1563
+ const CANONICAL_WITH_NAN : & str = r#"{
1564
+ "resourceMetrics": [
1565
+ {
1566
+ "resource": {
1567
+ "attributes": [
1568
+ {
1569
+ "key": "service.name",
1570
+ "value": null
1571
+ }
1572
+ ],
1573
+ "droppedAttributesCount": 0
1574
+ },
1575
+ "scopeMetrics": [
1576
+ {
1577
+ "scope": null,
1578
+ "metrics": [
1579
+ {
1580
+ "name": "example_metric",
1581
+ "description": "A sample metric with NaN values",
1582
+ "unit": "1",
1583
+ "metadata": [],
1584
+ "summary": {
1585
+ "dataPoints": [
1586
+ {
1587
+ "attributes": [],
1588
+ "startTimeUnixNano": "0",
1589
+ "timeUnixNano": "0",
1590
+ "count": "100",
1591
+ "sum": 500.0,
1592
+ "quantileValues": [
1593
+ {
1594
+ "quantile": 0.5,
1595
+ "value": "NaN"
1596
+ },
1597
+ {
1598
+ "quantile": 0.9,
1599
+ "value": "NaN"
1600
+ }
1601
+ ],
1602
+ "flags": 0
1603
+ }
1604
+ ]
1605
+ }
1606
+ }
1607
+ ],
1608
+ "schemaUrl": ""
1609
+ }
1610
+ ],
1611
+ "schemaUrl": ""
1612
+ }
1613
+ ]
1614
+ }"# ;
1615
+
1616
+ #[ test]
1617
+ fn serialize_with_nan ( ) {
1618
+ let input: ExportMetricsServiceRequest = value_with_nan ( ) ;
1619
+
1620
+ // Serialize the structure to JSON
1621
+ let actual = serde_json:: to_string_pretty ( & input) . expect ( "serialization must succeed" ) ;
1622
+
1623
+ // Normalize both the actual and expected JSON for comparison
1624
+ let actual_value: serde_json:: Value =
1625
+ serde_json:: from_str ( & actual) . expect ( "valid JSON" ) ;
1626
+ let expected_value: serde_json:: Value =
1627
+ serde_json:: from_str ( CANONICAL_WITH_NAN ) . expect ( "valid JSON" ) ;
1628
+
1629
+ // Compare the normalized JSON values
1630
+ assert_eq ! ( actual_value, expected_value) ;
1631
+ }
1632
+
1633
+ #[ test]
1634
+ fn deserialize_with_nan ( ) {
1635
+ let actual: ExportMetricsServiceRequest =
1636
+ serde_json:: from_str ( CANONICAL_WITH_NAN ) . expect ( "deserialization must succeed" ) ;
1637
+
1638
+ // Ensure the deserialized structure matches the expected values
1639
+ assert_eq ! ( actual. resource_metrics. len( ) , 1 ) ;
1640
+
1641
+ let resource_metric = & actual. resource_metrics [ 0 ] ;
1642
+ assert_eq ! (
1643
+ resource_metric. resource. as_ref( ) . unwrap( ) . attributes. len( ) ,
1644
+ 1
1645
+ ) ;
1646
+ assert_eq ! (
1647
+ resource_metric. resource. as_ref( ) . unwrap( ) . attributes[ 0 ] . key,
1648
+ "service.name"
1649
+ ) ;
1650
+ assert ! ( resource_metric. resource. as_ref( ) . unwrap( ) . attributes[ 0 ]
1651
+ . value
1652
+ . is_none( ) ) ;
1653
+
1654
+ assert_eq ! ( resource_metric. scope_metrics. len( ) , 1 ) ;
1655
+
1656
+ let scope_metric = & resource_metric. scope_metrics [ 0 ] ;
1657
+ assert ! ( scope_metric. scope. is_none( ) ) ;
1658
+ assert_eq ! ( scope_metric. metrics. len( ) , 1 ) ;
1659
+
1660
+ let metric = & scope_metric. metrics [ 0 ] ;
1661
+ assert_eq ! ( metric. name, "example_metric" ) ;
1662
+ assert_eq ! ( metric. description, "A sample metric with NaN values" ) ;
1663
+ assert_eq ! ( metric. unit, "1" ) ;
1664
+
1665
+ if let Some ( opentelemetry_proto:: tonic:: metrics:: v1:: metric:: Data :: Summary ( summary) ) =
1666
+ & metric. data
1667
+ {
1668
+ assert_eq ! ( summary. data_points. len( ) , 1 ) ;
1669
+
1670
+ let data_point = & summary. data_points [ 0 ] ;
1671
+ assert_eq ! ( data_point. attributes. len( ) , 0 ) ;
1672
+ assert_eq ! ( data_point. start_time_unix_nano, 0 ) ;
1673
+ assert_eq ! ( data_point. time_unix_nano, 0 ) ;
1674
+ assert_eq ! ( data_point. count, 100 ) ;
1675
+ assert_eq ! ( data_point. sum, 500.0 ) ;
1676
+
1677
+ assert_eq ! ( data_point. quantile_values. len( ) , 2 ) ;
1678
+
1679
+ // Verify that quantile values are NaN
1680
+ assert ! ( data_point. quantile_values[ 0 ] . value. is_nan( ) ) ;
1681
+ assert ! ( data_point. quantile_values[ 1 ] . value. is_nan( ) ) ;
1682
+ } else {
1683
+ panic ! ( "Expected metric data to be of type Summary" ) ;
1684
+ }
1685
+ }
1686
+ }
1506
1687
}
0 commit comments