Skip to content

Commit

Permalink
Merge branch 'main' into server.port-always
Browse files Browse the repository at this point in the history
  • Loading branch information
Kielek committed Mar 8, 2024
2 parents 279595c + 0820101 commit b4b2905
Show file tree
Hide file tree
Showing 13 changed files with 149 additions and 86 deletions.
6 changes: 6 additions & 0 deletions src/OpenTelemetry/.publicApi/Stable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
OpenTelemetry.OpenTelemetryBuilderSdkExtensions
OpenTelemetry.Trace.ActivityExportProcessorOptions
OpenTelemetry.Trace.ActivityExportProcessorOptions.ActivityExportProcessorOptions() -> void
OpenTelemetry.Trace.ActivityExportProcessorOptions.BatchExportProcessorOptions.get -> OpenTelemetry.Trace.BatchExportActivityProcessorOptions!
OpenTelemetry.Trace.ActivityExportProcessorOptions.BatchExportProcessorOptions.set -> void
OpenTelemetry.Trace.ActivityExportProcessorOptions.ExportProcessorType.get -> OpenTelemetry.ExportProcessorType
OpenTelemetry.Trace.ActivityExportProcessorOptions.ExportProcessorType.set -> void
static OpenTelemetry.OpenTelemetryBuilderSdkExtensions.ConfigureResource(this OpenTelemetry.IOpenTelemetryBuilder! builder, System.Action<OpenTelemetry.Resources.ResourceBuilder!>! configure) -> OpenTelemetry.IOpenTelemetryBuilder!
static OpenTelemetry.OpenTelemetryBuilderSdkExtensions.WithMetrics(this OpenTelemetry.IOpenTelemetryBuilder! builder) -> OpenTelemetry.IOpenTelemetryBuilder!
static OpenTelemetry.OpenTelemetryBuilderSdkExtensions.WithMetrics(this OpenTelemetry.IOpenTelemetryBuilder! builder, System.Action<OpenTelemetry.Metrics.MeterProviderBuilder!>! configure) -> OpenTelemetry.IOpenTelemetryBuilder!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,6 @@ public static IServiceCollection AddOpenTelemetryLoggerProviderBuilderServices(t

services!.TryAddSingleton<LoggerProviderBuilderSdk>();
services!.RegisterOptionsFactory(configuration => new BatchExportLogRecordProcessorOptions(configuration));

// Note: This registers a factory so that when
// sp.GetRequiredService<IOptionsMonitor<LogRecordExportProcessorOptions>>().Get(name)))
// is executed the SDK internal
// BatchExportLogRecordProcessorOptions(IConfiguration) ctor is used
// correctly which allows users to control the OTEL_BLRP_* keys using
// IConfiguration (envvars, appSettings, cli, etc.).
services!.RegisterOptionsFactory(
(sp, configuration, name) => new LogRecordExportProcessorOptions(
sp.GetRequiredService<IOptionsMonitor<BatchExportLogRecordProcessorOptions>>().Get(name)));
Expand All @@ -40,7 +33,10 @@ public static IServiceCollection AddOpenTelemetryMeterProviderBuilderServices(th
Debug.Assert(services != null, "services was null");

services!.TryAddSingleton<MeterProviderBuilderSdk>();
services!.RegisterOptionsFactory(configuration => new MetricReaderOptions(configuration));
services!.RegisterOptionsFactory(configuration => new PeriodicExportingMetricReaderOptions(configuration));
services!.RegisterOptionsFactory(
(sp, configuration, name) => new MetricReaderOptions(
sp.GetRequiredService<IOptionsMonitor<PeriodicExportingMetricReaderOptions>>().Get(name)));

return services!;
}
Expand All @@ -51,6 +47,9 @@ public static IServiceCollection AddOpenTelemetryTracerProviderBuilderServices(t

services!.TryAddSingleton<TracerProviderBuilderSdk>();
services!.RegisterOptionsFactory(configuration => new BatchExportActivityProcessorOptions(configuration));
services!.RegisterOptionsFactory(
(sp, configuration, name) => new ActivityExportProcessorOptions(
sp.GetRequiredService<IOptionsMonitor<BatchExportActivityProcessorOptions>>().Get(name)));

return services!;
}
Expand Down
54 changes: 25 additions & 29 deletions src/OpenTelemetry/Metrics/AggregatorStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ internal sealed class AggregatorStore
#endif
internal readonly bool OutputDelta;
internal readonly bool OutputDeltaWithUnusedMetricPointReclaimEnabled;
internal readonly int CardinalityLimit;
internal readonly int NumberOfMetricPoints;
internal readonly bool EmitOverflowAttribute;
internal readonly ConcurrentDictionary<Tags, LookupData>? TagsToMetricPointIndexDictionaryDelta;
internal readonly Func<ExemplarReservoir?>? ExemplarReservoirFactory;
Expand Down Expand Up @@ -71,11 +71,15 @@ internal AggregatorStore(
Func<ExemplarReservoir?>? exemplarReservoirFactory = null)
{
this.name = metricStreamIdentity.InstrumentName;
this.CardinalityLimit = cardinalityLimit;

this.metricPointCapHitMessage = $"Maximum MetricPoints limit reached for this Metric stream. Configured limit: {this.CardinalityLimit}";
this.metricPoints = new MetricPoint[cardinalityLimit];
this.currentMetricPointBatch = new int[cardinalityLimit];
// Increase the CardinalityLimit by 2 to reserve additional space.
// This adjustment accounts for overflow attribute and a case where zero tags are provided.
// Previously, these were included within the original cardinalityLimit, but now they are explicitly added to enhance clarity.
this.NumberOfMetricPoints = cardinalityLimit + 2;

this.metricPointCapHitMessage = $"Maximum MetricPoints limit reached for this Metric stream. Configured limit: {cardinalityLimit}";
this.metricPoints = new MetricPoint[this.NumberOfMetricPoints];
this.currentMetricPointBatch = new int[this.NumberOfMetricPoints];
this.aggType = aggType;
this.OutputDelta = temporality == AggregationTemporality.Delta;
this.histogramBounds = metricStreamIdentity.HistogramBucketBounds ?? FindDefaultHistogramBounds(in metricStreamIdentity);
Expand Down Expand Up @@ -110,31 +114,26 @@ internal AggregatorStore(
|| this.exemplarFilter == ExemplarFilterType.TraceBased,
"this.exemplarFilter had an unexpected value");

var reservedMetricPointsCount = 1;

if (emitOverflowAttribute)
{
// Setting metricPointIndex to 1 as we would reserve the metricPoints[1] for overflow attribute.
// Newer attributes should be added starting at the index: 2
this.metricPointIndex = 1;
reservedMetricPointsCount++;
}
// Setting metricPointIndex to 1 as we would reserve the metricPoints[1] for overflow attribute.
// Newer attributes should be added starting at the index: 2
this.metricPointIndex = 1;

this.OutputDeltaWithUnusedMetricPointReclaimEnabled = shouldReclaimUnusedMetricPoints && this.OutputDelta;

if (this.OutputDeltaWithUnusedMetricPointReclaimEnabled)
{
this.availableMetricPoints = new Queue<int>(cardinalityLimit - reservedMetricPointsCount);
this.availableMetricPoints = new Queue<int>(cardinalityLimit);

// There is no overload which only takes capacity as the parameter
// Using the DefaultConcurrencyLevel defined in the ConcurrentDictionary class: https://github.com/dotnet/runtime/blob/v7.0.5/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs#L2020
// We expect at the most (maxMetricPoints - reservedMetricPointsCount) * 2 entries- one for sorted and one for unsorted input
// We expect at the most (user provided cardinality limit) * 2 entries- one for sorted and one for unsorted input
this.TagsToMetricPointIndexDictionaryDelta =
new ConcurrentDictionary<Tags, LookupData>(concurrencyLevel: Environment.ProcessorCount, capacity: (cardinalityLimit - reservedMetricPointsCount) * 2);
new ConcurrentDictionary<Tags, LookupData>(concurrencyLevel: Environment.ProcessorCount, capacity: cardinalityLimit * 2);

// Add all the indices except for the reserved ones to the queue so that threads have
// readily available access to these MetricPoints for their use.
for (int i = reservedMetricPointsCount; i < this.CardinalityLimit; i++)
// Index 0 and 1 are reserved for no tags and overflow
for (int i = 2; i < this.NumberOfMetricPoints; i++)
{
this.availableMetricPoints.Enqueue(i);
}
Expand Down Expand Up @@ -199,12 +198,12 @@ internal int Snapshot()
}
else if (this.OutputDelta)
{
var indexSnapshot = Math.Min(this.metricPointIndex, this.CardinalityLimit - 1);
var indexSnapshot = Math.Min(this.metricPointIndex, this.NumberOfMetricPoints - 1);
this.SnapshotDelta(indexSnapshot);
}
else
{
var indexSnapshot = Math.Min(this.metricPointIndex, this.CardinalityLimit - 1);
var indexSnapshot = Math.Min(this.metricPointIndex, this.NumberOfMetricPoints - 1);
this.SnapshotCumulative(indexSnapshot);
}

Expand Down Expand Up @@ -260,12 +259,8 @@ internal void SnapshotDeltaWithMetricPointReclaim()
this.batchSize++;
}

int startIndexForReclaimableMetricPoints = 1;

if (this.EmitOverflowAttribute)
{
startIndexForReclaimableMetricPoints = 2; // Index 0 and 1 are reserved for no tags and overflow

// TakeSnapshot for the MetricPoint for overflow
ref var metricPointForOverflow = ref this.metricPoints[1];
if (metricPointForOverflow.MetricPointStatus != MetricPointStatus.NoCollectPending)
Expand All @@ -284,7 +279,8 @@ internal void SnapshotDeltaWithMetricPointReclaim()
}
}

