Skip to content

Commit 96da4af

Browse files
authored
Merge branch 'main' into reyang/aspnetcore-logging-example
2 parents 061748d + b870ed9 commit 96da4af

24 files changed

+318
-154
lines changed

docs/metrics/README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,15 @@ MyFruitCounter.Add(5, new("name", "apple"), new("color", "red"), new("taste", "s
2727
MyFruitCounter.Add(7, new("color", "red"), new("name", "apple"), new("taste", "sweet")); // <--- DON'T DO THIS
2828
```
2929

30-
- When emitting metrics with more than three tags, use `TagList` for better
31-
performance. Using
30+
For the best performance, it is highly recommended to pass in tags in certain
31+
ways so allocations are only happening on the stack rather than the heap,
32+
which eliminates pressure on the GC (garbage collector):
33+
34+
- When reporting measurements with 3 tags or less,
35+
emit the tags individually.
36+
- When reporting measurements with more than 3 tags, use
3237
[`TagList`](https://learn.microsoft.com/dotnet/api/system.diagnostics.taglist?view=net-7.0#remarks)
33-
avoids allocating any memory for up to eight tags, thereby, reducing the
34-
pressure on GC to free up memory.
38+
for better performance.
3539

3640
```csharp
3741
var tags = new TagList

src/OpenTelemetry/Metrics/AggregationType.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
// limitations under the License.
1515
// </copyright>
1616

17+
#nullable enable
18+
1719
namespace OpenTelemetry.Metrics;
1820

1921
internal enum AggregationType

src/OpenTelemetry/Metrics/AggregatorStore.cs

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
// limitations under the License.
1515
// </copyright>
1616

17+
#nullable enable
18+
1719
using System.Collections.Concurrent;
20+
using System.Diagnostics;
1821
using System.Runtime.CompilerServices;
1922
using OpenTelemetry.Internal;
2023

@@ -23,11 +26,12 @@ namespace OpenTelemetry.Metrics;
2326
internal sealed class AggregatorStore
2427
{
2528
private static readonly string MetricPointCapHitFixMessage = "Consider opting in for the experimental SDK feature to emit all the throttled metrics under the overflow attribute by setting env variable OTEL_DOTNET_EXPERIMENTAL_METRICS_EMIT_OVERFLOW_ATTRIBUTE = true. You could also modify instrumentation to reduce the number of unique key/value pair combinations. Or use Views to drop unwanted tags. Or use MeterProviderBuilder.SetMaxMetricPointsPerMetricStream to set higher limit.";
26-
private static readonly Comparison<KeyValuePair<string, object>> DimensionComparisonDelegate = (x, y) => x.Key.CompareTo(y.Key);
29+
private static readonly Comparison<KeyValuePair<string, object?>> DimensionComparisonDelegate = (x, y) => x.Key.CompareTo(y.Key);
30+
private static readonly ExemplarFilter DefaultExemplarFilter = new AlwaysOffExemplarFilter();
2731

2832
private readonly object lockZeroTags = new();
2933
private readonly object lockOverflowTag = new();
30-
private readonly HashSet<string> tagKeysInteresting;
34+
private readonly HashSet<string>? tagKeysInteresting;
3135
private readonly int tagsKeysInterestingCount;
3236

3337
private readonly ConcurrentDictionary<Tags, int> tagsToMetricPointIndexDictionary =
@@ -60,7 +64,7 @@ internal AggregatorStore(
6064
AggregationTemporality temporality,
6165
int maxMetricPoints,
6266
bool emitOverflowAttribute,
63-
ExemplarFilter exemplarFilter = null)
67+
ExemplarFilter? exemplarFilter = null)
6468
{
6569
this.name = metricStreamIdentity.InstrumentName;
6670
this.maxMetricPoints = maxMetricPoints;
@@ -73,7 +77,7 @@ internal AggregatorStore(
7377
this.exponentialHistogramMaxSize = metricStreamIdentity.ExponentialHistogramMaxSize;
7478
this.exponentialHistogramMaxScale = metricStreamIdentity.ExponentialHistogramMaxScale;
7579
this.StartTimeExclusive = DateTimeOffset.UtcNow;
76-
this.exemplarFilter = exemplarFilter ?? new AlwaysOffExemplarFilter();
80+
this.exemplarFilter = exemplarFilter ?? DefaultExemplarFilter;
7781
if (metricStreamIdentity.TagKeys == null)
7882
{
7983
this.updateLongCallback = this.UpdateLong;
@@ -98,9 +102,9 @@ internal AggregatorStore(
98102
}
99103
}
100104

101-
private delegate void UpdateLongDelegate(long value, ReadOnlySpan<KeyValuePair<string, object>> tags);
105+
private delegate void UpdateLongDelegate(long value, ReadOnlySpan<KeyValuePair<string, object?>> tags);
102106

103-
private delegate void UpdateDoubleDelegate(double value, ReadOnlySpan<KeyValuePair<string, object>> tags);
107+
private delegate void UpdateDoubleDelegate(double value, ReadOnlySpan<KeyValuePair<string, object?>> tags);
104108

105109
internal DateTimeOffset StartTimeExclusive { get; private set; }
106110

@@ -115,12 +119,12 @@ internal bool IsExemplarEnabled()
115119
return this.exemplarFilter is not AlwaysOffExemplarFilter;
116120
}
117121

118-
internal void Update(long value, ReadOnlySpan<KeyValuePair<string, object>> tags)
122+
internal void Update(long value, ReadOnlySpan<KeyValuePair<string, object?>> tags)
119123
{
120124
this.updateLongCallback(value, tags);
121125
}
122126

123-
internal void Update(double value, ReadOnlySpan<KeyValuePair<string, object>> tags)
127+
internal void Update(double value, ReadOnlySpan<KeyValuePair<string, object?>> tags)
124128
{
125129
this.updateDoubleCallback(value, tags);
126130
}
@@ -234,15 +238,15 @@ private void InitializeOverflowTagPointIfNotInitialized()
234238
{
235239
if (!this.overflowTagMetricPointInitialized)
236240
{
237-
this.metricPoints[1] = new MetricPoint(this, this.aggType, new KeyValuePair<string, object>[] { new("otel.metric.overflow", true) }, this.histogramBounds, this.exponentialHistogramMaxSize, this.exponentialHistogramMaxScale);
241+
this.metricPoints[1] = new MetricPoint(this, this.aggType, new KeyValuePair<string, object?>[] { new("otel.metric.overflow", true) }, this.histogramBounds, this.exponentialHistogramMaxSize, this.exponentialHistogramMaxScale);
238242
this.overflowTagMetricPointInitialized = true;
239243
}
240244
}
241245
}
242246
}
243247

244248
[MethodImpl(MethodImplOptions.AggressiveInlining)]
245-
private int LookupAggregatorStore(KeyValuePair<string, object>[] tagKeysAndValues, int length)
249+
private int LookupAggregatorStore(KeyValuePair<string, object?>[] tagKeysAndValues, int length)
246250
{
247251
var givenTags = new Tags(tagKeysAndValues);
248252

@@ -275,10 +279,10 @@ private int LookupAggregatorStore(KeyValuePair<string, object>[] tagKeysAndValue
275279
// so we need to make a deep copy for Dictionary storage.
276280
if (length <= ThreadStaticStorage.MaxTagCacheSize)
277281
{
278-
var givenTagKeysAndValues = new KeyValuePair<string, object>[length];
282+
var givenTagKeysAndValues = new KeyValuePair<string, object?>[length];
279283
tagKeysAndValues.CopyTo(givenTagKeysAndValues.AsSpan());
280284

281-
var sortedTagKeysAndValues = new KeyValuePair<string, object>[length];
285+
var sortedTagKeysAndValues = new KeyValuePair<string, object?>[length];
282286
tempSortedTagKeysAndValues.CopyTo(sortedTagKeysAndValues.AsSpan());
283287

284288
givenTags = new Tags(givenTagKeysAndValues);
@@ -328,7 +332,7 @@ private int LookupAggregatorStore(KeyValuePair<string, object>[] tagKeysAndValue
328332
}
329333

330334
// Note: We are using storage from ThreadStatic, so need to make a deep copy for Dictionary storage.
331-
var givenTagKeysAndValues = new KeyValuePair<string, object>[length];
335+
var givenTagKeysAndValues = new KeyValuePair<string, object?>[length];
332336

333337
tagKeysAndValues.CopyTo(givenTagKeysAndValues.AsSpan());
334338

@@ -366,7 +370,7 @@ private int LookupAggregatorStore(KeyValuePair<string, object>[] tagKeysAndValue
366370
return aggregatorIndex;
367371
}
368372

369-
private void UpdateLong(long value, ReadOnlySpan<KeyValuePair<string, object>> tags)
373+
private void UpdateLong(long value, ReadOnlySpan<KeyValuePair<string, object?>> tags)
370374
{
371375
try
372376
{
@@ -407,7 +411,7 @@ private void UpdateLong(long value, ReadOnlySpan<KeyValuePair<string, object>> t
407411
}
408412
}
409413

410-
private void UpdateLongCustomTags(long value, ReadOnlySpan<KeyValuePair<string, object>> tags)
414+
private void UpdateLongCustomTags(long value, ReadOnlySpan<KeyValuePair<string, object?>> tags)
411415
{
412416
try
413417
{
@@ -448,7 +452,7 @@ private void UpdateLongCustomTags(long value, ReadOnlySpan<KeyValuePair<string,
448452
}
449453
}
450454

451-
private void UpdateDouble(double value, ReadOnlySpan<KeyValuePair<string, object>> tags)
455+
private void UpdateDouble(double value, ReadOnlySpan<KeyValuePair<string, object?>> tags)
452456
{
453457
try
454458
{
@@ -489,7 +493,7 @@ private void UpdateDouble(double value, ReadOnlySpan<KeyValuePair<string, object
489493
}
490494
}
491495

492-
private void UpdateDoubleCustomTags(double value, ReadOnlySpan<KeyValuePair<string, object>> tags)
496+
private void UpdateDoubleCustomTags(double value, ReadOnlySpan<KeyValuePair<string, object?>> tags)
493497
{
494498
try
495499
{
@@ -530,7 +534,7 @@ private void UpdateDoubleCustomTags(double value, ReadOnlySpan<KeyValuePair<stri
530534
}
531535
}
532536

533-
private int FindMetricAggregatorsDefault(ReadOnlySpan<KeyValuePair<string, object>> tags)
537+
private int FindMetricAggregatorsDefault(ReadOnlySpan<KeyValuePair<string, object?>> tags)
534538
{
535539
int tagLength = tags.Length;
536540
if (tagLength == 0)
@@ -546,7 +550,7 @@ private int FindMetricAggregatorsDefault(ReadOnlySpan<KeyValuePair<string, objec
546550
return this.LookupAggregatorStore(tagKeysAndValues, tagLength);
547551
}
548552

549-
private int FindMetricAggregatorsCustomTag(ReadOnlySpan<KeyValuePair<string, object>> tags)
553+
private int FindMetricAggregatorsCustomTag(ReadOnlySpan<KeyValuePair<string, object?>> tags)
550554
{
551555
int tagLength = tags.Length;
552556
if (tagLength == 0 || this.tagsKeysInterestingCount == 0)
@@ -555,12 +559,11 @@ private int FindMetricAggregatorsCustomTag(ReadOnlySpan<KeyValuePair<string, obj
555559
return 0;
556560
}
557561

558-
// TODO: Get only interesting tags
559-
// from the incoming tags
560-
561562
var storage = ThreadStaticStorage.GetStorage();
562563

563-
storage.SplitToKeysAndValues(tags, tagLength, this.tagKeysInteresting, out var tagKeysAndValues, out var actualLength);
564+
Debug.Assert(this.tagKeysInteresting != null, "this.tagKeysInteresting was null");
565+
566+
storage.SplitToKeysAndValues(tags, tagLength, this.tagKeysInteresting!, out var tagKeysAndValues, out var actualLength);
564567

565568
// Actual number of tags depend on how many
566569
// of the incoming tags has user opted to
@@ -571,6 +574,8 @@ private int FindMetricAggregatorsCustomTag(ReadOnlySpan<KeyValuePair<string, obj
571574
return 0;
572575
}
573576

574-
return this.LookupAggregatorStore(tagKeysAndValues, actualLength);
577+
Debug.Assert(tagKeysAndValues != null, "tagKeysAndValues was null");
578+
579+
return this.LookupAggregatorStore(tagKeysAndValues!, actualLength);
575580
}
576581
}

src/OpenTelemetry/Metrics/Base2ExponentialBucketHistogram.LowerBoundary.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
// limitations under the License.
1515
// </copyright>
1616

17+
#nullable enable
18+
1719
namespace OpenTelemetry.Metrics;
1820

1921
/// <content>

src/OpenTelemetry/Metrics/Base2ExponentialBucketHistogram.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
// limitations under the License.
1515
// </copyright>
1616

17+
#nullable enable
18+
1719
using System.Diagnostics;
1820
using OpenTelemetry.Internal;
1921

@@ -38,7 +40,7 @@ internal sealed partial class Base2ExponentialBucketHistogram
3840

3941
internal int IsCriticalSectionOccupied = 0;
4042

41-
internal ExponentialHistogramData SnapshotExponentialHistogramData = new ExponentialHistogramData();
43+
internal ExponentialHistogramData SnapshotExponentialHistogramData = new();
4244

4345
private int scale;
4446
private double scalingFactor; // 2 ^ scale / log(2)
@@ -237,11 +239,13 @@ internal ExponentialHistogramData GetExponentialHistogramData()
237239
internal Base2ExponentialBucketHistogram Copy()
238240
{
239241
Debug.Assert(this.PositiveBuckets.Capacity == this.NegativeBuckets.Capacity, "Capacity of positive and negative buckets are not equal.");
240-
var copy = new Base2ExponentialBucketHistogram(this.PositiveBuckets.Capacity, this.SnapshotExponentialHistogramData.Scale);
241-
copy.SnapshotSum = this.SnapshotSum;
242-
copy.SnapshotMin = this.SnapshotMin;
243-
copy.SnapshotMax = this.SnapshotMax;
244-
copy.SnapshotExponentialHistogramData = this.SnapshotExponentialHistogramData.Copy();
245-
return copy;
242+
243+
return new Base2ExponentialBucketHistogram(this.PositiveBuckets.Capacity, this.SnapshotExponentialHistogramData.Scale)
244+
{
245+
SnapshotSum = this.SnapshotSum,
246+
SnapshotMin = this.SnapshotMin,
247+
SnapshotMax = this.SnapshotMax,
248+
SnapshotExponentialHistogramData = this.SnapshotExponentialHistogramData.Copy(),
249+
};
246250
}
247251
}

src/OpenTelemetry/Metrics/Base2ExponentialBucketHistogramConfiguration.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
// limitations under the License.
1515
// </copyright>
1616

17+
#nullable enable
18+
1719
namespace OpenTelemetry.Metrics;
1820

1921
/// <summary>

src/OpenTelemetry/Metrics/CircularBufferBuckets.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
// limitations under the License.
1515
// </copyright>
1616

17+
#nullable enable
18+
1719
using System.Diagnostics;
1820
using System.Runtime.CompilerServices;
1921
using OpenTelemetry.Internal;
@@ -25,7 +27,7 @@ namespace OpenTelemetry.Metrics;
2527
/// </summary>
2628
internal sealed class CircularBufferBuckets
2729
{
28-
private long[] trait;
30+
private long[]? trait;
2931
private int begin = 0;
3032
private int end = -1;
3133

@@ -62,7 +64,12 @@ public CircularBufferBuckets(int capacity)
6264
/// </remarks>
6365
public long this[int index]
6466
{
65-
get => this.trait[this.ModuloIndex(index)];
67+
get
68+
{
69+
Debug.Assert(this.trait != null, "trait was null");
70+
71+
return this.trait![this.ModuloIndex(index)];
72+
}
6673
}
6774

6875
/// <summary>

src/OpenTelemetry/Metrics/CompositeMetricReader.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
// limitations under the License.
1515
// </copyright>
1616

17+
#nullable enable
18+
1719
using System.Diagnostics;
20+
using System.Diagnostics.CodeAnalysis;
1821
using OpenTelemetry.Internal;
1922

2023
namespace OpenTelemetry.Metrics;
@@ -153,14 +156,15 @@ protected override void Dispose(bool disposing)
153156

154157
public struct Enumerator
155158
{
156-
private DoublyLinkedListNode node;
159+
private DoublyLinkedListNode? node;
157160

158161
internal Enumerator(DoublyLinkedListNode node)
159162
{
160163
this.node = node;
161164
this.Current = null;
162165
}
163166

167+
[AllowNull]
164168
public MetricReader Current { get; private set; }
165169

166170
public bool MoveNext()
@@ -185,8 +189,8 @@ public DoublyLinkedListNode(MetricReader value)
185189
this.Value = value;
186190
}
187191

188-
public DoublyLinkedListNode Previous { get; set; }
192+
public DoublyLinkedListNode? Previous { get; set; }
189193

190-
public DoublyLinkedListNode Next { get; set; }
194+
public DoublyLinkedListNode? Next { get; set; }
191195
}
192196
}

0 commit comments

Comments
 (0)