From 18caf7db7cde26957ab20c3a4fac21748eda42ae Mon Sep 17 00:00:00 2001 From: Alan West <3676547+alanwest@users.noreply.github.com> Date: Thu, 16 Jun 2022 17:20:30 -0700 Subject: [PATCH 1/9] Support for limiting activity tags, events, and links --- .../Internal/ActivityHelperExtensions.cs | 102 ++++++++++++++---- .../EnvironmentVariableConfiguration.cs | 46 ++++++++ .../Configuration/SdkConfiguration.cs | 69 ++++++++++++ .../Implementation/ActivityExtensions.cs | 14 +-- src/OpenTelemetry/Internal/TagTransformer.cs | 10 +- .../OtlpTraceExporterTests.cs | 44 ++++++++ 6 files changed, 259 insertions(+), 26 deletions(-) create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/EnvironmentVariableConfiguration.cs create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/SdkConfiguration.cs diff --git a/src/OpenTelemetry.Api/Internal/ActivityHelperExtensions.cs b/src/OpenTelemetry.Api/Internal/ActivityHelperExtensions.cs index 6cc238b8c23..e3a2c312072 100644 --- a/src/OpenTelemetry.Api/Internal/ActivityHelperExtensions.cs +++ b/src/OpenTelemetry.Api/Internal/ActivityHelperExtensions.cs @@ -44,7 +44,7 @@ public static bool TryGetStatus(this Activity activity, out StatusCode statusCod ActivityStatusTagEnumerator state = default; - ActivityTagsEnumeratorFactory.Enumerate(activity, ref state); + ActivityTagsEnumeratorFactory.Enumerate(activity, ref state, null); if (!state.StatusCode.HasValue) { @@ -72,7 +72,7 @@ public static object GetTagValue(this Activity activity, string tagName) ActivitySingleTagEnumerator state = new ActivitySingleTagEnumerator(tagName); - ActivityTagsEnumeratorFactory.Enumerate(activity, ref state); + ActivityTagsEnumeratorFactory.Enumerate(activity, ref state, null); return state.Value; } @@ -91,7 +91,7 @@ public static bool TryCheckFirstTag(this Activity activity, string tagName, out ActivityFirstTagEnumerator state = new ActivityFirstTagEnumerator(tagName); - ActivityTagsEnumeratorFactory.Enumerate(activity, ref state); + ActivityTagsEnumeratorFactory.Enumerate(activity, ref state, null); if (state.Value == null) { @@ -109,14 +109,15 @@ public static bool TryCheckFirstTag(this Activity activity, string tagName, out /// The struct implementation to use for the enumeration. /// Activity instance. /// Tag enumerator. + /// Maximum number of tags to enumerate. [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] - public static void EnumerateTags(this Activity activity, ref T tagEnumerator) + public static void EnumerateTags(this Activity activity, ref T tagEnumerator, int? maxTags = null) where T : struct, IActivityEnumerator> { Debug.Assert(activity != null, "Activity should not be null"); - ActivityTagsEnumeratorFactory.Enumerate(activity, ref tagEnumerator); + ActivityTagsEnumeratorFactory.Enumerate(activity, ref tagEnumerator, maxTags); } /// @@ -125,14 +126,15 @@ public static void EnumerateTags(this Activity activity, ref T tagEnumerator) /// The struct implementation to use for the enumeration. /// Activity instance. /// Link enumerator. + /// Maximum number of links to enumerate. [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] - public static void EnumerateLinks(this Activity activity, ref T linkEnumerator) + public static void EnumerateLinks(this Activity activity, ref T linkEnumerator, int? maxLinks = null) where T : struct, IActivityEnumerator { Debug.Assert(activity != null, "Activity should not be null"); - ActivityLinksEnumeratorFactory.Enumerate(activity, ref linkEnumerator); + ActivityLinksEnumeratorFactory.Enumerate(activity, ref linkEnumerator, maxLinks); } /// @@ -141,12 +143,13 @@ public static void EnumerateLinks(this Activity activity, ref T linkEnumerato /// The struct implementation to use for the enumeration. /// ActivityLink instance. /// Tag enumerator. + /// Maximum number of tags to enumerate. [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] - public static void EnumerateTags(this ActivityLink activityLink, ref T tagEnumerator) + public static void EnumerateTags(this ActivityLink activityLink, ref T tagEnumerator, int? maxTags = null) where T : struct, IActivityEnumerator> { - ActivityTagsEnumeratorFactory.Enumerate(activityLink, ref tagEnumerator); + ActivityTagsEnumeratorFactory.Enumerate(activityLink, ref tagEnumerator, maxTags); } /// @@ -155,14 +158,15 @@ public static void EnumerateTags(this ActivityLink activityLink, ref T tagEnu /// The struct implementation to use for the enumeration. /// Activity instance. /// Event enumerator. + /// Maximum number of events to enumerate. [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] - public static void EnumerateEvents(this Activity activity, ref T eventEnumerator) + public static void EnumerateEvents(this Activity activity, ref T eventEnumerator, int? maxEvents = null) where T : struct, IActivityEnumerator { Debug.Assert(activity != null, "Activity should not be null"); - ActivityEventsEnumeratorFactory.Enumerate(activity, ref eventEnumerator); + ActivityEventsEnumeratorFactory.Enumerate(activity, ref eventEnumerator, maxEvents); } /// @@ -171,12 +175,13 @@ public static void EnumerateEvents(this Activity activity, ref T eventEnumera /// The struct implementation to use for the enumeration. /// ActivityEvent instance. /// Tag enumerator. + /// Maximum number of tags to enumerate. [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] - public static void EnumerateTags(this ActivityEvent activityEvent, ref T tagEnumerator) + public static void EnumerateTags(this ActivityEvent activityEvent, ref T tagEnumerator, int? maxTags = null) where T : struct, IActivityEnumerator> { - ActivityTagsEnumeratorFactory.Enumerate(activityEvent, ref tagEnumerator); + ActivityTagsEnumeratorFactory.Enumerate(activityEvent, ref tagEnumerator, maxTags); } private struct ActivitySingleTagEnumerator : IActivityEnumerator> @@ -265,7 +270,7 @@ private static readonly DictionaryEnumerator.AllocationF private static readonly DictionaryEnumerator.ForEachDelegate ForEachTagValueCallbackRef = ForEachTagValueCallback; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Enumerate(Activity activity, ref TState state) + public static void Enumerate(Activity activity, ref TState state, int? maxTags) { var tagObjects = activity.TagObjects; @@ -274,6 +279,12 @@ public static void Enumerate(Activity activity, ref TState state) return; } + if (maxTags.HasValue) + { + SkipAllocationFreeEnumeration(tagObjects, ref state, maxTags.Value); + return; + } + ActivityTagObjectsEnumerator( tagObjects, ref state, @@ -281,7 +292,7 @@ public static void Enumerate(Activity activity, ref TState state) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Enumerate(ActivityLink activityLink, ref TState state) + public static void Enumerate(ActivityLink activityLink, ref TState state, int? maxTags) { var tags = activityLink.Tags; @@ -290,6 +301,12 @@ public static void Enumerate(ActivityLink activityLink, ref TState state) return; } + if (maxTags.HasValue) + { + SkipAllocationFreeEnumeration(tags, ref state, maxTags.Value); + return; + } + ActivityTagsCollectionEnumerator( tags, ref state, @@ -297,7 +314,7 @@ public static void Enumerate(ActivityLink activityLink, ref TState state) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Enumerate(ActivityEvent activityEvent, ref TState state) + public static void Enumerate(ActivityEvent activityEvent, ref TState state, int? maxTags) { var tags = activityEvent.Tags; @@ -306,12 +323,31 @@ public static void Enumerate(ActivityEvent activityEvent, ref TState state) return; } + if (maxTags.HasValue) + { + SkipAllocationFreeEnumeration(tags, ref state, maxTags.Value); + return; + } + ActivityTagsCollectionEnumerator( tags, ref state, ForEachTagValueCallbackRef); } + // TODO: When a limit has been configured an allocation-free enumerator is not used. + // Need to either: + // 1) modify the dynamically generated code to only enumerate up to the max number of items, or + // 2) wait until .NET 7 is released and do this more easily with the new enumerator functions + private static void SkipAllocationFreeEnumeration(IEnumerable> tags, ref TState state, int maxTags) + { + var enumerator = tags.GetEnumerator(); + for (var i = 0; enumerator.MoveNext() && i < maxTags; ++i) + { + state.ForEach(enumerator.Current); + } + } + private static bool ForEachTagValueCallback(ref TState state, KeyValuePair item) => state.ForEach(item); } @@ -328,7 +364,7 @@ private static readonly ListEnumerator.AllocationFreeForEa private static readonly ListEnumerator.ForEachDelegate ForEachLinkCallbackRef = ForEachLinkCallback; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Enumerate(Activity activity, ref TState state) + public static void Enumerate(Activity activity, ref TState state, int? maxLinks) { var activityLinks = activity.Links; @@ -337,6 +373,21 @@ public static void Enumerate(Activity activity, ref TState state) return; } + // TODO: When a limit has been configured an allocation-free enumerator is not used. + // Need to either: + // 1) modify the dynamically generated code to only enumerate up to the max number of items, or + // 2) wait until .NET 7 is released and do this more easily with the new enumerator functions + if (maxLinks.HasValue) + { + var enumerator = activityLinks.GetEnumerator(); + for (var i = 0; enumerator.MoveNext() && i < maxLinks; ++i) + { + state.ForEach(enumerator.Current); + } + + return; + } + ActivityLinksEnumerator( activityLinks, ref state, @@ -359,7 +410,7 @@ private static readonly ListEnumerator.AllocationFreeForE private static readonly ListEnumerator.ForEachDelegate ForEachEventCallbackRef = ForEachEventCallback; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Enumerate(Activity activity, ref TState state) + public static void Enumerate(Activity activity, ref TState state, int? maxEvents) { var activityEvents = activity.Events; @@ -368,6 +419,21 @@ public static void Enumerate(Activity activity, ref TState state) return; } + // TODO: When a limit has been configured an allocation-free enumerator is not used. + // Need to either: + // 1) modify the dynamically generated code to only enumerate up to the max number of items, or + // 2) wait until .NET 7 is released and do this more easily with the new enumerator functions + if (maxEvents.HasValue) + { + var enumerator = activityEvents.GetEnumerator(); + for (var i = 0; enumerator.MoveNext() && i < maxEvents; ++i) + { + state.ForEach(enumerator.Current); + } + + return; + } + ActivityEventsEnumerator( activityEvents, ref state, diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/EnvironmentVariableConfiguration.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/EnvironmentVariableConfiguration.cs new file mode 100644 index 00000000000..dd6cbc7ee9e --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/EnvironmentVariableConfiguration.cs @@ -0,0 +1,46 @@ +// +// 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; +using OpenTelemetry.Internal; + +namespace OpenTelemetry.Configuration; + +internal class EnvironmentVariableConfiguration +{ + public static void InitializeDefaultConfigurationFromEnvironment(SdkConfiguration sdkConfiguration) + { + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#attribute-limits + SetIntConfigValue("OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT", value => sdkConfiguration.AttributeValueLengthLimit = value); + SetIntConfigValue("OTEL_ATTRIBUTE_COUNT_LIMIT", value => sdkConfiguration.AttributeCountLimit = value); + + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#span-limits + SetIntConfigValue("OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT", value => sdkConfiguration.SpanAttributeValueLengthLimit = value); + SetIntConfigValue("OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT", value => sdkConfiguration.SpanAttributeCountLimit = value); + SetIntConfigValue("OTEL_SPAN_EVENT_COUNT_LIMIT", value => sdkConfiguration.SpanEventCountLimit = value); + SetIntConfigValue("OTEL_SPAN_LINK_COUNT_LIMIT", value => sdkConfiguration.SpanLinkCountLimit = value); + SetIntConfigValue("OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT", value => sdkConfiguration.EventAttributeCountLimit = value); + SetIntConfigValue("OTEL_LINK_ATTRIBUTE_COUNT_LIMIT", value => sdkConfiguration.LinkAttributeCountLimit = value); + } + + private static void SetIntConfigValue(string key, Action setter) + { + if (EnvironmentVariableHelper.LoadNumeric(key, out var result)) + { + setter(result); + } + } +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/SdkConfiguration.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/SdkConfiguration.cs new file mode 100644 index 00000000000..d5470cf72d3 --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/SdkConfiguration.cs @@ -0,0 +1,69 @@ +// +// 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. +// + +namespace OpenTelemetry.Configuration; + +internal class SdkConfiguration +{ + private int? spanAttributeValueLengthLimit; + private int? spanAttributeCountLimit; + private int? eventAttributeCountLimit; + private int? linkAttributeCountLimit; + + static SdkConfiguration() + { + Instance = new SdkConfiguration(); + EnvironmentVariableConfiguration.InitializeDefaultConfigurationFromEnvironment(Instance); + } + + private SdkConfiguration() + { + } + + public static SdkConfiguration Instance { get; } + + public int? AttributeValueLengthLimit { get; set; } + + public int? AttributeCountLimit { get; set; } = 128; + + public int? SpanAttributeValueLengthLimit + { + get => this.spanAttributeValueLengthLimit ?? this.AttributeValueLengthLimit; + set => this.spanAttributeValueLengthLimit = value; + } + + public int? SpanAttributeCountLimit + { + get => this.spanAttributeCountLimit ?? this.AttributeCountLimit; + set => this.spanAttributeCountLimit = value; + } + + public int? SpanEventCountLimit { get; set; } = 128; + + public int? SpanLinkCountLimit { get; set; } = 128; + + public int? EventAttributeCountLimit + { + get => this.eventAttributeCountLimit ?? this.SpanAttributeCountLimit; + set => this.eventAttributeCountLimit = value; + } + + public int? LinkAttributeCountLimit + { + get => this.linkAttributeCountLimit ?? this.SpanAttributeCountLimit; + set => this.linkAttributeCountLimit = value; + } +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs index 6e62e19e805..f1e36021513 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs @@ -24,6 +24,7 @@ using System.Runtime.CompilerServices; using Google.Protobuf; using Google.Protobuf.Collections; +using OpenTelemetry.Configuration; using OpenTelemetry.Internal; using OpenTelemetry.Trace; using OtlpCollector = Opentelemetry.Proto.Collector.Trace.V1; @@ -154,7 +155,7 @@ internal static OtlpTrace.Span ToOtlpSpan(this Activity activity) }; TagEnumerationState otlpTags = default; - activity.EnumerateTags(ref otlpTags); + activity.EnumerateTags(ref otlpTags, SdkConfiguration.Instance.SpanAttributeCountLimit); if (activity.Kind == ActivityKind.Client || activity.Kind == ActivityKind.Producer) { @@ -181,7 +182,7 @@ internal static OtlpTrace.Span ToOtlpSpan(this Activity activity) otlpSpan.Status = activity.ToOtlpStatus(ref otlpTags); EventEnumerationState otlpEvents = default; - activity.EnumerateEvents(ref otlpEvents); + activity.EnumerateEvents(ref otlpEvents, SdkConfiguration.Instance.SpanEventCountLimit); if (otlpEvents.Created) { otlpSpan.Events.AddRange(otlpEvents.Events); @@ -189,13 +190,14 @@ internal static OtlpTrace.Span ToOtlpSpan(this Activity activity) } LinkEnumerationState otlpLinks = default; - activity.EnumerateLinks(ref otlpLinks); + activity.EnumerateLinks(ref otlpLinks, SdkConfiguration.Instance.SpanLinkCountLimit); if (otlpLinks.Created) { otlpSpan.Links.AddRange(otlpLinks.Links); otlpLinks.Links.Return(); } + // TODO: The drop counts should be set when necessary. // Activity does not limit number of attributes, events, links, etc so drop counts are always zero. return otlpSpan; @@ -259,7 +261,7 @@ private static OtlpTrace.Span.Types.Link ToOtlpLink(ActivityLink activityLink) }; TagEnumerationState otlpTags = default; - activityLink.EnumerateTags(ref otlpTags); + activityLink.EnumerateTags(ref otlpTags, SdkConfiguration.Instance.LinkAttributeCountLimit); if (otlpTags.Created) { otlpLink.Attributes.AddRange(otlpTags.Tags); @@ -279,7 +281,7 @@ private static OtlpTrace.Span.Types.Event ToOtlpEvent(ActivityEvent activityEven }; TagEnumerationState otlpTags = default; - activityEvent.EnumerateTags(ref otlpTags); + activityEvent.EnumerateTags(ref otlpTags, SdkConfiguration.Instance.EventAttributeCountLimit); if (otlpTags.Created) { otlpEvent.Attributes.AddRange(otlpTags.Tags); @@ -355,7 +357,7 @@ public bool ForEach(KeyValuePair activityTag) this.Created = true; } - if (OtlpKeyValueTransformer.Instance.TryTransformTag(activityTag, out var attribute)) + if (OtlpKeyValueTransformer.Instance.TryTransformTag(activityTag, out var attribute, SdkConfiguration.Instance.AttributeValueLengthLimit)) { PooledList.Add(ref this.Tags, attribute); diff --git a/src/OpenTelemetry/Internal/TagTransformer.cs b/src/OpenTelemetry/Internal/TagTransformer.cs index e63ccd623f9..b26266c6132 100644 --- a/src/OpenTelemetry/Internal/TagTransformer.cs +++ b/src/OpenTelemetry/Internal/TagTransformer.cs @@ -21,7 +21,7 @@ namespace OpenTelemetry.Internal; internal abstract class TagTransformer { - public bool TryTransformTag(KeyValuePair tag, out T result) + public bool TryTransformTag(KeyValuePair tag, out T result, int? maxLength = null) { if (tag.Value == null) { @@ -33,7 +33,13 @@ public bool TryTransformTag(KeyValuePair tag, out T result) { case char: case string: - result = this.TransformStringTag(tag.Key, Convert.ToString(tag.Value)); + var value = Convert.ToString(tag.Value); + if (maxLength.HasValue) + { + value = value?.Length > maxLength ? value.Substring(0, maxLength.Value) : value; + } + + result = this.TransformStringTag(tag.Key, value); break; case bool b: result = this.TransformBooleanTag(tag.Key, b); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index 3dd760c4190..a2c9607c965 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -216,6 +216,50 @@ void RunTest(Batch batch) } } + [Fact] + public void SpanLimitsTest() + { + Environment.SetEnvironmentVariable("OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT", "4"); + Environment.SetEnvironmentVariable("OTEL_ATTRIBUTE_COUNT_LIMIT", "1"); + Environment.SetEnvironmentVariable("OTEL_SPAN_EVENT_COUNT_LIMIT", "1"); + Environment.SetEnvironmentVariable("OTEL_SPAN_LINK_COUNT_LIMIT", "1"); + + var tags = new ActivityTagsCollection() + { + new KeyValuePair("TruncatedTag", "12345"), + new KeyValuePair("OneTagTooMany", 1), + }; + + var links = new[] + { + new ActivityLink(default, tags), + new ActivityLink(default, tags), + }; + + using var activitySource = new ActivitySource(nameof(this.SpanLimitsTest)); + using var activity = activitySource.StartActivity("root", ActivityKind.Server, default(ActivityContext), tags, links); + + var event1 = new ActivityEvent("Event", DateTime.UtcNow, tags); + var event2 = new ActivityEvent("OneEventTooMany", DateTime.Now, tags); + + activity.AddEvent(event1); + activity.AddEvent(event2); + + var otlpSpan = activity.ToOtlpSpan(); + + Assert.NotNull(otlpSpan); + Assert.Single(otlpSpan.Attributes); + Assert.Equal("1234", otlpSpan.Attributes.First().Value.StringValue); + + Assert.Single(otlpSpan.Events); + Assert.Single(otlpSpan.Events.First().Attributes); + Assert.Equal("1234", otlpSpan.Events.First().Attributes.First().Value.StringValue); + + Assert.Single(otlpSpan.Links); + Assert.Single(otlpSpan.Links.First().Attributes); + Assert.Equal("1234", otlpSpan.Links.First().Attributes.First().Value.StringValue); + } + [Fact] public void ToOtlpSpanTest() { From db2f3e57c19d4313d451bfc6f9a5f8269d6f0864 Mon Sep 17 00:00:00 2001 From: Alan West <3676547+alanwest@users.noreply.github.com> Date: Fri, 17 Jun 2022 15:32:00 -0700 Subject: [PATCH 2/9] Attribute limit configuration tests --- .../OtlpTraceExporterTests.cs | 9 +- .../SdkConfigurationTests.cs | 110 ++++++++++++++++++ 2 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/SdkConfigurationTests.cs diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index a2c9607c965..5387c02b53a 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -20,6 +20,7 @@ using System.Linq; using Microsoft.Extensions.DependencyInjection; using Moq; +using OpenTelemetry.Configuration; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; using OpenTelemetry.Resources; @@ -219,10 +220,10 @@ void RunTest(Batch batch) [Fact] public void SpanLimitsTest() { - Environment.SetEnvironmentVariable("OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT", "4"); - Environment.SetEnvironmentVariable("OTEL_ATTRIBUTE_COUNT_LIMIT", "1"); - Environment.SetEnvironmentVariable("OTEL_SPAN_EVENT_COUNT_LIMIT", "1"); - Environment.SetEnvironmentVariable("OTEL_SPAN_LINK_COUNT_LIMIT", "1"); + SdkConfiguration.Instance.AttributeValueLengthLimit = 4; + SdkConfiguration.Instance.AttributeCountLimit = 1; + SdkConfiguration.Instance.SpanEventCountLimit = 1; + SdkConfiguration.Instance.SpanLinkCountLimit = 1; var tags = new ActivityTagsCollection() { diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/SdkConfigurationTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/SdkConfigurationTests.cs new file mode 100644 index 00000000000..c4ef85293d3 --- /dev/null +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/SdkConfigurationTests.cs @@ -0,0 +1,110 @@ +// +// 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; +using OpenTelemetry.Configuration; +using Xunit; + +namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests +{ + public class SdkConfigurationTests + { + [Fact] + public void SdkConfigurationDefaults() + { + var config = SdkConfiguration.Instance; + + Assert.Null(config.AttributeValueLengthLimit); + Assert.Equal(128, config.AttributeCountLimit); + Assert.Null(config.SpanAttributeValueLengthLimit); + Assert.Equal(128, config.SpanAttributeCountLimit); + Assert.Equal(128, config.SpanEventCountLimit); + Assert.Equal(128, config.SpanLinkCountLimit); + Assert.Equal(128, config.EventAttributeCountLimit); + Assert.Equal(128, config.LinkAttributeCountLimit); + } + + [Fact] + public void SdkConfigurationIsInitializedFromEnvironment() + { + // TODO: This is probably problematic. Setting these here could + // may impact other tests that are running concurrently. + Environment.SetEnvironmentVariable("OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT", "10"); + Environment.SetEnvironmentVariable("OTEL_ATTRIBUTE_COUNT_LIMIT", "10"); + Environment.SetEnvironmentVariable("OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT", "20"); + Environment.SetEnvironmentVariable("OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT", "20"); + Environment.SetEnvironmentVariable("OTEL_SPAN_EVENT_COUNT_LIMIT", "10"); + Environment.SetEnvironmentVariable("OTEL_SPAN_LINK_COUNT_LIMIT", "10"); + Environment.SetEnvironmentVariable("OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT", "30"); + Environment.SetEnvironmentVariable("OTEL_LINK_ATTRIBUTE_COUNT_LIMIT", "30"); + + var config = SdkConfiguration.Instance; + + Assert.Equal(10, config.AttributeValueLengthLimit); + Assert.Equal(10, config.AttributeCountLimit); + Assert.Equal(20, config.SpanAttributeValueLengthLimit); + Assert.Equal(20, config.SpanAttributeCountLimit); + Assert.Equal(10, config.SpanEventCountLimit); + Assert.Equal(10, config.SpanLinkCountLimit); + Assert.Equal(30, config.EventAttributeCountLimit); + Assert.Equal(30, config.LinkAttributeCountLimit); + } + + [Fact] + public void SpanAttributeValueLengthLimitFallback() + { + var config = SdkConfiguration.Instance; + + config.AttributeValueLengthLimit = 10; + Assert.Equal(10, config.AttributeValueLengthLimit); + Assert.Equal(10, config.SpanAttributeValueLengthLimit); + + config.SpanAttributeValueLengthLimit = 20; + Assert.Equal(10, config.AttributeValueLengthLimit); + Assert.Equal(20, config.SpanAttributeValueLengthLimit); + } + + [Fact] + public void SpanAttributeCountLimitFallback() + { + var config = SdkConfiguration.Instance; + + config.AttributeCountLimit = 10; + Assert.Equal(10, config.AttributeCountLimit); + Assert.Equal(10, config.SpanAttributeCountLimit); + Assert.Equal(10, config.EventAttributeCountLimit); + Assert.Equal(10, config.LinkAttributeCountLimit); + + config.SpanAttributeCountLimit = 20; + Assert.Equal(10, config.AttributeCountLimit); + Assert.Equal(20, config.SpanAttributeCountLimit); + Assert.Equal(20, config.EventAttributeCountLimit); + Assert.Equal(20, config.LinkAttributeCountLimit); + + config.EventAttributeCountLimit = 30; + Assert.Equal(10, config.AttributeCountLimit); + Assert.Equal(20, config.SpanAttributeCountLimit); + Assert.Equal(30, config.EventAttributeCountLimit); + Assert.Equal(20, config.LinkAttributeCountLimit); + + config.LinkAttributeCountLimit = 40; + Assert.Equal(10, config.AttributeCountLimit); + Assert.Equal(20, config.SpanAttributeCountLimit); + Assert.Equal(30, config.EventAttributeCountLimit); + Assert.Equal(40, config.LinkAttributeCountLimit); + } + } +} From 3b0634144a56c58c501240b0e1d729a5702e78cb Mon Sep 17 00:00:00 2001 From: Alan West <3676547+alanwest@users.noreply.github.com> Date: Fri, 17 Jun 2022 16:06:03 -0700 Subject: [PATCH 3/9] Fix namespace and file header --- .../{ => Configuration}/SdkConfigurationTests.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) rename test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/{ => Configuration}/SdkConfigurationTests.cs (96%) diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/SdkConfigurationTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Configuration/SdkConfigurationTests.cs similarity index 96% rename from test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/SdkConfigurationTests.cs rename to test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Configuration/SdkConfigurationTests.cs index c4ef85293d3..045959f3486 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/SdkConfigurationTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Configuration/SdkConfigurationTests.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,10 +15,9 @@ // using System; -using OpenTelemetry.Configuration; using Xunit; -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests +namespace OpenTelemetry.Configuration.Tests { public class SdkConfigurationTests { From cb26e70a22358a1048ccfae62f8c1bc39aa3cee1 Mon Sep 17 00:00:00 2001 From: Alan West <3676547+alanwest@users.noreply.github.com> Date: Fri, 17 Jun 2022 21:48:35 -0700 Subject: [PATCH 4/9] Truncate strings in array-valued attributes --- src/OpenTelemetry/Internal/TagTransformer.cs | 40 ++++++++++++------- .../OtlpTraceExporterTests.cs | 38 ++++++++++++++---- 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/src/OpenTelemetry/Internal/TagTransformer.cs b/src/OpenTelemetry/Internal/TagTransformer.cs index b26266c6132..b45651fbf5c 100644 --- a/src/OpenTelemetry/Internal/TagTransformer.cs +++ b/src/OpenTelemetry/Internal/TagTransformer.cs @@ -16,6 +16,7 @@ using System; using System.Collections.Generic; +using System.Linq; namespace OpenTelemetry.Internal; @@ -33,13 +34,7 @@ public bool TryTransformTag(KeyValuePair tag, out T result, int? { case char: case string: - var value = Convert.ToString(tag.Value); - if (maxLength.HasValue) - { - value = value?.Length > maxLength ? value.Substring(0, maxLength.Value) : value; - } - - result = this.TransformStringTag(tag.Key, value); + result = this.TransformStringTag(tag.Key, TruncateString(Convert.ToString(tag.Value), maxLength)); break; case bool b: result = this.TransformBooleanTag(tag.Key, b); @@ -60,7 +55,7 @@ public bool TryTransformTag(KeyValuePair tag, out T result, int? case Array array: try { - result = this.TransformArrayTagInternal(tag.Key, array); + result = this.TransformArrayTagInternal(tag.Key, array, maxLength); } catch { @@ -109,13 +104,20 @@ public bool TryTransformTag(KeyValuePair tag, out T result, int? protected abstract T TransformArrayTag(string key, Array array); - private T TransformArrayTagInternal(string key, Array array) + private static string TruncateString(string value, int? maxLength) + { + return maxLength.HasValue && value?.Length > maxLength + ? value.Substring(0, maxLength.Value) + : value; + } + + private T TransformArrayTagInternal(string key, Array array, int? maxStringValueLength) { // This switch ensures the values of the resultant array-valued tag are of the same type. return array switch { char[] => this.TransformArrayTag(key, array), - string[] => this.TransformArrayTag(key, array), + string[] => this.ConvertToStringArrayThenTransformArrayTag(key, array, maxStringValueLength), bool[] => this.TransformArrayTag(key, array), byte[] => this.TransformArrayTag(key, array), sbyte[] => this.TransformArrayTag(key, array), @@ -126,17 +128,25 @@ private T TransformArrayTagInternal(string key, Array array) long[] => this.TransformArrayTag(key, array), float[] => this.TransformArrayTag(key, array), double[] => this.TransformArrayTag(key, array), - _ => this.ConvertToStringArrayThenTransformArrayTag(key, array), + _ => this.ConvertToStringArrayThenTransformArrayTag(key, array, maxStringValueLength), }; } - private T ConvertToStringArrayThenTransformArrayTag(string key, Array array) + private T ConvertToStringArrayThenTransformArrayTag(string key, Array array, int? maxStringValueLength) { - var stringArray = new string[array.Length]; + string[] stringArray; - for (var i = 0; i < array.Length; ++i) + if (array is string[] arrayAsStringArray && !arrayAsStringArray.Any(s => s?.Length > maxStringValueLength)) + { + stringArray = arrayAsStringArray; + } + else { - stringArray[i] = array.GetValue(i)?.ToString(); + stringArray = new string[array.Length]; + for (var i = 0; i < array.Length; ++i) + { + stringArray[i] = TruncateString(array.GetValue(i)?.ToString(), maxStringValueLength); + } } return this.TransformArrayTag(key, stringArray); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index 5387c02b53a..fa0a130e64b 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -18,6 +18,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using Google.Protobuf.Collections; using Microsoft.Extensions.DependencyInjection; using Moq; using OpenTelemetry.Configuration; @@ -221,13 +222,14 @@ void RunTest(Batch batch) public void SpanLimitsTest() { SdkConfiguration.Instance.AttributeValueLengthLimit = 4; - SdkConfiguration.Instance.AttributeCountLimit = 1; + SdkConfiguration.Instance.AttributeCountLimit = 2; SdkConfiguration.Instance.SpanEventCountLimit = 1; SdkConfiguration.Instance.SpanLinkCountLimit = 1; var tags = new ActivityTagsCollection() { new KeyValuePair("TruncatedTag", "12345"), + new KeyValuePair("TruncatedStringArray", new string[] { "12345", "1234", string.Empty, null }), new KeyValuePair("OneTagTooMany", 1), }; @@ -249,16 +251,38 @@ public void SpanLimitsTest() var otlpSpan = activity.ToOtlpSpan(); Assert.NotNull(otlpSpan); - Assert.Single(otlpSpan.Attributes); - Assert.Equal("1234", otlpSpan.Attributes.First().Value.StringValue); + Assert.Equal(2, otlpSpan.Attributes.Count); + Assert.Equal("1234", otlpSpan.Attributes[0].Value.StringValue); + ArrayValueAsserts(otlpSpan.Attributes[1].Value.ArrayValue.Values); Assert.Single(otlpSpan.Events); - Assert.Single(otlpSpan.Events.First().Attributes); - Assert.Equal("1234", otlpSpan.Events.First().Attributes.First().Value.StringValue); + Assert.Equal(2, otlpSpan.Events[0].Attributes.Count); + Assert.Equal("1234", otlpSpan.Events[0].Attributes[0].Value.StringValue); + ArrayValueAsserts(otlpSpan.Events[0].Attributes[1].Value.ArrayValue.Values); Assert.Single(otlpSpan.Links); - Assert.Single(otlpSpan.Links.First().Attributes); - Assert.Equal("1234", otlpSpan.Links.First().Attributes.First().Value.StringValue); + Assert.Equal(2, otlpSpan.Links[0].Attributes.Count); + Assert.Equal("1234", otlpSpan.Links[0].Attributes[0].Value.StringValue); + ArrayValueAsserts(otlpSpan.Links[0].Attributes[1].Value.ArrayValue.Values); + + void ArrayValueAsserts(RepeatedField values) + { + var expectedStringArray = new string[] { "1234", "1234", string.Empty, null }; + for (var i = 0; i < expectedStringArray.Length; ++i) + { + var expectedValue = expectedStringArray[i]; + var expectedValueCase = expectedValue != null + ? OtlpCommon.AnyValue.ValueOneofCase.StringValue + : OtlpCommon.AnyValue.ValueOneofCase.None; + + var actual = values[i]; + Assert.Equal(expectedValueCase, actual.ValueCase); + if (expectedValueCase != OtlpCommon.AnyValue.ValueOneofCase.None) + { + Assert.Equal(expectedValue, actual.StringValue); + } + } + } } [Fact] From 7d2e9861dbae3d4344d890d95da76bca8da1aaa2 Mon Sep 17 00:00:00 2001 From: Alan West <3676547+alanwest@users.noreply.github.com> Date: Wed, 22 Jun 2022 17:36:04 -0700 Subject: [PATCH 5/9] Prevent configuration tests from running in parallel --- .../Configuration/SdkConfiguration.cs | 14 ++++----- .../Configuration/SdkConfigurationTests.cs | 30 +++++++++++++++++-- .../OtlpTraceExporterTests.cs | 3 ++ 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/SdkConfiguration.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/SdkConfiguration.cs index d5470cf72d3..2e3bf3cbd05 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/SdkConfiguration.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/SdkConfiguration.cs @@ -23,17 +23,12 @@ internal class SdkConfiguration private int? eventAttributeCountLimit; private int? linkAttributeCountLimit; - static SdkConfiguration() - { - Instance = new SdkConfiguration(); - EnvironmentVariableConfiguration.InitializeDefaultConfigurationFromEnvironment(Instance); - } - private SdkConfiguration() { + EnvironmentVariableConfiguration.InitializeDefaultConfigurationFromEnvironment(this); } - public static SdkConfiguration Instance { get; } + public static SdkConfiguration Instance { get; private set; } = new SdkConfiguration(); public int? AttributeValueLengthLimit { get; set; } @@ -66,4 +61,9 @@ public int? LinkAttributeCountLimit get => this.linkAttributeCountLimit ?? this.SpanAttributeCountLimit; set => this.linkAttributeCountLimit = value; } + + internal static void Reset() + { + Instance = new SdkConfiguration(); + } } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Configuration/SdkConfigurationTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Configuration/SdkConfigurationTests.cs index 045959f3486..eb026fe361a 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Configuration/SdkConfigurationTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Configuration/SdkConfigurationTests.cs @@ -19,8 +19,21 @@ namespace OpenTelemetry.Configuration.Tests { - public class SdkConfigurationTests + [Collection("xUnitCollectionPreventingTestsThatDependOnSdkConfigurationFromRunningInParallel")] + public class SdkConfigurationTests : IDisposable { + public SdkConfigurationTests() + { + ClearEnvVars(); + SdkConfiguration.Reset(); + } + + public void Dispose() + { + ClearEnvVars(); + SdkConfiguration.Reset(); + } + [Fact] public void SdkConfigurationDefaults() { @@ -39,8 +52,6 @@ public void SdkConfigurationDefaults() [Fact] public void SdkConfigurationIsInitializedFromEnvironment() { - // TODO: This is probably problematic. Setting these here could - // may impact other tests that are running concurrently. Environment.SetEnvironmentVariable("OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT", "10"); Environment.SetEnvironmentVariable("OTEL_ATTRIBUTE_COUNT_LIMIT", "10"); Environment.SetEnvironmentVariable("OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT", "20"); @@ -50,6 +61,7 @@ public void SdkConfigurationIsInitializedFromEnvironment() Environment.SetEnvironmentVariable("OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT", "30"); Environment.SetEnvironmentVariable("OTEL_LINK_ATTRIBUTE_COUNT_LIMIT", "30"); + SdkConfiguration.Reset(); var config = SdkConfiguration.Instance; Assert.Equal(10, config.AttributeValueLengthLimit); @@ -105,5 +117,17 @@ public void SpanAttributeCountLimitFallback() Assert.Equal(30, config.EventAttributeCountLimit); Assert.Equal(40, config.LinkAttributeCountLimit); } + + private static void ClearEnvVars() + { + Environment.SetEnvironmentVariable("OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT", null); + Environment.SetEnvironmentVariable("OTEL_ATTRIBUTE_COUNT_LIMIT", null); + Environment.SetEnvironmentVariable("OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT", null); + Environment.SetEnvironmentVariable("OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT", null); + Environment.SetEnvironmentVariable("OTEL_SPAN_EVENT_COUNT_LIMIT", null); + Environment.SetEnvironmentVariable("OTEL_SPAN_LINK_COUNT_LIMIT", null); + Environment.SetEnvironmentVariable("OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT", null); + Environment.SetEnvironmentVariable("OTEL_LINK_ATTRIBUTE_COUNT_LIMIT", null); + } } } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index fa0a130e64b..e6cdb422fa5 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -35,6 +35,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests { + [Collection("xUnitCollectionPreventingTestsThatDependOnSdkConfigurationFromRunningInParallel")] public class OtlpTraceExporterTests : Http2UnencryptedSupportTests { static OtlpTraceExporterTests() @@ -283,6 +284,8 @@ void ArrayValueAsserts(RepeatedField values) } } } + + SdkConfiguration.Reset(); } [Fact] From 72594c4dc2b5a0059420dca5c91c5584ed4b9721 Mon Sep 17 00:00:00 2001 From: Alan West <3676547+alanwest@users.noreply.github.com> Date: Thu, 23 Jun 2022 15:24:18 -0700 Subject: [PATCH 6/9] Don't linq statement if you don't have to --- src/OpenTelemetry/Internal/TagTransformer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry/Internal/TagTransformer.cs b/src/OpenTelemetry/Internal/TagTransformer.cs index b45651fbf5c..156b2dff373 100644 --- a/src/OpenTelemetry/Internal/TagTransformer.cs +++ b/src/OpenTelemetry/Internal/TagTransformer.cs @@ -136,7 +136,7 @@ private T ConvertToStringArrayThenTransformArrayTag(string key, Array array, int { string[] stringArray; - if (array is string[] arrayAsStringArray && !arrayAsStringArray.Any(s => s?.Length > maxStringValueLength)) + if (array is string[] arrayAsStringArray && (!maxStringValueLength.HasValue || !arrayAsStringArray.Any(s => s?.Length > maxStringValueLength))) { stringArray = arrayAsStringArray; } From f9ee6a9db6f74a5a9255eb59720528b58e098f69 Mon Sep 17 00:00:00 2001 From: Alan West <3676547+alanwest@users.noreply.github.com> Date: Mon, 18 Jul 2022 13:41:46 -0700 Subject: [PATCH 7/9] Update changelog --- .../CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md index d90417bb3de..4769929c382 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +* Adds support for limiting the length and count of attributes exported from + the OTLP exporter. These + [Attribute Limits](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#attribute-limits) + are configured via the environment variables defined in the specification. + ([#3376](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3376)) + ## 1.3.0 Released 2022-Jun-03 From 1dd75f043a122dafed19531238c5d7af232de27f Mon Sep 17 00:00:00 2001 From: Alan West <3676547+alanwest@users.noreply.github.com> Date: Wed, 20 Jul 2022 16:32:44 -0700 Subject: [PATCH 8/9] Default to null for all attribute limit settings --- .../Configuration/SdkConfiguration.cs | 6 +++--- .../Configuration/SdkConfigurationTests.cs | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/SdkConfiguration.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/SdkConfiguration.cs index 2e3bf3cbd05..a7772bcd532 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/SdkConfiguration.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/SdkConfiguration.cs @@ -32,7 +32,7 @@ private SdkConfiguration() public int? AttributeValueLengthLimit { get; set; } - public int? AttributeCountLimit { get; set; } = 128; + public int? AttributeCountLimit { get; set; } public int? SpanAttributeValueLengthLimit { @@ -46,9 +46,9 @@ public int? SpanAttributeCountLimit set => this.spanAttributeCountLimit = value; } - public int? SpanEventCountLimit { get; set; } = 128; + public int? SpanEventCountLimit { get; set; } - public int? SpanLinkCountLimit { get; set; } = 128; + public int? SpanLinkCountLimit { get; set; } public int? EventAttributeCountLimit { diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Configuration/SdkConfigurationTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Configuration/SdkConfigurationTests.cs index eb026fe361a..dbbc9e43688 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Configuration/SdkConfigurationTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Configuration/SdkConfigurationTests.cs @@ -40,13 +40,13 @@ public void SdkConfigurationDefaults() var config = SdkConfiguration.Instance; Assert.Null(config.AttributeValueLengthLimit); - Assert.Equal(128, config.AttributeCountLimit); + Assert.Null(config.AttributeCountLimit); Assert.Null(config.SpanAttributeValueLengthLimit); - Assert.Equal(128, config.SpanAttributeCountLimit); - Assert.Equal(128, config.SpanEventCountLimit); - Assert.Equal(128, config.SpanLinkCountLimit); - Assert.Equal(128, config.EventAttributeCountLimit); - Assert.Equal(128, config.LinkAttributeCountLimit); + Assert.Null(config.SpanAttributeCountLimit); + Assert.Null(config.SpanEventCountLimit); + Assert.Null(config.SpanLinkCountLimit); + Assert.Null(config.EventAttributeCountLimit); + Assert.Null(config.LinkAttributeCountLimit); } [Fact] From eb8056b9dc0fae800eca1449ea51c656f5623395 Mon Sep 17 00:00:00 2001 From: Alan West <3676547+alanwest@users.noreply.github.com> Date: Fri, 22 Jul 2022 13:43:20 -0700 Subject: [PATCH 9/9] Truncate ToStringed complex types --- src/OpenTelemetry/Internal/TagTransformer.cs | 2 +- .../OtlpTraceExporterTests.cs | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/OpenTelemetry/Internal/TagTransformer.cs b/src/OpenTelemetry/Internal/TagTransformer.cs index 156b2dff373..f2a0cee9cc6 100644 --- a/src/OpenTelemetry/Internal/TagTransformer.cs +++ b/src/OpenTelemetry/Internal/TagTransformer.cs @@ -78,7 +78,7 @@ public bool TryTransformTag(KeyValuePair tag, out T result, int? default: try { - result = this.TransformStringTag(tag.Key, tag.Value.ToString()); + result = this.TransformStringTag(tag.Key, TruncateString(tag.Value.ToString(), maxLength)); } catch { diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index 326ae70b042..a3a21e45c3f 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -223,7 +223,7 @@ void RunTest(Batch batch) public void SpanLimitsTest() { SdkConfiguration.Instance.AttributeValueLengthLimit = 4; - SdkConfiguration.Instance.AttributeCountLimit = 2; + SdkConfiguration.Instance.AttributeCountLimit = 3; SdkConfiguration.Instance.SpanEventCountLimit = 1; SdkConfiguration.Instance.SpanLinkCountLimit = 1; @@ -231,6 +231,7 @@ public void SpanLimitsTest() { new KeyValuePair("TruncatedTag", "12345"), new KeyValuePair("TruncatedStringArray", new string[] { "12345", "1234", string.Empty, null }), + new KeyValuePair("TruncatedObjectTag", new object()), new KeyValuePair("OneTagTooMany", 1), }; @@ -252,19 +253,22 @@ public void SpanLimitsTest() var otlpSpan = activity.ToOtlpSpan(); Assert.NotNull(otlpSpan); - Assert.Equal(2, otlpSpan.Attributes.Count); + Assert.Equal(3, otlpSpan.Attributes.Count); Assert.Equal("1234", otlpSpan.Attributes[0].Value.StringValue); ArrayValueAsserts(otlpSpan.Attributes[1].Value.ArrayValue.Values); + Assert.Equal(new object().ToString().Substring(0, 4), otlpSpan.Attributes[2].Value.StringValue); Assert.Single(otlpSpan.Events); - Assert.Equal(2, otlpSpan.Events[0].Attributes.Count); + Assert.Equal(3, otlpSpan.Events[0].Attributes.Count); Assert.Equal("1234", otlpSpan.Events[0].Attributes[0].Value.StringValue); ArrayValueAsserts(otlpSpan.Events[0].Attributes[1].Value.ArrayValue.Values); + Assert.Equal(new object().ToString().Substring(0, 4), otlpSpan.Events[0].Attributes[2].Value.StringValue); Assert.Single(otlpSpan.Links); - Assert.Equal(2, otlpSpan.Links[0].Attributes.Count); + Assert.Equal(3, otlpSpan.Links[0].Attributes.Count); Assert.Equal("1234", otlpSpan.Links[0].Attributes[0].Value.StringValue); ArrayValueAsserts(otlpSpan.Links[0].Attributes[1].Value.ArrayValue.Values); + Assert.Equal(new object().ToString().Substring(0, 4), otlpSpan.Links[0].Attributes[2].Value.StringValue); void ArrayValueAsserts(RepeatedField values) {