for (int i = startIndexForReclaimableMetricPoints; i < this.CardinalityLimit; i++)
// Index 0 and 1 are reserved for no tags and overflow
for (int i = 2; i < this.NumberOfMetricPoints; i++)
{
ref var metricPoint = ref this.metricPoints[i];

Expand Down Expand Up @@ -473,7 +469,7 @@ private int LookupAggregatorStore(KeyValuePair<string, object?>[] tagKeysAndValu
if (!this.tagsToMetricPointIndexDictionary.TryGetValue(sortedTags, out aggregatorIndex))
{
aggregatorIndex = this.metricPointIndex;
if (aggregatorIndex >= this.CardinalityLimit)
if (aggregatorIndex >= this.NumberOfMetricPoints)
{
// sorry! out of data points.
// TODO: Once we support cleanup of
Expand Down Expand Up @@ -502,7 +498,7 @@ private int LookupAggregatorStore(KeyValuePair<string, object?>[] tagKeysAndValu
if (!this.tagsToMetricPointIndexDictionary.TryGetValue(sortedTags, out aggregatorIndex))
{
aggregatorIndex = ++this.metricPointIndex;
if (aggregatorIndex >= this.CardinalityLimit)
if (aggregatorIndex >= this.NumberOfMetricPoints)
{
// sorry! out of data points.
// TODO: Once we support cleanup of
Expand All @@ -529,7 +525,7 @@ private int LookupAggregatorStore(KeyValuePair<string, object?>[] tagKeysAndValu
{
// This else block is for tag length = 1
aggregatorIndex = this.metricPointIndex;
if (aggregatorIndex >= this.CardinalityLimit)
if (aggregatorIndex >= this.NumberOfMetricPoints)
{
// sorry! out of data points.
// TODO: Once we support cleanup of
Expand All @@ -551,7 +547,7 @@ private int LookupAggregatorStore(KeyValuePair<string, object?>[] tagKeysAndValu
if (!this.tagsToMetricPointIndexDictionary.TryGetValue(givenTags, out aggregatorIndex))
{
aggregatorIndex = ++this.metricPointIndex;
if (aggregatorIndex >= this.CardinalityLimit)
if (aggregatorIndex >= this.NumberOfMetricPoints)
{
// sorry! out of data points.
// TODO: Once we support cleanup of
Expand Down
10 changes: 1 addition & 9 deletions src/OpenTelemetry/Metrics/MetricReaderExt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,17 +176,9 @@ internal void ApplyParentProviderSettings(
this.metrics = new Metric[metricLimit];
this.metricsCurrentBatch = new Metric[metricLimit];
this.cardinalityLimit = cardinalityLimit;
this.emitOverflowAttribute = emitOverflowAttribute;
this.reclaimUnusedMetricPoints = reclaimUnusedMetricPoints;
this.exemplarFilter = exemplarFilter;

if (emitOverflowAttribute)
{
// We need at least two metric points. One is reserved for zero tags and the other one for overflow attribute
if (cardinalityLimit > 1)
{
this.emitOverflowAttribute = true;
}
}
}

private bool TryGetExistingMetric(in MetricStreamIdentity metricStreamIdentity, [NotNullWhen(true)] out Metric? existingMetric)
Expand Down
11 changes: 7 additions & 4 deletions src/OpenTelemetry/Metrics/MetricReaderOptions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using Microsoft.Extensions.Configuration;
using System.Diagnostics;
using OpenTelemetry.Internal;

namespace OpenTelemetry.Metrics;
Expand All @@ -17,13 +17,16 @@ public class MetricReaderOptions
/// Initializes a new instance of the <see cref="MetricReaderOptions"/> class.
/// </summary>
public MetricReaderOptions()
: this(new ConfigurationBuilder().AddEnvironmentVariables().Build())
: this(new())
{
}

internal MetricReaderOptions(IConfiguration configuration)
internal MetricReaderOptions(
PeriodicExportingMetricReaderOptions defaultPeriodicExportingMetricReaderOptions)
{
this.periodicExportingMetricReaderOptions = new PeriodicExportingMetricReaderOptions(configuration);
Debug.Assert(defaultPeriodicExportingMetricReaderOptions != null, "defaultPeriodicExportingMetricReaderOptions was null");

this.periodicExportingMetricReaderOptions = defaultPeriodicExportingMetricReaderOptions ?? new();
}

/// <summary>
Expand Down
8 changes: 6 additions & 2 deletions src/OpenTelemetry/Metrics/MetricStreamConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,12 @@ public string[]? TagKeys
/// <para>Spec reference: <see
/// href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#cardinality-limits">Cardinality
/// limits</see>.</para>
/// Note: If not set the default MeterProvider cardinality limit of 2000
/// will apply.
/// Note: The cardinality limit determines the maximum number of unique
/// dimension combinations for metrics.
/// Metrics with zero dimensions and overflow metrics are treated specially
/// and do not count against this limit.
/// If not set the default
/// MeterProvider cardinality limit of 2000 will apply.
/// </remarks>
#if NET8_0_OR_GREATER
[Experimental(DiagnosticDefinitions.CardinalityLimitExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
Expand Down
49 changes: 49 additions & 0 deletions src/OpenTelemetry/Trace/ActivityExportProcessorOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using System.Diagnostics;
using OpenTelemetry.Internal;

namespace OpenTelemetry.Trace;

/// <summary>
/// Options for configuring either a <see cref="SimpleActivityExportProcessor"/> or <see cref="BatchActivityExportProcessor"/>.
/// </summary>
public class ActivityExportProcessorOptions
{
private BatchExportActivityProcessorOptions batchExportProcessorOptions;

/// <summary>
/// Initializes a new instance of the <see cref="ActivityExportProcessorOptions"/> class.
/// </summary>
public ActivityExportProcessorOptions()
: this(new())
{
}

internal ActivityExportProcessorOptions(
BatchExportActivityProcessorOptions defaultBatchExportActivityProcessorOptions)
{
Debug.Assert(defaultBatchExportActivityProcessorOptions != null, "defaultBatchExportActivityProcessorOptions was null");

this.batchExportProcessorOptions = defaultBatchExportActivityProcessorOptions ?? new();
}

/// <summary>
/// Gets or sets the export processor type to be used. The default value is <see cref="ExportProcessorType.Batch"/>.
/// </summary>
public ExportProcessorType ExportProcessorType { get; set; } = ExportProcessorType.Batch;

/// <summary>
/// Gets or sets the batch export options. Ignored unless <see cref="ExportProcessorType"/> is <see cref="ExportProcessorType.Batch"/>.
/// </summary>
public BatchExportActivityProcessorOptions BatchExportProcessorOptions
{
get => this.batchExportProcessorOptions;
set
{
Guard.ThrowIfNull(value);
this.batchExportProcessorOptions = value;
}
}
}
4 changes: 2 additions & 2 deletions src/Shared/Options/ConfigurationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public static bool TryGetValue<T>(
public static IServiceCollection RegisterOptionsFactory<T>(
this IServiceCollection services,
Func<IConfiguration, T> optionsFactoryFunc)
where T : class, new()
where T : class
{
Debug.Assert(services != null, "services was null");
Debug.Assert(optionsFactoryFunc != null, "optionsFactoryFunc was null");
Expand All @@ -150,7 +150,7 @@ public static IServiceCollection RegisterOptionsFactory<T>(
public static IServiceCollection RegisterOptionsFactory<T>(
this IServiceCollection services,
Func<IServiceProvider, IConfiguration, string, T> optionsFactoryFunc)
where T : class, new()
where T : class
{
Debug.Assert(services != null, "services was null");
Debug.Assert(optionsFactoryFunc != null, "optionsFactoryFunc was null");
Expand Down
2 changes: 1 addition & 1 deletion src/Shared/Options/DelegatingOptionsFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace Microsoft.Extensions.Options
/// <typeparam name="TOptions">The type of options being requested.</typeparam>
internal sealed class DelegatingOptionsFactory<TOptions> :
IOptionsFactory<TOptions>
where TOptions : class, new()
where TOptions : class
{
private readonly Func<IConfiguration, string, TOptions> optionsFactoryFunc;
private readonly IConfiguration configuration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,10 @@ public void CreatePeriodicExportingMetricReader_FromIConfiguration()
.AddInMemoryCollection(values)
.Build();

var options = new MetricReaderOptions(configuration);
var options = new PeriodicExportingMetricReaderOptions(configuration);

Assert.Equal(18, options.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds);
Assert.Equal(19, options.PeriodicExportingMetricReaderOptions.ExportTimeoutMilliseconds);
Assert.Equal(18, options.ExportIntervalMilliseconds);
Assert.Equal(19, options.ExportTimeoutMilliseconds);
}

[Fact]
Expand Down
17 changes: 16 additions & 1 deletion test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1399,7 +1399,22 @@ int MetricPointCount()

foreach (var metric in exportedItems)
{
foreach (ref readonly var metricPoint in metric.GetMetricPoints())
var enumerator = metric.GetMetricPoints().GetEnumerator();

// A case with zero tags and overflow attribute and are not a part of cardinality limit. Avoid counting them.
enumerator.MoveNext(); // First element reserved for zero tags.
enumerator.MoveNext(); // Second element reserved for overflow attribute.

// Validate second element is overflow attribute.
// Overflow attribute is behind experimental flag. So, it is not guaranteed to be present.
var tagEnumerator = enumerator.Current.Tags.GetEnumerator();
tagEnumerator.MoveNext();
if (!tagEnumerator.Current.Key.Contains("otel.metric.overflow"))
{
count++;
}

while (enumerator.MoveNext())
{
count++;
}
Expand Down
Loading

0 comments on commit b4b2905

Please sign in to comment.