Skip to content

Commit f2ccaf7

Browse files
authored
[geneva] Nullable annotations for the metrics folder (open-telemetry#2218)
1 parent e606128 commit f2ccaf7

12 files changed

+113
-50
lines changed

src/OpenTelemetry.Exporter.Geneva/Internal/ConnectionStringBuilder.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,16 @@ public string ParseUnixDomainSocketPath()
244244
}
245245
}
246246

247+
public bool TryGetMetricsAccountAndNamespace(
248+
[NotNullWhen(true)] out string? metricsAccount,
249+
[NotNullWhen(true)] out string? metricsNamespace)
250+
{
251+
var hasAccount = this.parts.TryGetValue(nameof(this.Account), out metricsAccount);
252+
var hasNamespace = this.parts.TryGetValue(nameof(this.Namespace), out metricsNamespace);
253+
254+
return hasAccount && hasNamespace;
255+
}
256+
247257
/// <summary>
248258
/// Replace first charater of string if it matches with <paramref name="oldChar"/> with <paramref name="newChar"/>.
249259
/// </summary>

src/OpenTelemetry.Exporter.Geneva/Metrics/MetricSerializer.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright The OpenTelemetry Authors
22
// SPDX-License-Identifier: Apache-2.0
33

4+
#nullable enable
5+
46
using System.Runtime.CompilerServices;
57
using System.Runtime.InteropServices;
68
using System.Text;
@@ -174,11 +176,11 @@ internal static class MetricSerializer
174176
/// <param name="bufferIndex">Index of the buffer.</param>
175177
/// <param name="value">The value.</param>
176178
[MethodImpl(MethodImplOptions.AggressiveInlining)]
177-
public static void SerializeString(byte[] buffer, ref int bufferIndex, string value)
179+
public static void SerializeString(byte[] buffer, ref int bufferIndex, string? value)
178180
{
179181
if (!string.IsNullOrEmpty(value))
180182
{
181-
if (bufferIndex + value.Length + sizeof(short) >= buffer.Length)
183+
if (bufferIndex + value!.Length + sizeof(short) >= buffer.Length)
182184
{
183185
// TODO: What should we do when the data is invalid?
184186
}
@@ -391,12 +393,13 @@ public static unsafe void SerializeFloat64(byte[] buffer, ref int bufferIndex, d
391393
/// <param name="bufferIndex">Index of the buffer.</param>
392394
/// <param name="value">The value.</param>
393395
[MethodImpl(MethodImplOptions.AggressiveInlining)]
394-
public static void SerializeBase128String(byte[] buffer, ref int bufferIndex, string value)
396+
public static void SerializeBase128String(byte[] buffer, ref int bufferIndex, string? value)
395397
{
396398
if (!string.IsNullOrEmpty(value))
397399
{
398-
if (bufferIndex + value.Length + sizeof(short) >= buffer.Length)
400+
if (bufferIndex + value!.Length + sizeof(short) >= buffer.Length)
399401
{
402+
// TODO: What should we do when the data is invalid?
400403
}
401404

402405
var encodedValue = Encoding.UTF8.GetBytes(value);

src/OpenTelemetry.Exporter.Geneva/Metrics/OtlpProtobuf/FieldNumberConstants.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright The OpenTelemetry Authors
22
// SPDX-License-Identifier: Apache-2.0
33

4+
#nullable enable
5+
46
using System.Runtime.CompilerServices;
57
using OpenTelemetry.Metrics;
68

src/OpenTelemetry.Exporter.Geneva/Metrics/OtlpProtobuf/OtlpProtobufMetricExporter.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright The OpenTelemetry Authors
22
// SPDX-License-Identifier: Apache-2.0
33

4+
#nullable enable
5+
46
using System.Diagnostics;
57
using System.Runtime.InteropServices;
68
using OpenTelemetry.Metrics;
@@ -19,11 +21,11 @@ internal sealed class OtlpProtobufMetricExporter : IDisposable
1921
public OtlpProtobufMetricExporter(
2022
Func<Resource> getResource,
2123
ConnectionStringBuilder connectionStringBuilder,
22-
IReadOnlyDictionary<string, object> prepopulatedMetricDimensions)
24+
IReadOnlyDictionary<string, object>? prepopulatedMetricDimensions)
2325
{
2426
Debug.Assert(getResource != null, "getResource was null");
2527

26-
this.getResource = getResource;
28+
this.getResource = getResource!;
2729

2830
#if NET6_0_OR_GREATER
2931
IMetricDataTransport transport = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)

src/OpenTelemetry.Exporter.Geneva/Metrics/OtlpProtobuf/OtlpProtobufSerializer.cs

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Copyright The OpenTelemetry Authors
22
// SPDX-License-Identifier: Apache-2.0
33

4+
#nullable enable
5+
6+
using System.Diagnostics;
47
using System.Globalization;
58
using OpenTelemetry.Metrics;
69
using 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)

src/OpenTelemetry.Exporter.Geneva/Metrics/OtlpProtobuf/ProtobufSerializerHelper.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright The OpenTelemetry Authors
22
// SPDX-License-Identifier: Apache-2.0
33

4+
#nullable enable
5+
46
using System.Buffers.Binary;
57
using System.Runtime.CompilerServices;
68
using System.Text;

src/OpenTelemetry.Exporter.Geneva/Metrics/OtlpProtobuf/TimestampHelpers.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright The OpenTelemetry Authors
22
// SPDX-License-Identifier: Apache-2.0
33

4+
#nullable enable
5+
46
namespace OpenTelemetry.Exporter.Geneva;
57

68
/// <summary>

src/OpenTelemetry.Exporter.Geneva/Metrics/OtlpProtobuf/WireType.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright The OpenTelemetry Authors
22
// SPDX-License-Identifier: Apache-2.0
33

4+
#nullable enable
5+
46
namespace OpenTelemetry.Exporter.Geneva;
57

68
/// <summary>

src/OpenTelemetry.Exporter.Geneva/Metrics/TlvMetricExporter.cs

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright The OpenTelemetry Authors
22
// SPDX-License-Identifier: Apache-2.0
33

4+
#nullable enable
5+
46
using System.Globalization;
57
using System.Runtime.CompilerServices;
68
using System.Runtime.InteropServices;
@@ -14,15 +16,17 @@ internal sealed class TlvMetricExporter : IDisposable
1416
private readonly ushort prepopulatedDimensionsCount;
1517
private readonly int fixedPayloadStartIndex;
1618
private readonly IMetricDataTransport metricDataTransport;
17-
private readonly List<byte[]> serializedPrepopulatedDimensionsKeys;
18-
private readonly List<byte[]> serializedPrepopulatedDimensionsValues;
19+
private readonly List<byte[]>? serializedPrepopulatedDimensionsKeys;
20+
private readonly List<byte[]>? serializedPrepopulatedDimensionsValues;
1921
private readonly byte[] buffer = new byte[GenevaMetricExporter.BufferSize];
2022
private readonly string defaultMonitoringAccount;
2123
private readonly string defaultMetricNamespace;
2224

2325
private bool isDisposed;
2426

25-
internal TlvMetricExporter(ConnectionStringBuilder connectionStringBuilder, IReadOnlyDictionary<string, object> prepopulatedMetricDimensions)
27+
internal TlvMetricExporter(
28+
ConnectionStringBuilder connectionStringBuilder,
29+
IReadOnlyDictionary<string, object>? prepopulatedMetricDimensions)
2630
{
2731
this.defaultMonitoringAccount = connectionStringBuilder.Account;
2832
this.defaultMetricNamespace = connectionStringBuilder.Namespace;
@@ -581,6 +585,21 @@ private static void SerializeHistogramBucketWithTLV(in HistogramBucket bucket, b
581585
MetricSerializer.SerializeUInt32(buffer, ref bufferIndex, Convert.ToUInt32(bucket.BucketCount));
582586
}
583587

588+
private static string? ConvertTagValueToString(object? value)
589+
{
590+
string? repr;
591+
try
592+
{
593+
repr = Convert.ToString(value, CultureInfo.InvariantCulture);
594+
}
595+
catch
596+
{
597+
repr = $"ERROR: type {value?.GetType().FullName} is not supported";
598+
}
599+
600+
return repr;
601+
}
602+
584603
[MethodImpl(MethodImplOptions.AggressiveInlining)]
585604
private void SerializeDimensionsAndGetCustomAccountNamespace(in ReadOnlyTagCollection tags, byte[] buffer, ref int bufferIndex, out string monitoringAccount, out string metricNamespace)
586605
{
@@ -602,7 +621,7 @@ private void SerializeDimensionsAndGetCustomAccountNamespace(in ReadOnlyTagColle
602621
// Serialize PrepopulatedDimensions keys
603622
for (ushort i = 0; i < this.prepopulatedDimensionsCount; i++)
604623
{
605-
MetricSerializer.SerializeEncodedString(buffer, ref bufferIndex, this.serializedPrepopulatedDimensionsKeys[i]);
624+
MetricSerializer.SerializeEncodedString(buffer, ref bufferIndex, this.serializedPrepopulatedDimensionsKeys![i]);
606625
}
607626

608627
if (this.prepopulatedDimensionsCount > 0)
@@ -635,7 +654,7 @@ private void SerializeDimensionsAndGetCustomAccountNamespace(in ReadOnlyTagColle
635654
// Serialize PrepopulatedDimensions values
636655
for (ushort i = 0; i < this.prepopulatedDimensionsCount; i++)
637656
{
638-
MetricSerializer.SerializeEncodedString(buffer, ref bufferIndex, this.serializedPrepopulatedDimensionsValues[i]);
657+
MetricSerializer.SerializeEncodedString(buffer, ref bufferIndex, this.serializedPrepopulatedDimensionsValues![i]);
639658
}
640659

641660
// Serialize MetricPoint Dimension values
@@ -661,8 +680,8 @@ private void SerializeDimensionsAndGetCustomAccountNamespace(in ReadOnlyTagColle
661680
continue;
662681
}
663682

664-
var dimensionValue = Convert.ToString(tag.Value, CultureInfo.InvariantCulture);
665-
if (dimensionValue.Length > GenevaMetricExporter.MaxDimensionValueSize)
683+
var dimensionValue = ConvertTagValueToString(tag.Value);
684+
if (dimensionValue?.Length > GenevaMetricExporter.MaxDimensionValueSize)
666685
{
667686
// TODO: Data Validation
668687
}
@@ -693,8 +712,11 @@ private List<byte[]> SerializePrepopulatedDimensionsValues(IEnumerable<object> v
693712
var serializedValues = new List<byte[]>(this.prepopulatedDimensionsCount);
694713
foreach (var value in values)
695714
{
696-
var valueAsString = Convert.ToString(value, CultureInfo.InvariantCulture);
697-
serializedValues.Add(Encoding.UTF8.GetBytes(valueAsString));
715+
var valueAsString = ConvertTagValueToString(value);
716+
if (valueAsString != null)
717+
{
718+
serializedValues.Add(Encoding.UTF8.GetBytes(valueAsString));
719+
}
698720
}
699721

700722
return serializedValues;

0 commit comments

Comments
 (0)