Skip to content

Commit 4f5449f

Browse files
authored
Merge branch 'main' into yunl/RefactorPrometheusExporter2
2 parents 6dfc90c + 2b1e75d commit 4f5449f

File tree

28 files changed

+1409
-142
lines changed

28 files changed

+1409
-142
lines changed

src/OpenTelemetry.Api/Internal/ActivityHelperExtensions.cs

Lines changed: 84 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public static bool TryGetStatus(this Activity activity, out StatusCode statusCod
4444

4545
ActivityStatusTagEnumerator state = default;
4646

47-
ActivityTagsEnumeratorFactory<ActivityStatusTagEnumerator>.Enumerate(activity, ref state);
47+
ActivityTagsEnumeratorFactory<ActivityStatusTagEnumerator>.Enumerate(activity, ref state, null);
4848

4949
if (!state.StatusCode.HasValue)
5050
{
@@ -72,7 +72,7 @@ public static object GetTagValue(this Activity activity, string tagName)
7272

7373
ActivitySingleTagEnumerator state = new ActivitySingleTagEnumerator(tagName);
7474

75-
ActivityTagsEnumeratorFactory<ActivitySingleTagEnumerator>.Enumerate(activity, ref state);
75+
ActivityTagsEnumeratorFactory<ActivitySingleTagEnumerator>.Enumerate(activity, ref state, null);
7676

7777
return state.Value;
7878
}
@@ -91,7 +91,7 @@ public static bool TryCheckFirstTag(this Activity activity, string tagName, out
9191

9292
ActivityFirstTagEnumerator state = new ActivityFirstTagEnumerator(tagName);
9393

94-
ActivityTagsEnumeratorFactory<ActivityFirstTagEnumerator>.Enumerate(activity, ref state);
94+
ActivityTagsEnumeratorFactory<ActivityFirstTagEnumerator>.Enumerate(activity, ref state, null);
9595

9696
if (state.Value == null)
9797
{
@@ -109,14 +109,15 @@ public static bool TryCheckFirstTag(this Activity activity, string tagName, out
109109
/// <typeparam name="T">The struct <see cref="IActivityEnumerator{T}"/> implementation to use for the enumeration.</typeparam>
110110
/// <param name="activity">Activity instance.</param>
111111
/// <param name="tagEnumerator">Tag enumerator.</param>
112+
/// <param name="maxTags">Maximum number of tags to enumerate.</param>
112113
[MethodImpl(MethodImplOptions.AggressiveInlining)]
113114
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")]
114-
public static void EnumerateTags<T>(this Activity activity, ref T tagEnumerator)
115+
public static void EnumerateTags<T>(this Activity activity, ref T tagEnumerator, int? maxTags = null)
115116
where T : struct, IActivityEnumerator<KeyValuePair<string, object>>
116117
{
117118
Debug.Assert(activity != null, "Activity should not be null");
118119

119-
ActivityTagsEnumeratorFactory<T>.Enumerate(activity, ref tagEnumerator);
120+
ActivityTagsEnumeratorFactory<T>.Enumerate(activity, ref tagEnumerator, maxTags);
120121
}
121122

122123
/// <summary>
@@ -125,14 +126,15 @@ public static void EnumerateTags<T>(this Activity activity, ref T tagEnumerator)
125126
/// <typeparam name="T">The struct <see cref="IActivityEnumerator{T}"/> implementation to use for the enumeration.</typeparam>
126127
/// <param name="activity">Activity instance.</param>
127128
/// <param name="linkEnumerator">Link enumerator.</param>
129+
/// <param name="maxLinks">Maximum number of links to enumerate.</param>
128130
[MethodImpl(MethodImplOptions.AggressiveInlining)]
129131
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")]
130-
public static void EnumerateLinks<T>(this Activity activity, ref T linkEnumerator)
132+
public static void EnumerateLinks<T>(this Activity activity, ref T linkEnumerator, int? maxLinks = null)
131133
where T : struct, IActivityEnumerator<ActivityLink>
132134
{
133135
Debug.Assert(activity != null, "Activity should not be null");
134136

135-
ActivityLinksEnumeratorFactory<T>.Enumerate(activity, ref linkEnumerator);
137+
ActivityLinksEnumeratorFactory<T>.Enumerate(activity, ref linkEnumerator, maxLinks);
136138
}
137139

138140
/// <summary>
@@ -141,12 +143,13 @@ public static void EnumerateLinks<T>(this Activity activity, ref T linkEnumerato
141143
/// <typeparam name="T">The struct <see cref="IActivityEnumerator{T}"/> implementation to use for the enumeration.</typeparam>
142144
/// <param name="activityLink">ActivityLink instance.</param>
143145
/// <param name="tagEnumerator">Tag enumerator.</param>
146+
/// <param name="maxTags">Maximum number of tags to enumerate.</param>
144147
[MethodImpl(MethodImplOptions.AggressiveInlining)]
145148
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")]
146-
public static void EnumerateTags<T>(this ActivityLink activityLink, ref T tagEnumerator)
149+
public static void EnumerateTags<T>(this ActivityLink activityLink, ref T tagEnumerator, int? maxTags = null)
147150
where T : struct, IActivityEnumerator<KeyValuePair<string, object>>
148151
{
149-
ActivityTagsEnumeratorFactory<T>.Enumerate(activityLink, ref tagEnumerator);
152+
ActivityTagsEnumeratorFactory<T>.Enumerate(activityLink, ref tagEnumerator, maxTags);
150153
}
151154

152155
/// <summary>
@@ -155,14 +158,15 @@ public static void EnumerateTags<T>(this ActivityLink activityLink, ref T tagEnu
155158
/// <typeparam name="T">The struct <see cref="IActivityEnumerator{T}"/> implementation to use for the enumeration.</typeparam>
156159
/// <param name="activity">Activity instance.</param>
157160
/// <param name="eventEnumerator">Event enumerator.</param>
161+
/// <param name="maxEvents">Maximum number of events to enumerate.</param>
158162
[MethodImpl(MethodImplOptions.AggressiveInlining)]
159163
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")]
160-
public static void EnumerateEvents<T>(this Activity activity, ref T eventEnumerator)
164+
public static void EnumerateEvents<T>(this Activity activity, ref T eventEnumerator, int? maxEvents = null)
161165
where T : struct, IActivityEnumerator<ActivityEvent>
162166
{
163167
Debug.Assert(activity != null, "Activity should not be null");
164168

165-
ActivityEventsEnumeratorFactory<T>.Enumerate(activity, ref eventEnumerator);
169+
ActivityEventsEnumeratorFactory<T>.Enumerate(activity, ref eventEnumerator, maxEvents);
166170
}
167171

168172
/// <summary>
@@ -171,12 +175,13 @@ public static void EnumerateEvents<T>(this Activity activity, ref T eventEnumera
171175
/// <typeparam name="T">The struct <see cref="IActivityEnumerator{T}"/> implementation to use for the enumeration.</typeparam>
172176
/// <param name="activityEvent">ActivityEvent instance.</param>
173177
/// <param name="tagEnumerator">Tag enumerator.</param>
178+
/// <param name="maxTags">Maximum number of tags to enumerate.</param>
174179
[MethodImpl(MethodImplOptions.AggressiveInlining)]
175180
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")]
176-
public static void EnumerateTags<T>(this ActivityEvent activityEvent, ref T tagEnumerator)
181+
public static void EnumerateTags<T>(this ActivityEvent activityEvent, ref T tagEnumerator, int? maxTags = null)
177182
where T : struct, IActivityEnumerator<KeyValuePair<string, object>>
178183
{
179-
ActivityTagsEnumeratorFactory<T>.Enumerate(activityEvent, ref tagEnumerator);
184+
ActivityTagsEnumeratorFactory<T>.Enumerate(activityEvent, ref tagEnumerator, maxTags);
180185
}
181186

182187
private struct ActivitySingleTagEnumerator : IActivityEnumerator<KeyValuePair<string, object>>
@@ -265,7 +270,7 @@ private static readonly DictionaryEnumerator<string, object, TState>.AllocationF
265270
private static readonly DictionaryEnumerator<string, object, TState>.ForEachDelegate ForEachTagValueCallbackRef = ForEachTagValueCallback;
266271

267272
[MethodImpl(MethodImplOptions.AggressiveInlining)]
268-
public static void Enumerate(Activity activity, ref TState state)
273+
public static void Enumerate(Activity activity, ref TState state, int? maxTags)
269274
{
270275
var tagObjects = activity.TagObjects;
271276

@@ -274,14 +279,20 @@ public static void Enumerate(Activity activity, ref TState state)
274279
return;
275280
}
276281

282+
if (maxTags.HasValue)
283+
{
284+
SkipAllocationFreeEnumeration(tagObjects, ref state, maxTags.Value);
285+
return;
286+
}
287+
277288
ActivityTagObjectsEnumerator(
278289
tagObjects,
279290
ref state,
280291
ForEachTagValueCallbackRef);
281292
}
282293

283294
[MethodImpl(MethodImplOptions.AggressiveInlining)]
284-
public static void Enumerate(ActivityLink activityLink, ref TState state)
295+
public static void Enumerate(ActivityLink activityLink, ref TState state, int? maxTags)
285296
{
286297
var tags = activityLink.Tags;
287298

@@ -290,14 +301,20 @@ public static void Enumerate(ActivityLink activityLink, ref TState state)
290301
return;
291302
}
292303

304+
if (maxTags.HasValue)
305+
{
306+
SkipAllocationFreeEnumeration(tags, ref state, maxTags.Value);
307+
return;
308+
}
309+
293310
ActivityTagsCollectionEnumerator(
294311
tags,
295312
ref state,
296313
ForEachTagValueCallbackRef);
297314
}
298315

299316
[MethodImpl(MethodImplOptions.AggressiveInlining)]
300-
public static void Enumerate(ActivityEvent activityEvent, ref TState state)
317+
public static void Enumerate(ActivityEvent activityEvent, ref TState state, int? maxTags)
301318
{
302319
var tags = activityEvent.Tags;
303320

@@ -306,12 +323,31 @@ public static void Enumerate(ActivityEvent activityEvent, ref TState state)
306323
return;
307324
}
308325

326+
if (maxTags.HasValue)
327+
{
328+
SkipAllocationFreeEnumeration(tags, ref state, maxTags.Value);
329+
return;
330+
}
331+
309332
ActivityTagsCollectionEnumerator(
310333
tags,
311334
ref state,
312335
ForEachTagValueCallbackRef);
313336
}
314337

338+
// TODO: When a limit has been configured an allocation-free enumerator is not used.
339+
// Need to either:
340+
// 1) modify the dynamically generated code to only enumerate up to the max number of items, or
341+
// 2) wait until .NET 7 is released and do this more easily with the new enumerator functions
342+
private static void SkipAllocationFreeEnumeration(IEnumerable<KeyValuePair<string, object>> tags, ref TState state, int maxTags)
343+
{
344+
var enumerator = tags.GetEnumerator();
345+
for (var i = 0; enumerator.MoveNext() && i < maxTags; ++i)
346+
{
347+
state.ForEach(enumerator.Current);
348+
}
349+
}
350+
315351
private static bool ForEachTagValueCallback(ref TState state, KeyValuePair<string, object> item)
316352
=> state.ForEach(item);
317353
}
@@ -328,7 +364,7 @@ private static readonly ListEnumerator<ActivityLink, TState>.AllocationFreeForEa
328364
private static readonly ListEnumerator<ActivityLink, TState>.ForEachDelegate ForEachLinkCallbackRef = ForEachLinkCallback;
329365

330366
[MethodImpl(MethodImplOptions.AggressiveInlining)]
331-
public static void Enumerate(Activity activity, ref TState state)
367+
public static void Enumerate(Activity activity, ref TState state, int? maxLinks)
332368
{
333369
var activityLinks = activity.Links;
334370

@@ -337,6 +373,21 @@ public static void Enumerate(Activity activity, ref TState state)
337373
return;
338374
}
339375

376+
// TODO: When a limit has been configured an allocation-free enumerator is not used.
377+
// Need to either:
378+
// 1) modify the dynamically generated code to only enumerate up to the max number of items, or
379+
// 2) wait until .NET 7 is released and do this more easily with the new enumerator functions
380+
if (maxLinks.HasValue)
381+
{
382+
var enumerator = activityLinks.GetEnumerator();
383+
for (var i = 0; enumerator.MoveNext() && i < maxLinks; ++i)
384+
{
385+
state.ForEach(enumerator.Current);
386+
}
387+
388+
return;
389+
}
390+
340391
ActivityLinksEnumerator(
341392
activityLinks,
342393
ref state,
@@ -359,7 +410,7 @@ private static readonly ListEnumerator<ActivityEvent, TState>.AllocationFreeForE
359410
private static readonly ListEnumerator<ActivityEvent, TState>.ForEachDelegate ForEachEventCallbackRef = ForEachEventCallback;
360411

361412
[MethodImpl(MethodImplOptions.AggressiveInlining)]
362-
public static void Enumerate(Activity activity, ref TState state)
413+
public static void Enumerate(Activity activity, ref TState state, int? maxEvents)
363414
{
364415
var activityEvents = activity.Events;
365416

@@ -368,6 +419,21 @@ public static void Enumerate(Activity activity, ref TState state)
368419
return;
369420
}
370421

422+
// TODO: When a limit has been configured an allocation-free enumerator is not used.
423+
// Need to either:
424+
// 1) modify the dynamically generated code to only enumerate up to the max number of items, or
425+
// 2) wait until .NET 7 is released and do this more easily with the new enumerator functions
426+
if (maxEvents.HasValue)
427+
{
428+
var enumerator = activityEvents.GetEnumerator();
429+
for (var i = 0; enumerator.MoveNext() && i < maxEvents; ++i)
430+
{
431+
state.ForEach(enumerator.Current);
432+
}
433+
434+
return;
435+
}
436+
371437
ActivityEventsEnumerator(
372438
activityEvents,
373439
ref state,

src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
## Unreleased
44

5+
* Adds support for limiting the length and count of attributes exported from
6+
the OTLP exporter. These
7+
[Attribute Limits](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#attribute-limits)
8+
are configured via the environment variables defined in the specification.
9+
([#3376](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3376))
10+
511
* The `MetricReaderOptions` defaults can be overridden using
612
`OTEL_METRIC_EXPORT_INTERVAL` and `OTEL_METRIC_EXPORT_TIMEOUT`
713
environmental variables as defined in the
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// <copyright file="EnvironmentVariableConfiguration.cs" company="OpenTelemetry Authors">
2+
// Copyright The OpenTelemetry Authors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
// </copyright>
16+
17+
using System;
18+
using OpenTelemetry.Internal;
19+
20+
namespace OpenTelemetry.Configuration;
21+
22+
internal class EnvironmentVariableConfiguration
23+
{
24+
public static void InitializeDefaultConfigurationFromEnvironment(SdkConfiguration sdkConfiguration)
25+
{
26+
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#attribute-limits
27+
SetIntConfigValue("OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT", value => sdkConfiguration.AttributeValueLengthLimit = value);
28+
SetIntConfigValue("OTEL_ATTRIBUTE_COUNT_LIMIT", value => sdkConfiguration.AttributeCountLimit = value);
29+
30+
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#span-limits
31+
SetIntConfigValue("OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT", value => sdkConfiguration.SpanAttributeValueLengthLimit = value);
32+
SetIntConfigValue("OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT", value => sdkConfiguration.SpanAttributeCountLimit = value);
33+
SetIntConfigValue("OTEL_SPAN_EVENT_COUNT_LIMIT", value => sdkConfiguration.SpanEventCountLimit = value);
34+
SetIntConfigValue("OTEL_SPAN_LINK_COUNT_LIMIT", value => sdkConfiguration.SpanLinkCountLimit = value);
35+
SetIntConfigValue("OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT", value => sdkConfiguration.EventAttributeCountLimit = value);
36+
SetIntConfigValue("OTEL_LINK_ATTRIBUTE_COUNT_LIMIT", value => sdkConfiguration.LinkAttributeCountLimit = value);
37+
}
38+
39+
private static void SetIntConfigValue(string key, Action<int> setter)
40+
{
41+
if (EnvironmentVariableHelper.LoadNumeric(key, out var result))
42+
{
43+
setter(result);
44+
}
45+
}
46+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// <copyright file="SdkConfiguration.cs" company="OpenTelemetry Authors">
2+
// Copyright The OpenTelemetry Authors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
// </copyright>
16+
17+
namespace OpenTelemetry.Configuration;
18+
19+
internal class SdkConfiguration
20+
{
21+
private int? spanAttributeValueLengthLimit;
22+
private int? spanAttributeCountLimit;
23+
private int? eventAttributeCountLimit;
24+
private int? linkAttributeCountLimit;
25+
26+
private SdkConfiguration()
27+
{
28+
EnvironmentVariableConfiguration.InitializeDefaultConfigurationFromEnvironment(this);
29+
}
30+
31+
public static SdkConfiguration Instance { get; private set; } = new SdkConfiguration();
32+
33+
public int? AttributeValueLengthLimit { get; set; }
34+
35+
public int? AttributeCountLimit { get; set; }
36+
37+
public int? SpanAttributeValueLengthLimit
38+
{
39+
get => this.spanAttributeValueLengthLimit ?? this.AttributeValueLengthLimit;
40+
set => this.spanAttributeValueLengthLimit = value;
41+
}
42+
43+
public int? SpanAttributeCountLimit
44+
{
45+
get => this.spanAttributeCountLimit ?? this.AttributeCountLimit;
46+
set => this.spanAttributeCountLimit = value;
47+
}
48+
49+
public int? SpanEventCountLimit { get; set; }
50+
51+
public int? SpanLinkCountLimit { get; set; }
52+
53+
public int? EventAttributeCountLimit
54+
{
55+
get => this.eventAttributeCountLimit ?? this.SpanAttributeCountLimit;
56+
set => this.eventAttributeCountLimit = value;
57+
}
58+
59+
public int? LinkAttributeCountLimit
60+
{
61+
get => this.linkAttributeCountLimit ?? this.SpanAttributeCountLimit;
62+
set => this.linkAttributeCountLimit = value;
63+
}
64+
65+
internal static void Reset()
66+
{
67+
Instance = new SdkConfiguration();
68+
}
69+
}

0 commit comments

Comments
 (0)