diff --git a/src/OpenTelemetry.Exporter.Geneva/TLDExporter/JsonSerializer.cs b/src/OpenTelemetry.Exporter.Geneva/TLDExporter/JsonSerializer.cs index 955f55a26f..57ccdbfcd3 100644 --- a/src/OpenTelemetry.Exporter.Geneva/TLDExporter/JsonSerializer.cs +++ b/src/OpenTelemetry.Exporter.Geneva/TLDExporter/JsonSerializer.cs @@ -1,6 +1,8 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#nullable enable + using System.Globalization; using System.Runtime.CompilerServices; using System.Text; @@ -46,7 +48,7 @@ public static int SerializeNull(byte[] buffer, int cursor) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string SerializeString(string value) + public static string SerializeString(string? value) { var buffer = ThreadLocalBuffer.Value; if (buffer == null) @@ -60,7 +62,7 @@ public static string SerializeString(string value) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int SerializeString(byte[] buffer, int cursor, string value) + public static int SerializeString(byte[] buffer, int cursor, string? value) { if (value == null) { @@ -90,7 +92,7 @@ public static int SerializeString(byte[] buffer, int cursor, ReadOnlySpan #endif [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string SerializeArray(T[] array) + public static string SerializeArray(T[]? array) { var buffer = ThreadLocalBuffer.Value; if (buffer == null) @@ -104,7 +106,7 @@ public static string SerializeArray(T[] array) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int SerializeArray(byte[] buffer, int cursor, T[] array) + public static int SerializeArray(byte[] buffer, int cursor, T[]? array) { if (array == null) { @@ -128,7 +130,7 @@ public static int SerializeArray(byte[] buffer, int cursor, T[] array) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string SerializeMap(IEnumerable> map) + public static string SerializeMap(IEnumerable>? map) { var buffer = ThreadLocalBuffer.Value; if (buffer == null) @@ -142,7 +144,7 @@ public static string SerializeMap(IEnumerable> map) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte[] SerializeKeyValuePairsListAsBytes(List> listKVp, out int count) + public static byte[] SerializeKeyValuePairsListAsBytes(List>? listKVp, out int count) { var buffer = ThreadLocalBuffer.Value; if (buffer == null) @@ -156,7 +158,7 @@ public static byte[] SerializeKeyValuePairsListAsBytes(List> map) + public static int SerializeMap(byte[] buffer, int cursor, IEnumerable>? map) { if (map == null) { @@ -183,7 +185,7 @@ public static int SerializeMap(byte[] buffer, int cursor, IEnumerable> listKvp) + public static int SerializeKeyValuePairList(byte[] buffer, int cursor, List>? listKvp) { if (listKvp == null) { @@ -210,7 +212,7 @@ public static int SerializeKeyValuePairList(byte[] buffer, int cursor, List tmp = stackalloc char[MAX_STACK_ALLOC_SIZE_IN_BYTES / sizeof(char)]; - (obj as ISpanFormattable).TryFormat(tmp, out int charsWritten, default, CultureInfo.InvariantCulture); + ((ISpanFormattable)obj).TryFormat(tmp, out int charsWritten, default, CultureInfo.InvariantCulture); return WriteString(buffer, cursor, tmp.Slice(0, charsWritten)); case DateTime dt: tmp = stackalloc char[MAX_STACK_ALLOC_SIZE_IN_BYTES / sizeof(char)]; @@ -297,7 +299,7 @@ public static int Serialize(byte[] buffer, int cursor, object obj) return SerializeArray(buffer, cursor, vdtarray); case string v: return SerializeString(buffer, cursor, v); - case IEnumerable> v: + case IEnumerable> v: return SerializeMap(buffer, cursor, v); case object[] v: return SerializeArray(buffer, cursor, v); @@ -314,7 +316,7 @@ public static int Serialize(byte[] buffer, int cursor, object obj) #endif default: - string repr; + string? repr; try { repr = Convert.ToString(obj, CultureInfo.InvariantCulture); diff --git a/src/OpenTelemetry.Exporter.Geneva/TLDExporter/TldExporter.cs b/src/OpenTelemetry.Exporter.Geneva/TLDExporter/TldExporter.cs index a7f6f3ba33..92b7ed47a0 100644 --- a/src/OpenTelemetry.Exporter.Geneva/TLDExporter/TldExporter.cs +++ b/src/OpenTelemetry.Exporter.Geneva/TLDExporter/TldExporter.cs @@ -1,6 +1,9 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#nullable enable + +using System.Diagnostics; using System.Globalization; using System.Runtime.CompilerServices; using System.Text; @@ -35,6 +38,8 @@ internal abstract class TldExporter [MethodImpl(MethodImplOptions.AggressiveInlining)] protected static void Serialize(EventBuilder eb, string key, object value) { + Debug.Assert(value != null, "value was null"); + switch (value) { case bool vb: @@ -124,11 +129,11 @@ protected static void Serialize(EventBuilder eb, string key, object value) string repr; try { - repr = Convert.ToString(value, CultureInfo.InvariantCulture); + repr = Convert.ToString(value, CultureInfo.InvariantCulture) ?? string.Empty; } catch { - repr = $"ERROR: type {value.GetType().FullName} is not supported"; + repr = $"ERROR: type {value!.GetType().FullName} is not supported"; } eb.AddCountedAnsiString(key, repr, Encoding.UTF8, 0, Math.Min(repr.Length, StringLengthLimit)); diff --git a/src/OpenTelemetry.Exporter.Geneva/TLDExporter/TldLogExporter.cs b/src/OpenTelemetry.Exporter.Geneva/TLDExporter/TldLogExporter.cs index 6053771a9d..b14a1f3f6a 100644 --- a/src/OpenTelemetry.Exporter.Geneva/TLDExporter/TldLogExporter.cs +++ b/src/OpenTelemetry.Exporter.Geneva/TLDExporter/TldLogExporter.cs @@ -1,6 +1,9 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#nullable enable + +using System.Diagnostics; using System.Globalization; using System.Runtime.CompilerServices; using System.Text; @@ -17,7 +20,7 @@ internal sealed class TldLogExporter : TldExporter, IDisposable // TODO: Is using a single ThreadLocal a better idea? private static readonly ThreadLocal EventBuilder = new(); - private static readonly ThreadLocal>> EnvProperties = new(); + private static readonly ThreadLocal>> EnvProperties = new(); private static readonly ThreadLocal[]> PartCFields = new(); // This is used to temporarily store the PartC fields from tags private static readonly Action ProcessScopeForIndividualColumnsAction = OnProcessScopeForIndividualColumns; private static readonly string[] LogLevels = new string[7] @@ -29,9 +32,9 @@ internal sealed class TldLogExporter : TldExporter, IDisposable private readonly byte partAFieldsCount = 1; // At least one field: time private readonly bool shouldPassThruTableMappings; private readonly string defaultEventName = "Log"; - private readonly HashSet customFields; - private readonly Dictionary tableMappings; - private readonly Tuple repeatedPartAFields; + private readonly HashSet? customFields; + private readonly Dictionary? tableMappings; + private readonly Tuple? repeatedPartAFields; private readonly ExceptionStackExportMode exceptionStackExportMode; private readonly EventProvider eventProvider; @@ -90,12 +93,7 @@ public TldLogExporter(GenevaExporterOptions options) var prePopulatedFieldsCount = (byte)(options.PrepopulatedFields.Count - 1); // PrepopulatedFields option has the key ".ver" added to it which is not needed for TLD this.partAFieldsCount += prePopulatedFieldsCount; - var eb = EventBuilder.Value; - if (eb == null) - { - eb = new EventBuilder(UncheckedASCIIEncoding.SharedInstance); - EventBuilder.Value = eb; - } + var eb = EventBuilder.Value ??= new EventBuilder(UncheckedASCIIEncoding.SharedInstance); eb.Reset("_"); // EventName does not matter here as we only need the serialized key-value pairs @@ -109,7 +107,7 @@ public TldLogExporter(GenevaExporterOptions options) continue; } - V40_PART_A_TLD_MAPPING.TryGetValue(key, out string replacementKey); + V40_PART_A_TLD_MAPPING.TryGetValue(key, out string? replacementKey); var keyToSerialize = replacementKey ?? key; Serialize(eb, keyToSerialize, value); @@ -120,15 +118,17 @@ public TldLogExporter(GenevaExporterOptions options) public ExportResult Export(in Batch batch) { - var result = ExportResult.Success; if (this.eventProvider.IsEnabled()) { - foreach (var activity in batch) + var result = ExportResult.Success; + + foreach (var logRecord in batch) { try { - this.SerializeLogRecord(activity); - this.eventProvider.Write(EventBuilder.Value); + var eventBuilder = this.SerializeLogRecord(logRecord); + + this.eventProvider.Write(eventBuilder); } catch (Exception ex) { @@ -136,13 +136,11 @@ public ExportResult Export(in Batch batch) result = ExportResult.Failure; } } - } - else - { - return ExportResult.Failure; + + return result; } - return result; + return ExportResult.Failure; } public void Dispose() @@ -166,9 +164,9 @@ public void Dispose() this.isDisposed = true; } - internal void SerializeLogRecord(LogRecord logRecord) + internal EventBuilder SerializeLogRecord(LogRecord logRecord) { - IReadOnlyList> listKvp; + IReadOnlyList>? listKvp; // `LogRecord.State` and `LogRecord.StateValues` were marked Obsolete in https://github.com/open-telemetry/opentelemetry-dotnet/pull/4334 #pragma warning disable 0618 @@ -179,7 +177,7 @@ internal void SerializeLogRecord(LogRecord logRecord) else { // Attempt to see if State could be ROL_KVP. - listKvp = logRecord.State as IReadOnlyList>; + listKvp = logRecord.State as IReadOnlyList>; } #pragma warning restore 0618 @@ -194,33 +192,27 @@ internal void SerializeLogRecord(LogRecord logRecord) // price = 100 // TODO: 2. Structured with strongly typed logging. - string eventName; - var categoryName = logRecord.CategoryName; + var categoryName = logRecord.CategoryName ?? this.defaultEventName; // If user configured explicit TableName, use it. - if (this.tableMappings != null && this.tableMappings.TryGetValue(categoryName, out eventName)) + if (this.tableMappings?.TryGetValue(categoryName, out var eventName) != true) { - } - else if (!this.shouldPassThruTableMappings) - { - eventName = this.defaultEventName; - } - else - { - // TODO: Avoid allocation - eventName = GetSanitizedCategoryName(categoryName); + if (!this.shouldPassThruTableMappings) + { + eventName = this.defaultEventName; + } + else + { + // TODO: Avoid allocation + eventName = GetSanitizedCategoryName(categoryName); + } } - var eb = EventBuilder.Value; - if (eb == null) - { - eb = new EventBuilder(UncheckedASCIIEncoding.SharedInstance); - EventBuilder.Value = eb; - } + var eb = EventBuilder.Value ??= new EventBuilder(UncheckedASCIIEncoding.SharedInstance); var timestamp = logRecord.Timestamp; - eb.Reset(eventName); + eb.Reset(eventName!); eb.AddUInt16("__csver__", 1024, EventOutType.Hex); var partAFieldsCountPatch = eb.AddStruct("PartA", this.partAFieldsCount); @@ -248,7 +240,12 @@ internal void SerializeLogRecord(LogRecord logRecord) // Part A - ex extension if (logRecord.Exception != null) { - eb.AddCountedAnsiString("ext_ex_type", logRecord.Exception.GetType().FullName, Encoding.UTF8); + var fullName = logRecord.Exception.GetType().FullName; + if (!string.IsNullOrEmpty(fullName)) + { + eb.AddCountedAnsiString("ext_ex_type", fullName, Encoding.UTF8); + } + eb.AddCountedAnsiString("ext_ex_msg", logRecord.Exception.Message, Encoding.UTF8); partAFieldsCount += 2; @@ -296,14 +293,9 @@ internal void SerializeLogRecord(LogRecord logRecord) bool namePopulated = false; byte partCFieldsCountFromState = 0; - var kvpArrayForPartCFields = PartCFields.Value; - if (kvpArrayForPartCFields == null) - { - kvpArrayForPartCFields = new KeyValuePair[120]; - PartCFields.Value = kvpArrayForPartCFields; - } + var kvpArrayForPartCFields = PartCFields.Value ??= new KeyValuePair[120]; - List> envPropertiesList = null; + List>? envPropertiesList = null; for (int i = 0; i < listKvp?.Count; i++) { @@ -313,7 +305,10 @@ internal void SerializeLogRecord(LogRecord logRecord) // i.e all Part B fields and opt-in Part C fields. if (entry.Key == "{OriginalFormat}") { - eb.AddCountedAnsiString("body", logRecord.FormattedMessage ?? Convert.ToString(entry.Value, CultureInfo.InvariantCulture), Encoding.UTF8); + eb.AddCountedAnsiString( + "body", + logRecord.FormattedMessage ?? Convert.ToString(entry.Value, CultureInfo.InvariantCulture) ?? string.Empty, + Encoding.UTF8); partBFieldsCount++; bodyPopulated = true; continue; @@ -345,18 +340,13 @@ internal void SerializeLogRecord(LogRecord logRecord) if (hasEnvProperties == 0) { hasEnvProperties = 1; - envPropertiesList = EnvProperties.Value; - if (envPropertiesList == null) - { - envPropertiesList = new List>(); - EnvProperties.Value = envPropertiesList; - } + envPropertiesList = EnvProperties.Value ??= new(); envPropertiesList.Clear(); } // TODO: This could lead to unbounded memory usage. - envPropertiesList.Add(new(entry.Key, entry.Value)); + envPropertiesList!.Add(new(entry.Key, entry.Value)); } } @@ -376,12 +366,7 @@ internal void SerializeLogRecord(LogRecord logRecord) // Part C // Prepare state for scopes - var dataForScopes = this.serializationData.Value; - if (dataForScopes == null) - { - dataForScopes = new SerializationDataForScopes(); - this.serializationData.Value = dataForScopes; - } + var dataForScopes = this.serializationData.Value ??= new(); dataForScopes.HasEnvProperties = hasEnvProperties; dataForScopes.PartCFieldsCountFromState = partCFieldsCountFromState; @@ -413,6 +398,8 @@ internal void SerializeLogRecord(LogRecord logRecord) eb.SetStructFieldCount(partCFieldsCountPatch, (byte)partCFieldsCount); } + + return eb; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -496,12 +483,16 @@ private static string GetSanitizedCategoryName(string categoryName) private static void OnProcessScopeForIndividualColumns(LogRecordScope scope, TldLogExporter state) { - var stateData = state.serializationData.Value; + Debug.Assert(state.serializationData.Value != null, "state.serializationData was null"); + Debug.Assert(PartCFields.Value != null, "PartCFields.Value was null"); + + var stateData = state.serializationData.Value!; var customFields = state.customFields; - var kvpArrayForPartCFields = PartCFields.Value; - var envPropertiesList = EnvProperties.Value; + var kvpArrayForPartCFields = PartCFields.Value!; - foreach (KeyValuePair scopeItem in scope) + List>? envPropertiesList = null; + + foreach (KeyValuePair scopeItem in scope) { if (string.IsNullOrEmpty(scopeItem.Key) || scopeItem.Key == "{OriginalFormat}") { @@ -521,17 +512,14 @@ private static void OnProcessScopeForIndividualColumns(LogRecordScope scope, Tld if (stateData.HasEnvProperties == 0) { stateData.HasEnvProperties = 1; - if (envPropertiesList == null) - { - envPropertiesList = new List>(); - EnvProperties.Value = envPropertiesList; - } + + envPropertiesList = EnvProperties.Value ??= new(); envPropertiesList.Clear(); } // TODO: This could lead to unbounded memory usage. - envPropertiesList.Add(new(scopeItem.Key, scopeItem.Value)); + envPropertiesList!.Add(new(scopeItem.Key, scopeItem.Value)); } } } diff --git a/src/OpenTelemetry.Exporter.Geneva/TLDExporter/TldTraceExporter.cs b/src/OpenTelemetry.Exporter.Geneva/TLDExporter/TldTraceExporter.cs index cddfa44470..33fdefecdd 100644 --- a/src/OpenTelemetry.Exporter.Geneva/TLDExporter/TldTraceExporter.cs +++ b/src/OpenTelemetry.Exporter.Geneva/TLDExporter/TldTraceExporter.cs @@ -1,6 +1,8 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#nullable enable + using System.Diagnostics; using System.Globalization; using System.Text; @@ -13,12 +15,12 @@ internal sealed class TldTraceExporter : TldExporter, IDisposable { // TODO: Is using a single ThreadLocal a better idea? private static readonly ThreadLocal EventBuilder = new(); - private static readonly ThreadLocal>> KeyValuePairs = new(); + private static readonly ThreadLocal>> KeyValuePairs = new(); private static readonly ThreadLocal[]> PartCFields = new(); // This is used to temporarily store the PartC fields from tags private static readonly string INVALID_SPAN_ID = default(ActivitySpanId).ToHexString(); - private static readonly Dictionary CS40_PART_B_MAPPING = new Dictionary + private static readonly Dictionary CS40_PART_B_MAPPING = new() { ["db.system"] = "dbSystem", ["db.name"] = "dbName", @@ -38,8 +40,8 @@ internal sealed class TldTraceExporter : TldExporter, IDisposable private readonly string partAName = "Span"; private readonly byte partAFieldsCount = 3; // At least three fields: time, ext_dt_traceId, ext_dt_spanId - private readonly HashSet customFields; - private readonly Tuple repeatedPartAFields; + private readonly HashSet? customFields; + private readonly Tuple? repeatedPartAFields; private readonly bool shouldIncludeTraceState; private readonly EventProvider eventProvider; @@ -67,9 +69,9 @@ public TldTraceExporter(GenevaExporterOptions options) // Seed customFields with Span PartB customFields.Add("azureResourceProvider"); - foreach (var name in CS40_PART_B_MAPPING.Values) + foreach (var mapping in CS40_PART_B_MAPPING) { - customFields.Add(name); + customFields.Add(mapping.Key); } foreach (var name in options.CustomFields) @@ -85,12 +87,7 @@ public TldTraceExporter(GenevaExporterOptions options) var prePopulatedFieldsCount = (byte)(options.PrepopulatedFields.Count - 1); // PrepopulatedFields option has the key ".ver" added to it which is not needed for TLD this.partAFieldsCount += prePopulatedFieldsCount; - var eb = EventBuilder.Value; - if (eb == null) - { - eb = new EventBuilder(UncheckedASCIIEncoding.SharedInstance); - EventBuilder.Value = eb; - } + var eb = EventBuilder.Value ??= new EventBuilder(UncheckedASCIIEncoding.SharedInstance); eb.Reset(this.partAName); @@ -104,7 +101,7 @@ public TldTraceExporter(GenevaExporterOptions options) continue; } - V40_PART_A_TLD_MAPPING.TryGetValue(key, out string replacementKey); + V40_PART_A_TLD_MAPPING.TryGetValue(key, out string? replacementKey); var keyToSerialize = replacementKey ?? key; Serialize(eb, keyToSerialize, value); @@ -117,15 +114,17 @@ public TldTraceExporter(GenevaExporterOptions options) public ExportResult Export(in Batch batch) { - var result = ExportResult.Success; if (this.eventProvider.IsEnabled()) { + var result = ExportResult.Success; + foreach (var activity in batch) { try { - this.SerializeActivity(activity); - this.eventProvider.Write(EventBuilder.Value); + var eventBuilder = this.SerializeActivity(activity); + + this.eventProvider.Write(eventBuilder); } catch (Exception ex) { @@ -133,13 +132,11 @@ public ExportResult Export(in Batch batch) result = ExportResult.Failure; } } - } - else - { - return ExportResult.Failure; + + return result; } - return result; + return ExportResult.Failure; } public void Dispose() @@ -162,14 +159,9 @@ public void Dispose() this.isDisposed = true; } - internal void SerializeActivity(Activity activity) + internal EventBuilder SerializeActivity(Activity activity) { - var eb = EventBuilder.Value; - if (eb == null) - { - eb = new EventBuilder(UncheckedASCIIEncoding.SharedInstance); - EventBuilder.Value = eb; - } + var eb = EventBuilder.Value ??= new EventBuilder(UncheckedASCIIEncoding.SharedInstance); eb.Reset(this.partAName); eb.AddUInt16("__csver__", 1024, EventOutType.Hex); @@ -209,7 +201,7 @@ internal void SerializeActivity(Activity activity) var traceStateString = activity.TraceStateString; if (!string.IsNullOrEmpty(traceStateString)) { - eb.AddCountedAnsiString("traceState", traceStateString, Encoding.UTF8); + eb.AddCountedAnsiString("traceState", traceStateString!, Encoding.UTF8); partBFieldsCount++; } } @@ -217,12 +209,7 @@ internal void SerializeActivity(Activity activity) var linkEnumerator = activity.EnumerateLinks(); if (linkEnumerator.MoveNext()) { - var keyValuePairsForLinks = KeyValuePairs.Value; - if (keyValuePairsForLinks == null) - { - keyValuePairsForLinks = new List>(); - KeyValuePairs.Value = keyValuePairsForLinks; - } + var keyValuePairsForLinks = KeyValuePairs.Value ??= new(); keyValuePairsForLinks.Clear(); @@ -247,61 +234,64 @@ internal void SerializeActivity(Activity activity) string statusDescription = string.Empty; int partCFieldsCountFromTags = 0; - var kvpArrayForPartCFields = PartCFields.Value; - if (kvpArrayForPartCFields == null) - { - kvpArrayForPartCFields = new KeyValuePair[120]; - PartCFields.Value = kvpArrayForPartCFields; - } + var kvpArrayForPartCFields = PartCFields.Value ??= new KeyValuePair[120]; - List> envPropertiesList = null; + List>? envPropertiesList = null; foreach (ref readonly var entry in activity.EnumerateTagObjects()) { + if (entry.Value == null) + { + continue; + } + // TODO: check name collision - if (CS40_PART_B_MAPPING.TryGetValue(entry.Key, out string replacementKey)) + if (CS40_PART_B_MAPPING.TryGetValue(entry.Key, out string? replacementKey)) { Serialize(eb, replacementKey, entry.Value); partBFieldsCount++; + continue; } - else if (string.Equals(entry.Key, "otel.status_code", StringComparison.Ordinal)) + + if (entry.Key.StartsWith("otel.status_", StringComparison.Ordinal)) { - if (string.Equals(Convert.ToString(entry.Value, CultureInfo.InvariantCulture), "ERROR", StringComparison.Ordinal)) + var keyPart = entry.Key.AsSpan().Slice(12); + if (keyPart is "code") { - isStatusSuccess = 0; + if (string.Equals(Convert.ToString(entry.Value, CultureInfo.InvariantCulture), "ERROR", StringComparison.Ordinal)) + { + isStatusSuccess = 0; + } + + continue; } - continue; - } - else if (string.Equals(entry.Key, "otel.status_description", StringComparison.Ordinal)) - { - statusDescription = Convert.ToString(entry.Value, CultureInfo.InvariantCulture); - continue; + if (keyPart is "description") + { + statusDescription = Convert.ToString(entry.Value, CultureInfo.InvariantCulture) ?? string.Empty; + continue; + } } - else if (this.customFields == null || this.customFields.Contains(entry.Key)) + + if (this.customFields == null || this.customFields.Contains(entry.Key)) { // TODO: the above null check can be optimized and avoided inside foreach. kvpArrayForPartCFields[partCFieldsCountFromTags] = new(entry.Key, entry.Value); partCFieldsCountFromTags++; + continue; } - else + + if (hasEnvProperties == 0) { - if (hasEnvProperties == 0) - { - hasEnvProperties = 1; - envPropertiesList = KeyValuePairs.Value; - if (envPropertiesList == null) - { - envPropertiesList = new List>(); - KeyValuePairs.Value = envPropertiesList; - } + hasEnvProperties = 1; - envPropertiesList.Clear(); - } + envPropertiesList = KeyValuePairs.Value ??= new(); - // TODO: This could lead to unbounded memory usage. - envPropertiesList.Add(new(entry.Key, entry.Value)); + envPropertiesList.Clear(); } + + // TODO: This could lead to unbounded memory usage. + envPropertiesList!.Add(new(entry.Key, entry.Value)); } if (activity.Status != ActivityStatusCode.Unset) @@ -313,17 +303,14 @@ internal void SerializeActivity(Activity activity) if (!string.IsNullOrEmpty(activity.StatusDescription)) { - eb.AddCountedAnsiString("statusMessage", statusDescription, Encoding.UTF8); + eb.AddCountedAnsiString("statusMessage", statusDescription!, Encoding.UTF8); partBFieldsCount++; } } - else + else if (!string.IsNullOrEmpty(activity.StatusDescription)) { - if (!string.IsNullOrEmpty(activity.StatusDescription)) - { - eb.AddCountedAnsiString("statusMessage", statusDescription, Encoding.UTF8); - partBFieldsCount++; - } + eb.AddCountedAnsiString("statusMessage", statusDescription!, Encoding.UTF8); + partBFieldsCount++; } // Do not increment partBFieldsCount here as the field "success" has already been accounted for @@ -351,5 +338,7 @@ internal void SerializeActivity(Activity activity) eb.AddCountedAnsiString("env_properties", serializedEnvPropertiesStringAsBytes, 0, count); } } + + return eb; } } diff --git a/src/OpenTelemetry.Exporter.Geneva/TLDExporter/UncheckedASCIIEncoding.cs b/src/OpenTelemetry.Exporter.Geneva/TLDExporter/UncheckedASCIIEncoding.cs index 5b8f52b7b3..a27c983040 100644 --- a/src/OpenTelemetry.Exporter.Geneva/TLDExporter/UncheckedASCIIEncoding.cs +++ b/src/OpenTelemetry.Exporter.Geneva/TLDExporter/UncheckedASCIIEncoding.cs @@ -1,6 +1,8 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#nullable enable + using System.Text; using OpenTelemetry.Internal;