11// Copyright The OpenTelemetry Authors
22// SPDX-License-Identifier: Apache-2.0
33
4+ #nullable enable
5+
6+ using System . Diagnostics ;
47using System . Globalization ;
58using OpenTelemetry . Metrics ;
69using OpenTelemetry . Resources ;
@@ -14,13 +17,13 @@ internal sealed class OtlpProtobufSerializer
1417 private const int TagAndLengthSize = 4 ;
1518
1619 private readonly Dictionary < string , List < Metric > > scopeMetrics = new ( ) ;
17- private readonly string metricNamespace ;
18- private readonly string metricAccount ;
19- private readonly byte [ ] prepopulatedNumberDataPointAttributes ;
20+ private readonly string ? metricNamespace ;
21+ private readonly string ? metricAccount ;
22+ private readonly byte [ ] ? prepopulatedNumberDataPointAttributes ;
2023 private readonly int prepopulatedNumberDataPointAttributesLength ;
21- private readonly byte [ ] prepopulatedHistogramDataPointAttributes ;
24+ private readonly byte [ ] ? prepopulatedHistogramDataPointAttributes ;
2225 private readonly int prepopulatedHistogramDataPointAttributesLength ;
23- private readonly byte [ ] prepopulatedExponentialHistogramDataPointAttributes ;
26+ private readonly byte [ ] ? prepopulatedExponentialHistogramDataPointAttributes ;
2427 private readonly int prepopulatedExponentialHistogramDataPointAttributesLength ;
2528 private int resourceMetricTagAndLengthIndex ;
2629 private int scopeMetricsTagAndLengthIndex ;
@@ -34,50 +37,46 @@ internal sealed class OtlpProtobufSerializer
3437 private int metricPointValueIndex ;
3538 private ExportResult metricExportResult ;
3639
37- public OtlpProtobufSerializer ( IMetricDataTransport metricDataTransport , ConnectionStringBuilder connectionStringBuilder , IReadOnlyDictionary < string , object > prepopulatedMetricDimensions )
40+ public OtlpProtobufSerializer (
41+ IMetricDataTransport metricDataTransport ,
42+ ConnectionStringBuilder ? connectionStringBuilder ,
43+ IReadOnlyDictionary < string , object > ? prepopulatedMetricDimensions )
3844 {
39- this . MetricDataTransport = metricDataTransport ;
45+ Debug . Assert ( metricDataTransport != null , "metricDataTransport was null" ) ;
46+
47+ this . MetricDataTransport = metricDataTransport ! ;
4048
4149 // Taking a arbitrary number here for writing attributes.
4250 byte [ ] temp = new byte [ 20000 ] ;
4351 if ( prepopulatedMetricDimensions != null )
4452 {
4553 // Initialize numberDataPoint attributes.
4654 int cursor = 0 ;
47- SerializeTags ( temp , ref cursor , prepopulatedMetricDimensions , FieldNumberConstants . NumberDataPoint_attributes ) ;
55+ SerializeTags ( temp , ref cursor , prepopulatedMetricDimensions ! , FieldNumberConstants . NumberDataPoint_attributes ) ;
4856 this . prepopulatedNumberDataPointAttributes = new byte [ cursor ] ;
4957 Array . Copy ( temp , this . prepopulatedNumberDataPointAttributes , cursor ) ;
5058 this . prepopulatedNumberDataPointAttributesLength = cursor ;
5159
5260 // Initialize histogramDataPoint attributes.
5361 cursor = 0 ;
54- SerializeTags ( temp , ref cursor , prepopulatedMetricDimensions , FieldNumberConstants . HistogramDataPoint_attributes ) ;
62+ SerializeTags ( temp , ref cursor , prepopulatedMetricDimensions ! , FieldNumberConstants . HistogramDataPoint_attributes ) ;
5563 this . prepopulatedHistogramDataPointAttributes = new byte [ cursor ] ;
5664 Array . Copy ( temp , this . prepopulatedHistogramDataPointAttributes , cursor ) ;
5765 this . prepopulatedHistogramDataPointAttributesLength = cursor ;
5866
5967 cursor = 0 ;
60- SerializeTags ( temp , ref cursor , prepopulatedMetricDimensions , FieldNumberConstants . ExponentialHistogramDataPoint_attributes ) ;
68+ SerializeTags ( temp , ref cursor , prepopulatedMetricDimensions ! , FieldNumberConstants . ExponentialHistogramDataPoint_attributes ) ;
6169 this . prepopulatedExponentialHistogramDataPointAttributes = new byte [ cursor ] ;
6270 Array . Copy ( temp , this . prepopulatedExponentialHistogramDataPointAttributes , cursor ) ;
6371 this . prepopulatedExponentialHistogramDataPointAttributesLength = cursor ;
6472 }
6573
66- try
74+ if ( connectionStringBuilder ? . TryGetMetricsAccountAndNamespace (
75+ out var metricsAccount ,
76+ out var metricsNamespace ) == true )
6777 {
68- if ( connectionStringBuilder != null && connectionStringBuilder . Namespace != null )
69- {
70- this . metricNamespace = connectionStringBuilder . Namespace ;
71- }
72-
73- if ( connectionStringBuilder != null && connectionStringBuilder . Account != null )
74- {
75- this . metricAccount = connectionStringBuilder . Account ;
76- }
77- }
78- catch
79- {
80- // TODO: add log.
78+ this . metricAccount = metricsAccount ;
79+ this . metricNamespace = metricsNamespace ;
8180 }
8281 }
8382
@@ -99,7 +98,7 @@ internal static void WriteInstrumentDetails(byte[] buffer, ref int cursor, Metri
9998 }
10099 }
101100
102- internal static void SerializeInstrumentationScope ( byte [ ] buffer , ref int cursor , string name , IEnumerable < KeyValuePair < string , object > > meterTags )
101+ internal static void SerializeInstrumentationScope ( byte [ ] buffer , ref int cursor , string name , IEnumerable < KeyValuePair < string , object ? > > ? meterTags )
103102 {
104103 int tagAndLengthIndex = cursor ;
105104 cursor += TagAndLengthSize ;
@@ -145,7 +144,7 @@ internal static void SerializeTag(byte[] buffer, ref int cursor, string key, obj
145144 {
146145 case char :
147146 case string :
148- ProtobufSerializerHelper . WriteStringTag ( buffer , ref cursor , FieldNumberConstants . AnyValue_string_value , Convert . ToString ( value , CultureInfo . InvariantCulture ) ) ;
147+ ProtobufSerializerHelper . WriteStringTag ( buffer , ref cursor , FieldNumberConstants . AnyValue_string_value , Convert . ToString ( value , CultureInfo . InvariantCulture ) ! ) ;
149148 break ;
150149 case bool b :
151150 ProtobufSerializerHelper . WriteBoolWithTag ( buffer , ref cursor , FieldNumberConstants . AnyValue_bool_value , ( bool ) value ) ;
@@ -165,7 +164,17 @@ internal static void SerializeTag(byte[] buffer, ref int cursor, string key, obj
165164 ProtobufSerializerHelper . WriteDoubleWithTag ( buffer , ref cursor , FieldNumberConstants . AnyValue_double_value , Convert . ToDouble ( value , CultureInfo . InvariantCulture ) ) ;
166165 break ;
167166 default :
168- ProtobufSerializerHelper . WriteStringTag ( buffer , ref cursor , FieldNumberConstants . AnyValue_string_value , Convert . ToString ( value , CultureInfo . InvariantCulture ) ) ;
167+ string repr ;
168+ try
169+ {
170+ repr = Convert . ToString ( value , CultureInfo . InvariantCulture ) ?? string . Empty ;
171+ }
172+ catch
173+ {
174+ repr = $ "ERROR: type { value . GetType ( ) . FullName } is not supported";
175+ }
176+
177+ ProtobufSerializerHelper . WriteStringTag ( buffer , ref cursor , FieldNumberConstants . AnyValue_string_value , repr ) ;
169178 break ;
170179
171180 // TODO: Handle array type.
@@ -277,11 +286,11 @@ private static void SerializeExemplar<T>(byte[] buffer, ref int cursor, in Exemp
277286 {
278287 // Casting to ulong is ok here as the bit representation for long versus ulong will be the same
279288 // The difference would in the way the bit representation is interpreted on decoding side (signed versus unsigned)
280- ProtobufSerializerHelper . WriteFixed64WithTag ( buffer , ref cursor , FieldNumberConstants . Exemplar_as_int , ( ulong ) ( long ) ( object ) value ) ;
289+ ProtobufSerializerHelper . WriteFixed64WithTag ( buffer , ref cursor , FieldNumberConstants . Exemplar_as_int , ( ulong ) ( long ) ( object ) value ! ) ;
281290 }
282291 else if ( typeof ( T ) == typeof ( double ) )
283292 {
284- ProtobufSerializerHelper . WriteDoubleWithTag ( buffer , ref cursor , FieldNumberConstants . Exemplar_as_double , ( double ) ( object ) value ) ;
293+ ProtobufSerializerHelper . WriteDoubleWithTag ( buffer , ref cursor , FieldNumberConstants . Exemplar_as_double , ( double ) ( object ) value ! ) ;
285294 }
286295
287296 var time = ( ulong ) exemplar . Timestamp . ToUnixTimeNanoseconds ( ) ;
@@ -306,11 +315,14 @@ private static void SerializeExemplarTags(byte[] buffer, ref int cursor, ReadOnl
306315 {
307316 foreach ( var tag in tags )
308317 {
309- SerializeTag ( buffer , ref cursor , tag . Key , tag . Value , FieldNumberConstants . Exemplar_attributes ) ;
318+ if ( tag . Value != null )
319+ {
320+ SerializeTag ( buffer , ref cursor , tag . Key , tag . Value , FieldNumberConstants . Exemplar_attributes ) ;
321+ }
310322 }
311323 }
312324
313- private static void SerializeTags ( byte [ ] buffer , ref int cursor , IEnumerable < KeyValuePair < string , object > > attributes , int fieldNumber )
325+ private static void SerializeTags ( byte [ ] buffer , ref int cursor , IEnumerable < KeyValuePair < string , object ? > > ? attributes , int fieldNumber )
314326 {
315327 if ( attributes != null )
316328 {
@@ -661,11 +673,11 @@ private void WriteNumberDataPoint<T>(byte[] buffer, ref int cursor, int fieldNum
661673 {
662674 // Casting to ulong is ok here as the bit representation for long versus ulong will be the same
663675 // The difference would in the way the bit representation is interpreted on decoding side (signed versus unsigned)
664- ProtobufSerializerHelper . WriteFixed64WithTag ( buffer , ref cursor , FieldNumberConstants . NumberDataPoint_as_int , ( ulong ) ( long ) ( object ) value ) ;
676+ ProtobufSerializerHelper . WriteFixed64WithTag ( buffer , ref cursor , FieldNumberConstants . NumberDataPoint_as_int , ( ulong ) ( long ) ( object ) value ! ) ;
665677 }
666678 else if ( typeof ( T ) == typeof ( double ) )
667679 {
668- ProtobufSerializerHelper . WriteDoubleWithTag ( buffer , ref cursor , FieldNumberConstants . NumberDataPoint_as_double , ( double ) ( object ) value ) ;
680+ ProtobufSerializerHelper . WriteDoubleWithTag ( buffer , ref cursor , FieldNumberConstants . NumberDataPoint_as_double , ( double ) ( object ) value ! ) ;
669681 }
670682
671683 var startTime = ( ulong ) metricPoint . StartTime . ToUnixTimeNanoseconds ( ) ;
@@ -740,7 +752,7 @@ private void SerializeResource(byte[] buffer, ref int cursor, Resource resource)
740752 cursor += TagAndLengthSize ;
741753 int valueIndex = cursor ;
742754
743- SerializeTags ( buffer , ref cursor , resource . Attributes , FieldNumberConstants . Resource_attributes ) ;
755+ SerializeTags ( buffer , ref cursor , resource . Attributes ! , FieldNumberConstants . Resource_attributes ) ;
744756
745757 // TODO: check to see if should de-dupe in case the values are also provided via resource attributes.
746758 if ( this . metricAccount != null )
0 commit comments