diff --git a/src/OpenTelemetry.Exporter.OneCollector/CHANGELOG.md b/src/OpenTelemetry.Exporter.OneCollector/CHANGELOG.md index 8b7b8b531b..cab0a366f9 100644 --- a/src/OpenTelemetry.Exporter.OneCollector/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OneCollector/CHANGELOG.md @@ -22,6 +22,13 @@ extension because it is not currently supported by the OneCollector service. ([#1345](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1345)) +* Added dedicated handling for `IReadOnlyList>` + types during serialization to improve performance. + ([#1361](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1361)) + +* Added caching of extension property UTF8 JSON strings to improve performance. + ([#1361](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1361)) + ## 1.5.1 Released 2023-Aug-07 diff --git a/src/OpenTelemetry.Exporter.OneCollector/Internal/ExtensionFieldInformation.cs b/src/OpenTelemetry.Exporter.OneCollector/Internal/ExtensionFieldInformation.cs new file mode 100644 index 0000000000..596c2943d9 --- /dev/null +++ b/src/OpenTelemetry.Exporter.OneCollector/Internal/ExtensionFieldInformation.cs @@ -0,0 +1,29 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Text.Json; + +namespace OpenTelemetry.Exporter.OneCollector; + +internal sealed class ExtensionFieldInformation +{ + public string? ExtensionName; + public JsonEncodedText EncodedExtensionName; + public string? FieldName; + public JsonEncodedText EncodedFieldName; + + public bool IsValid => this.ExtensionName != null; +} diff --git a/src/OpenTelemetry.Exporter.OneCollector/Internal/ExtensionFieldInformationManager.cs b/src/OpenTelemetry.Exporter.OneCollector/Internal/ExtensionFieldInformationManager.cs index f2a92b62d1..98bd8dd745 100644 --- a/src/OpenTelemetry.Exporter.OneCollector/Internal/ExtensionFieldInformationManager.cs +++ b/src/OpenTelemetry.Exporter.OneCollector/Internal/ExtensionFieldInformationManager.cs @@ -16,6 +16,10 @@ using System.Collections; using System.Diagnostics; +#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif +using System.Text.Json; namespace OpenTelemetry.Exporter.OneCollector; @@ -28,9 +32,14 @@ internal sealed class ExtensionFieldInformationManager public int CountOfCachedExtensionFields => this.fieldInformationCache.Count; - public bool TryResolveExtensionFieldInformation(string fullFieldName, out (string ExtensionName, string FieldName) resolvedFieldInformation) + public bool TryResolveExtensionFieldInformation( + string fullFieldName, +#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER + [NotNullWhen(true)] +#endif + out ExtensionFieldInformation? resolvedFieldInformation) { - if (this.fieldInformationCache[fullFieldName] is not FieldInformation fieldInformation) + if (this.fieldInformationCache[fullFieldName] is not ExtensionFieldInformation fieldInformation) { fieldInformation = this.ResolveExtensionFieldInformationRare(fullFieldName); } @@ -41,39 +50,44 @@ public bool TryResolveExtensionFieldInformation(string fullFieldName, out (strin return false; } - resolvedFieldInformation = new(fieldInformation.ExtensionName!, fieldInformation.FieldName!); + resolvedFieldInformation = fieldInformation; return true; } - private static FieldInformation BuildFieldInformation(string fullFieldName) + private static ExtensionFieldInformation BuildFieldInformation(string fullFieldName) { Debug.Assert(fullFieldName.Length >= 4, "fullFieldName length was invalid"); Debug.Assert(fullFieldName.StartsWith("ext.", StringComparison.OrdinalIgnoreCase), "fullFieldName did not start with 'ext.'"); - var extensionName = fullFieldName.AsSpan().Slice(4); + var extensionName = fullFieldName.AsSpan().Slice(4).TrimEnd(); var locationOfDot = extensionName.IndexOf('.'); if (locationOfDot <= 0) { return new(); } - var fieldName = extensionName.Slice(locationOfDot + 1); + var fieldName = extensionName.Slice(locationOfDot + 1).TrimStart(); if (fieldName.Length <= 0) { return new(); } - extensionName = extensionName.Slice(0, locationOfDot); + extensionName = extensionName.Slice(0, locationOfDot).TrimEnd(); + if (extensionName.Length <= 0) + { + return new(); + } - return new FieldInformation + return new ExtensionFieldInformation { ExtensionName = extensionName.ToString(), + EncodedExtensionName = JsonEncodedText.Encode(extensionName), FieldName = fieldName.ToString(), - IsValid = true, + EncodedFieldName = JsonEncodedText.Encode(fieldName), }; } - private FieldInformation ResolveExtensionFieldInformationRare(string fullFieldName) + private ExtensionFieldInformation ResolveExtensionFieldInformationRare(string fullFieldName) { if (this.fieldInformationCache.Count >= MaxNumberOfCachedFieldInformations) { @@ -82,7 +96,7 @@ private FieldInformation ResolveExtensionFieldInformationRare(string fullFieldNa lock (this.fieldInformationCache) { - if (this.fieldInformationCache[fullFieldName] is not FieldInformation fieldInformation) + if (this.fieldInformationCache[fullFieldName] is not ExtensionFieldInformation fieldInformation) { fieldInformation = BuildFieldInformation(fullFieldName); if (this.fieldInformationCache.Count < MaxNumberOfCachedFieldInformations) @@ -94,11 +108,4 @@ private FieldInformation ResolveExtensionFieldInformationRare(string fullFieldNa return fieldInformation; } } - - private sealed class FieldInformation - { - public string? ExtensionName; - public string? FieldName; - public bool IsValid; - } } diff --git a/src/OpenTelemetry.Exporter.OneCollector/Internal/Serialization/CommonSchemaJsonSerializationHelper.cs b/src/OpenTelemetry.Exporter.OneCollector/Internal/Serialization/CommonSchemaJsonSerializationHelper.cs index dd687a15b3..fa015b281c 100644 --- a/src/OpenTelemetry.Exporter.OneCollector/Internal/Serialization/CommonSchemaJsonSerializationHelper.cs +++ b/src/OpenTelemetry.Exporter.OneCollector/Internal/Serialization/CommonSchemaJsonSerializationHelper.cs @@ -158,6 +158,10 @@ public static void SerializeValueToJson(object? value, Utf8JsonWriter writer) SerializeArrayValueToJson(v, writer); return; + case IReadOnlyList> v: + SerializeMapValueToJson(v, writer); + return; + case IEnumerable> v: SerializeMapValueToJson(v, writer); return; @@ -180,6 +184,25 @@ private static void SerializeArrayValueToJson(Array value, Utf8JsonWriter writer writer.WriteEndArray(); } + private static void SerializeMapValueToJson(IReadOnlyList> value, Utf8JsonWriter writer) + { + writer.WriteStartObject(); + + for (int i = 0; i < value.Count; i++) + { + var element = value[i]; + + if (string.IsNullOrEmpty(element.Key)) + { + continue; + } + + SerializeKeyValueToJson(element.Key, element.Value, writer); + } + + writer.WriteEndObject(); + } + private static void SerializeMapValueToJson(IEnumerable> value, Utf8JsonWriter writer) { writer.WriteStartObject(); diff --git a/src/OpenTelemetry.Exporter.OneCollector/Internal/Serialization/CommonSchemaJsonSerializationState.cs b/src/OpenTelemetry.Exporter.OneCollector/Internal/Serialization/CommonSchemaJsonSerializationState.cs index 52b992aaa1..cc464979fe 100644 --- a/src/OpenTelemetry.Exporter.OneCollector/Internal/Serialization/CommonSchemaJsonSerializationState.cs +++ b/src/OpenTelemetry.Exporter.OneCollector/Internal/Serialization/CommonSchemaJsonSerializationState.cs @@ -14,6 +14,7 @@ // limitations under the License. // +using System.Diagnostics; #if NET6_0_OR_GREATER using System.Runtime.InteropServices; #endif @@ -26,7 +27,7 @@ internal sealed class CommonSchemaJsonSerializationState public const int MaxNumberOfExtensionKeys = 64; public const int MaxNumberOfExtensionValuesPerKey = 16; private readonly Dictionary keys = new(4, StringComparer.OrdinalIgnoreCase); - private readonly List> allValues = new(16); + private readonly List> allValues = new(16); private string itemType; private int nextKeysToAllValuesLookupIndex; private KeyValueLookup[] keysToAllValuesLookup = new KeyValueLookup[4]; @@ -47,12 +48,17 @@ public void AddExtensionAttribute(KeyValuePair attribute) { if (!ExtensionFieldInformationManager.SharedCache.TryResolveExtensionFieldInformation( attribute.Key, - out (string ExtensionName, string FieldName) fieldInformation)) + out var fieldInformation)) { OneCollectorExporterEventSource.Log.AttributeDropped(this.itemType, attribute.Key, "Invalid extension field name"); return; } + Debug.Assert(fieldInformation?.ExtensionName != null, "fieldInformation.ExtensionName was null"); + Debug.Assert(fieldInformation?.EncodedExtensionName.EncodedUtf8Bytes.Length > 0, "fieldInformation.EncodedExtensionName was empty"); + Debug.Assert(fieldInformation?.FieldName != null, "fieldInformation.FieldName was null"); + Debug.Assert(fieldInformation?.EncodedFieldName.EncodedUtf8Bytes.Length > 0, "fieldInformation.EncodedFieldName was empty"); + #if NET6_0_OR_GREATER ref var lookupIndex = ref CollectionsMarshal.GetValueRefOrAddDefault(this.keys, fieldInformation.ExtensionName, out var existed); if (!existed) @@ -60,10 +66,10 @@ public void AddExtensionAttribute(KeyValuePair attribute) this.AssignNewExtensionToLookupIndex(ref lookupIndex); } #else - if (!this.keys.TryGetValue(fieldInformation.ExtensionName, out int lookupIndex)) + if (!this.keys.TryGetValue(fieldInformation!.ExtensionName!, out int lookupIndex)) { this.AssignNewExtensionToLookupIndex(ref lookupIndex); - this.keys[fieldInformation.ExtensionName] = lookupIndex; + this.keys[fieldInformation.ExtensionName!] = lookupIndex; } #endif @@ -82,7 +88,7 @@ public void AddExtensionAttribute(KeyValuePair attribute) } int index = this.allValues.Count; - this.allValues.Add(new KeyValuePair(fieldInformation.FieldName, attribute.Value)); + this.allValues.Add(new KeyValuePair(fieldInformation, attribute.Value)); unsafe { @@ -107,7 +113,7 @@ public void SerializeExtensionPropertiesToJson(bool writeExtensionObjectEnvelope foreach (var extensionPropertyKey in this.keys) { - writer.WriteStartObject(extensionPropertyKey.Key); + var wroteStartObject = false; ref KeyValueLookup keyLookup = ref this.keysToAllValuesLookup[extensionPropertyKey.Value]; @@ -120,12 +126,23 @@ public void SerializeExtensionPropertiesToJson(bool writeExtensionObjectEnvelope #else var attribute = allValues[keyLookup.ValueIndicies[i]]; #endif + var fieldInformation = attribute.Key; + + if (!wroteStartObject) + { + writer.WriteStartObject(fieldInformation.EncodedExtensionName); + wroteStartObject = true; + } - CommonSchemaJsonSerializationHelper.SerializeKeyValueToJson(attribute.Key, attribute.Value, writer); + writer.WritePropertyName(fieldInformation.EncodedFieldName); + CommonSchemaJsonSerializationHelper.SerializeValueToJson(attribute.Value, writer); } } - writer.WriteEndObject(); + if (wroteStartObject) + { + writer.WriteEndObject(); + } } if (writeExtensionObjectEnvelope) diff --git a/test/OpenTelemetry.Exporter.OneCollector.Tests/CommonSchemaJsonSerializationHelperTests.cs b/test/OpenTelemetry.Exporter.OneCollector.Tests/CommonSchemaJsonSerializationHelperTests.cs index cb7673710b..ab3adfc7c6 100644 --- a/test/OpenTelemetry.Exporter.OneCollector.Tests/CommonSchemaJsonSerializationHelperTests.cs +++ b/test/OpenTelemetry.Exporter.OneCollector.Tests/CommonSchemaJsonSerializationHelperTests.cs @@ -82,8 +82,11 @@ public void SerializeComplexValueToJsonTest() var array = new[] { 0, 1, 18 }; this.SerializeValueToJsonTest(array, "[0,1,18]"); - var map = new List> { new KeyValuePair("key1", "value1") }; - this.SerializeValueToJsonTest(map, "{\"key1\":\"value1\"}"); + var listMap = new List> { new KeyValuePair("key1", "value1") }; + this.SerializeValueToJsonTest(listMap, "{\"key1\":\"value1\"}"); + + var dictMap = new Dictionary { ["key1"] = "value1" }; + this.SerializeValueToJsonTest(dictMap, "{\"key1\":\"value1\"}"); var typeWithToString = new TypeWithToString(); this.SerializeValueToJsonTest(typeWithToString, "\"Hello world\""); diff --git a/test/OpenTelemetry.Exporter.OneCollector.Tests/CommonSchemaJsonSerializationStateTests.cs b/test/OpenTelemetry.Exporter.OneCollector.Tests/CommonSchemaJsonSerializationStateTests.cs index 0363ca3996..37a50d3f6d 100644 --- a/test/OpenTelemetry.Exporter.OneCollector.Tests/CommonSchemaJsonSerializationStateTests.cs +++ b/test/OpenTelemetry.Exporter.OneCollector.Tests/CommonSchemaJsonSerializationStateTests.cs @@ -50,7 +50,7 @@ public void AddExtensionAttributeTest() var json = Encoding.UTF8.GetString(stream.ToArray()); Assert.Equal( - "{\"ext\":{\"something\":{\"field1\":1,\"field2\":2,\"field3\":3,\"field4\":6},\"food\":{\"field1\":4,\"field2\":5}}}", + """{"ext":{"something":{"field1":1,"field2":2,"field3":3,"field4":6},"food":{"field1":4,"field2":5}}}""", json); stream.SetLength(0); @@ -74,7 +74,7 @@ public void AddExtensionAttributeTest() json = Encoding.UTF8.GetString(stream.ToArray()); Assert.Equal( - "{\"ext\":{\"something\":{\"field1\":1},\"food\":{\"field1\":1}}}", + """{"ext":{"something":{"field1":1},"food":{"field1":1}}}""", json); } @@ -102,7 +102,7 @@ public void AddExtensionAttributeDuplicatesTest() var json = Encoding.UTF8.GetString(stream.ToArray()); Assert.Equal( - "{\"ext\":{\"something\":{\"field1\":1,\"field1\":2}}}", + """{"ext":{"something":{"field1":1,"field1":2}}}""", json); } diff --git a/test/OpenTelemetry.Exporter.OneCollector.Tests/ExtensionFieldInformationManagerTests.cs b/test/OpenTelemetry.Exporter.OneCollector.Tests/ExtensionFieldInformationManagerTests.cs index e2b0de5890..fa07ae22c8 100644 --- a/test/OpenTelemetry.Exporter.OneCollector.Tests/ExtensionFieldInformationManagerTests.cs +++ b/test/OpenTelemetry.Exporter.OneCollector.Tests/ExtensionFieldInformationManagerTests.cs @@ -28,6 +28,7 @@ public void FieldInformationIsCachedTest() var result = extensionFieldInformationManager.TryResolveExtensionFieldInformation("ext.something.fieldName1", out var fieldInformation); Assert.True(result); + Assert.NotNull(fieldInformation); Assert.Equal("something", fieldInformation.ExtensionName); Assert.Equal("fieldName1", fieldInformation.FieldName); @@ -38,6 +39,7 @@ public void FieldInformationIsCachedTest() Assert.Equal(1, extensionFieldInformationManager.CountOfCachedExtensionFields); Assert.True(result); + Assert.NotNull(fieldInformation); Assert.Equal("something", fieldInformation.ExtensionName); Assert.Equal("fieldName1", fieldInformation.FieldName); @@ -46,6 +48,7 @@ public void FieldInformationIsCachedTest() Assert.Equal(2, extensionFieldInformationManager.CountOfCachedExtensionFields); Assert.True(result); + Assert.NotNull(fieldInformation); Assert.Equal("something", fieldInformation.ExtensionName); Assert.Equal("field.Name2", fieldInformation.FieldName); } @@ -54,19 +57,23 @@ public void FieldInformationIsCachedTest() public void InvalidFieldNamesIgnoredTest() { var extensionFieldInformationManager = new ExtensionFieldInformationManager(); - Assert.False(extensionFieldInformationManager.TryResolveExtensionFieldInformation("ext.", out _)); Assert.Equal(1, extensionFieldInformationManager.CountOfCachedExtensionFields); - Assert.False(extensionFieldInformationManager.TryResolveExtensionFieldInformation("ext.", out _)); + Assert.False(extensionFieldInformationManager.TryResolveExtensionFieldInformation("EXT.", out _)); Assert.Equal(1, extensionFieldInformationManager.CountOfCachedExtensionFields); + Assert.False(extensionFieldInformationManager.TryResolveExtensionFieldInformation("ext..", out _)); + Assert.False(extensionFieldInformationManager.TryResolveExtensionFieldInformation("ext. .", out _)); + Assert.False(extensionFieldInformationManager.TryResolveExtensionFieldInformation("ext..field", out _)); Assert.False(extensionFieldInformationManager.TryResolveExtensionFieldInformation("ext.something", out _)); Assert.False(extensionFieldInformationManager.TryResolveExtensionFieldInformation("ext.something.", out _)); + Assert.False(extensionFieldInformationManager.TryResolveExtensionFieldInformation("ext.SOMETHING.", out _)); + Assert.False(extensionFieldInformationManager.TryResolveExtensionFieldInformation("ext.something. ", out _)); - Assert.Equal(3, extensionFieldInformationManager.CountOfCachedExtensionFields); + Assert.Equal(7, extensionFieldInformationManager.CountOfCachedExtensionFields); } [Fact] @@ -81,6 +88,7 @@ public void FieldInformationCacheLimitTest() var result = extensionFieldInformationManager.TryResolveExtensionFieldInformation($"ext.something.{fieldName}", out var fieldInformation); Assert.True(result); + Assert.NotNull(fieldInformation); Assert.Equal("something", fieldInformation.ExtensionName); Assert.Equal(fieldName, fieldInformation.FieldName); } diff --git a/test/OpenTelemetry.Exporter.OneCollector.Tests/LogRecordCommonSchemaJsonSerializerTests.cs b/test/OpenTelemetry.Exporter.OneCollector.Tests/LogRecordCommonSchemaJsonSerializerTests.cs index 22386f8a10..5642b73b19 100644 --- a/test/OpenTelemetry.Exporter.OneCollector.Tests/LogRecordCommonSchemaJsonSerializerTests.cs +++ b/test/OpenTelemetry.Exporter.OneCollector.Tests/LogRecordCommonSchemaJsonSerializerTests.cs @@ -32,7 +32,7 @@ public void EmptyLogRecordJsonTest() string json = GetLogRecordJson(1, (index, logRecord) => { }); Assert.Equal( - "{\"ver\":\"4.0\",\"name\":\"Namespace.Name\",\"time\":\"2032-01-18T10:11:12Z\",\"iKey\":\"o:tenant-token\",\"data\":{\"severityText\":\"Trace\",\"severityNumber\":1}}\n", + """{"ver":"4.0","name":"Namespace.Name","time":"2032-01-18T10:11:12Z","iKey":"o:tenant-token","data":{"severityText":"Trace","severityNumber":1}}""" + "\n", json); } @@ -42,8 +42,8 @@ public void MultipleEmptyLogRecordJsonTest() string json = GetLogRecordJson(2, (index, logRecord) => { }); Assert.Equal( - "{\"ver\":\"4.0\",\"name\":\"Namespace.Name\",\"time\":\"2032-01-18T10:11:12Z\",\"iKey\":\"o:tenant-token\",\"data\":{\"severityText\":\"Trace\",\"severityNumber\":1}}\n" - + "{\"ver\":\"4.0\",\"name\":\"Namespace.Name\",\"time\":\"2032-01-18T10:11:12Z\",\"iKey\":\"o:tenant-token\",\"data\":{\"severityText\":\"Trace\",\"severityNumber\":1}}\n", + """{"ver":"4.0","name":"Namespace.Name","time":"2032-01-18T10:11:12Z","iKey":"o:tenant-token","data":{"severityText":"Trace","severityNumber":1}}""" + "\n" + + """{"ver":"4.0","name":"Namespace.Name","time":"2032-01-18T10:11:12Z","iKey":"o:tenant-token","data":{"severityText":"Trace","severityNumber":1}}""" + "\n", json); } @@ -63,7 +63,7 @@ public void LogRecordLogLevelJsonTest(LogLevel logLevel, string severityText, in }); Assert.Equal( - $"{{\"ver\":\"4.0\",\"name\":\"Namespace.Name\",\"time\":\"2032-01-18T10:11:12Z\",\"iKey\":\"o:tenant-token\",\"data\":{{\"severityText\":\"{severityText}\",\"severityNumber\":{severityNumber}}}}}\n", + $$$"""{"ver":"4.0","name":"Namespace.Name","time":"2032-01-18T10:11:12Z","iKey":"o:tenant-token","data":{"severityText":"{{{severityText}}}","severityNumber":{{{severityNumber}}}}}""" + "\n", json); } @@ -79,7 +79,7 @@ public void LogRecordCategoryNameAndEventNameJsonTest(string categoryName, strin }); Assert.Equal( - $"{{\"ver\":\"4.0\",\"name\":\"{categoryName}.{eventName ?? "Name"}\",\"time\":\"2032-01-18T10:11:12Z\",\"iKey\":\"o:tenant-token\",\"data\":{{\"severityText\":\"Trace\",\"severityNumber\":1}}}}\n", + $$$"""{"ver":"4.0","name":"{{{categoryName}}}.{{{eventName ?? "Name"}}}","time":"2032-01-18T10:11:12Z","iKey":"o:tenant-token","data":{"severityText":"Trace","severityNumber":1}}""" + "\n", json); } @@ -92,7 +92,7 @@ public void LogRecordEventIdJsonTest() }); Assert.Equal( - "{\"ver\":\"4.0\",\"name\":\"Namespace.Name\",\"time\":\"2032-01-18T10:11:12Z\",\"iKey\":\"o:tenant-token\",\"data\":{\"eventId\":18,\"severityText\":\"Trace\",\"severityNumber\":1}}\n", + """{"ver":"4.0","name":"Namespace.Name","time":"2032-01-18T10:11:12Z","iKey":"o:tenant-token","data":{"eventId":18,"severityText":"Trace","severityNumber":1}}""" + "\n", json); } @@ -105,7 +105,7 @@ public void LogRecordTimestampJsonTest() }); Assert.Equal( - "{\"ver\":\"4.0\",\"name\":\"Namespace.Name\",\"time\":\"2023-01-18T10:18:00Z\",\"iKey\":\"o:tenant-token\",\"data\":{\"severityText\":\"Trace\",\"severityNumber\":1}}\n", + """{"ver":"4.0","name":"Namespace.Name","time":"2023-01-18T10:18:00Z","iKey":"o:tenant-token","data":{"severityText":"Trace","severityNumber":1}}""" + "\n", json); } @@ -119,7 +119,7 @@ public void LogRecordOriginalFormatBodyJsonTest() }); Assert.Equal( - $"{{\"ver\":\"4.0\",\"name\":\"Namespace.Name\",\"time\":\"2032-01-18T10:11:12Z\",\"iKey\":\"o:tenant-token\",\"data\":{{\"severityText\":\"Trace\",\"severityNumber\":1,\"body\":\"hello world\",\"formattedMessage\":\"goodbye world\"}}}}\n", + """{"ver":"4.0","name":"Namespace.Name","time":"2032-01-18T10:11:12Z","iKey":"o:tenant-token","data":{"severityText":"Trace","severityNumber":1,"body":"hello world","formattedMessage":"goodbye world"}}""" + "\n", json); } @@ -133,7 +133,7 @@ public void LogRecordBodyJsonTest() }); Assert.Equal( - $"{{\"ver\":\"4.0\",\"name\":\"Namespace.Name\",\"time\":\"2032-01-18T10:11:12Z\",\"iKey\":\"o:tenant-token\",\"data\":{{\"severityText\":\"Trace\",\"severityNumber\":1,\"body\":\"hello world\",\"formattedMessage\":\"goodbye world\"}}}}\n", + """{"ver":"4.0","name":"Namespace.Name","time":"2032-01-18T10:11:12Z","iKey":"o:tenant-token","data":{"severityText":"Trace","severityNumber":1,"body":"hello world","formattedMessage":"goodbye world"}}""" + "\n", json); } @@ -146,7 +146,7 @@ public void LogRecordFormattedMessageBodyJsonTest() }); Assert.Equal( - $"{{\"ver\":\"4.0\",\"name\":\"Namespace.Name\",\"time\":\"2032-01-18T10:11:12Z\",\"iKey\":\"o:tenant-token\",\"data\":{{\"severityText\":\"Trace\",\"severityNumber\":1,\"body\":\"goodbye world\",\"formattedMessage\":\"goodbye world\"}}}}\n", + """{"ver":"4.0","name":"Namespace.Name","time":"2032-01-18T10:11:12Z","iKey":"o:tenant-token","data":{"severityText":"Trace","severityNumber":1,"body":"goodbye world","formattedMessage":"goodbye world"}}""" + "\n", json); } @@ -164,7 +164,7 @@ public void LogRecordResourceJsonTest() string json = GetLogRecordJson(1, (index, logRecord) => { }, resource); Assert.Equal( - $"{{\"ver\":\"4.0\",\"name\":\"Namespace.Name\",\"time\":\"2032-01-18T10:11:12Z\",\"iKey\":\"o:tenant-token\",\"data\":{{\"severityText\":\"Trace\",\"severityNumber\":1,\"resourceKey1\":\"resourceValue1\",\"resourceKey2\":\"resourceValue2\"}}}}\n", + """{"ver":"4.0","name":"Namespace.Name","time":"2032-01-18T10:11:12Z","iKey":"o:tenant-token","data":{"severityText":"Trace","severityNumber":1,"resourceKey1":"resourceValue1","resourceKey2":"resourceValue2"}}""" + "\n", json); } @@ -178,7 +178,7 @@ public void LogRecordScopesJsonTest() string json = GetLogRecordJson(1, (index, logRecord) => { }, scopeProvider: scopeProvider); Assert.Equal( - $"{{\"ver\":\"4.0\",\"name\":\"Namespace.Name\",\"time\":\"2032-01-18T10:11:12Z\",\"iKey\":\"o:tenant-token\",\"data\":{{\"severityText\":\"Trace\",\"severityNumber\":1,\"scope1Key1\":\"scope1Value1\",\"scope1Key2\":\"scope1Value2\",\"scope2Key1\":\"scope2Value1\"}}}}\n", + """{"ver":"4.0","name":"Namespace.Name","time":"2032-01-18T10:11:12Z","iKey":"o:tenant-token","data":{"severityText":"Trace","severityNumber":1,"scope1Key1":"scope1Value1","scope1Key2":"scope1Value2","scope2Key1":"scope2Value1"}}""" + "\n", json); } @@ -191,7 +191,7 @@ public void LogRecordStateValuesJsonTest() }); Assert.Equal( - $"{{\"ver\":\"4.0\",\"name\":\"Namespace.Name\",\"time\":\"2032-01-18T10:11:12Z\",\"iKey\":\"o:tenant-token\",\"data\":{{\"severityText\":\"Trace\",\"severityNumber\":1,\"stateKey1\":\"stateValue1\",\"stateKey2\":\"stateValue2\"}}}}\n", + """{"ver":"4.0","name":"Namespace.Name","time":"2032-01-18T10:11:12Z","iKey":"o:tenant-token","data":{"severityText":"Trace","severityNumber":1,"stateKey1":"stateValue1","stateKey2":"stateValue2"}}""" + "\n", json); } @@ -209,7 +209,9 @@ public void LogRecordTraceContextJsonTest() }); Assert.Equal( - $"{{\"ver\":\"4.0\",\"name\":\"Namespace.Name\",\"time\":\"2032-01-18T10:11:12Z\",\"iKey\":\"o:tenant-token\",\"data\":{{\"severityText\":\"Trace\",\"severityNumber\":1}},\"ext\":{{\"dt\":{{\"traceId\":\"{traceId}\",\"spanId\":\"{spanId}\"}}}}}}\n", + $$$$""" + {"ver":"4.0","name":"Namespace.Name","time":"2032-01-18T10:11:12Z","iKey":"o:tenant-token","data":{"severityText":"Trace","severityNumber":1},"ext":{"dt":{"traceId":"{{{{traceId}}}}","spanId":"{{{{spanId}}}}"}}} + """ + "\n", json); } @@ -227,11 +229,13 @@ public void LogRecordExceptionJsonTest(bool includeStackTraceAsString) includeStackTraceAsString: includeStackTraceAsString); var stackJson = includeStackTraceAsString - ? $",\"stack\":\"System.InvalidOperationException: Operation is not valid due to the current state of the object.\"" + ? ",\"stack\":\"System.InvalidOperationException: Operation is not valid due to the current state of the object.\"" : string.Empty; Assert.Equal( - $"{{\"ver\":\"4.0\",\"name\":\"Namespace.Name\",\"time\":\"2032-01-18T10:11:12Z\",\"iKey\":\"o:tenant-token\",\"data\":{{\"severityText\":\"Trace\",\"severityNumber\":1}},\"ext\":{{\"ex\":{{\"type\":\"System.InvalidOperationException\",\"msg\":\"Operation is not valid due to the current state of the object.\"{stackJson}}}}}}}\n", + $$$$""" + {"ver":"4.0","name":"Namespace.Name","time":"2032-01-18T10:11:12Z","iKey":"o:tenant-token","data":{"severityText":"Trace","severityNumber":1},"ext":{"ex":{"type":"System.InvalidOperationException","msg":"Operation is not valid due to the current state of the object."{{{{stackJson}}}}}}} + """ + "\n", json); } @@ -258,7 +262,7 @@ public void LogRecordExtensionsJsonTest() scopeProvider); Assert.Equal( - "{\"ver\":\"4.0\",\"name\":\"Namespace.Name\",\"time\":\"2032-01-18T10:11:12Z\",\"iKey\":\"o:tenant-token\",\"data\":{\"severityText\":\"Trace\",\"severityNumber\":1},\"ext\":{\"state\":{\"field\":\"stateValue1\"},\"resource\":{\"field\":\"resourceValue1\"},\"scope\":{\"field\":\"scopeValue1\"}}}\n", + """{"ver":"4.0","name":"Namespace.Name","time":"2032-01-18T10:11:12Z","iKey":"o:tenant-token","data":{"severityText":"Trace","severityNumber":1},"ext":{"state":{"field":"stateValue1"},"resource":{"field":"resourceValue1"},"scope":{"field":"scopeValue1"}}}""" + "\n", json); }