Allow duplicate instrument registration (#2916)
This commit is contained in:
parent
62217ce2df
commit
ffffe5cc5a
|
|
@ -62,13 +62,13 @@ namespace OpenTelemetry.Exporter
|
|||
msg.Append($", Unit: {metric.Unit}");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(metric.Meter.Name))
|
||||
if (!string.IsNullOrEmpty(metric.MeterName))
|
||||
{
|
||||
msg.Append($", Meter: {metric.Meter.Name}");
|
||||
msg.Append($", Meter: {metric.MeterName}");
|
||||
|
||||
if (!string.IsNullOrEmpty(metric.Meter.Version))
|
||||
if (!string.IsNullOrEmpty(metric.MeterVersion))
|
||||
{
|
||||
msg.Append($"/{metric.Meter.Version}");
|
||||
msg.Append($"/{metric.MeterVersion}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -60,10 +60,10 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
|
|||
continue;
|
||||
}
|
||||
|
||||
var meterName = metric.Meter.Name;
|
||||
var meterName = metric.MeterName;
|
||||
if (!metricsByLibrary.TryGetValue(meterName, out var instrumentationLibraryMetrics))
|
||||
{
|
||||
instrumentationLibraryMetrics = GetMetricListFromPool(meterName, metric.Meter.Version);
|
||||
instrumentationLibraryMetrics = GetMetricListFromPool(meterName, metric.MeterVersion);
|
||||
|
||||
metricsByLibrary.Add(meterName, instrumentationLibraryMetrics);
|
||||
resourceMetrics.InstrumentationLibraryMetrics.Add(instrumentationLibraryMetrics);
|
||||
|
|
|
|||
|
|
@ -63,7 +63,8 @@ OpenTelemetry.Metrics.MeterProviderExtensions
|
|||
OpenTelemetry.Metrics.Metric
|
||||
OpenTelemetry.Metrics.Metric.Description.get -> string
|
||||
OpenTelemetry.Metrics.Metric.GetMetricPoints() -> OpenTelemetry.Metrics.MetricPointsAccessor
|
||||
OpenTelemetry.Metrics.Metric.Meter.get -> System.Diagnostics.Metrics.Meter
|
||||
OpenTelemetry.Metrics.Metric.MeterName.get -> string
|
||||
OpenTelemetry.Metrics.Metric.MeterVersion.get -> string
|
||||
OpenTelemetry.Metrics.Metric.MetricType.get -> OpenTelemetry.Metrics.MetricType
|
||||
OpenTelemetry.Metrics.Metric.Name.get -> string
|
||||
OpenTelemetry.Metrics.Metric.Temporality.get -> OpenTelemetry.Metrics.AggregationTemporality
|
||||
|
|
|
|||
|
|
@ -63,7 +63,8 @@ OpenTelemetry.Metrics.MeterProviderExtensions
|
|||
OpenTelemetry.Metrics.Metric
|
||||
OpenTelemetry.Metrics.Metric.Description.get -> string
|
||||
OpenTelemetry.Metrics.Metric.GetMetricPoints() -> OpenTelemetry.Metrics.MetricPointsAccessor
|
||||
OpenTelemetry.Metrics.Metric.Meter.get -> System.Diagnostics.Metrics.Meter
|
||||
OpenTelemetry.Metrics.Metric.MeterName.get -> string
|
||||
OpenTelemetry.Metrics.Metric.MeterVersion.get -> string
|
||||
OpenTelemetry.Metrics.Metric.MetricType.get -> OpenTelemetry.Metrics.MetricType
|
||||
OpenTelemetry.Metrics.Metric.Name.get -> string
|
||||
OpenTelemetry.Metrics.Metric.Temporality.get -> OpenTelemetry.Metrics.AggregationTemporality
|
||||
|
|
|
|||
|
|
@ -2,14 +2,29 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
* Instantiating multiple metric instruments with the same name and also
|
||||
identical in all other respects - same type, description, and unit - result
|
||||
in a single metric stream aggregating measurements from all the identical
|
||||
instruments.
|
||||
|
||||
Instantiating multiple metric instruments with the same name but differ in
|
||||
some respect - different type, description, or unit - will result in a
|
||||
separate metric stream for each distinct instrument.
|
||||
|
||||
([#2916](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2916))
|
||||
|
||||
* The `Meter` property on `OpenTelemetry.Metrics.Metric` has been removed.
|
||||
It now has `MeterName` and `MeterVersion` properties.
|
||||
([#2916](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2916))
|
||||
|
||||
* Added support for implementing custom `ResourceDetector`.
|
||||
([#2949](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2949/)
|
||||
[#2897](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2897))
|
||||
|
||||
* Perf improvement for Histogram and HistogramSumCount by implementing lock-free
|
||||
updates.
|
||||
([2951](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2951))
|
||||
([2961](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2961))
|
||||
([#2951](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2951)
|
||||
[#2961](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2961))
|
||||
|
||||
## 1.2.0-rc2
|
||||
|
||||
|
|
@ -27,7 +42,7 @@ Released 2022-Feb-02
|
|||
|
||||
* Performance improvement: when emitting metrics, users are strongly advised to
|
||||
provide tags with same Key order, to achieve maximum performance.
|
||||
([#2805](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2805/files))
|
||||
([#2805](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2805))
|
||||
|
||||
## 1.2.0-rc1
|
||||
|
||||
|
|
|
|||
|
|
@ -360,6 +360,12 @@ namespace OpenTelemetry.Internal
|
|||
this.WriteEvent(37, providerName);
|
||||
}
|
||||
|
||||
[Event(38, Message = "Duplicate Instrument '{0}', Meter '{1}' encountered. Reason: '{2}'. Suggested action: '{3}'", Level = EventLevel.Warning)]
|
||||
public void DuplicateMetricInstrument(string instrumentName, string meterName, string reason, string fix)
|
||||
{
|
||||
this.WriteEvent(38, instrumentName, meterName, reason, fix);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public class OpenTelemetryEventListener : EventListener
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
// <copyright file="InstrumentIdentity.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.Metrics;
|
||||
|
||||
namespace OpenTelemetry.Metrics
|
||||
{
|
||||
internal readonly struct InstrumentIdentity : IEquatable<InstrumentIdentity>
|
||||
{
|
||||
private readonly int hashCode;
|
||||
|
||||
public InstrumentIdentity(Meter meter, string instrumentName, string unit, string description, Type instrumentType)
|
||||
{
|
||||
this.MeterName = meter.Name;
|
||||
this.MeterVersion = meter.Version ?? string.Empty;
|
||||
this.InstrumentName = instrumentName;
|
||||
this.Unit = unit ?? string.Empty;
|
||||
this.Description = description ?? string.Empty;
|
||||
this.InstrumentType = instrumentType;
|
||||
|
||||
unchecked
|
||||
{
|
||||
var hash = 17;
|
||||
hash = (hash * 31) + this.InstrumentType.GetHashCode();
|
||||
hash = (hash * 31) + this.MeterName.GetHashCode();
|
||||
hash = (hash * 31) + this.MeterVersion.GetHashCode();
|
||||
hash = (hash * 31) + this.InstrumentName.GetHashCode();
|
||||
hash = this.Unit == null ? hash : (hash * 31) + this.Unit.GetHashCode();
|
||||
hash = this.Description == null ? hash : (hash * 31) + this.Description.GetHashCode();
|
||||
this.hashCode = hash;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly string MeterName { get; }
|
||||
|
||||
public readonly string MeterVersion { get; }
|
||||
|
||||
public readonly string InstrumentName { get; }
|
||||
|
||||
public readonly string Unit { get; }
|
||||
|
||||
public readonly string Description { get; }
|
||||
|
||||
public readonly Type InstrumentType { get; }
|
||||
|
||||
public static bool operator ==(InstrumentIdentity metricIdentity1, InstrumentIdentity metricIdentity2) => metricIdentity1.Equals(metricIdentity2);
|
||||
|
||||
public static bool operator !=(InstrumentIdentity metricIdentity1, InstrumentIdentity metricIdentity2) => !metricIdentity1.Equals(metricIdentity2);
|
||||
|
||||
public readonly override bool Equals(object obj)
|
||||
{
|
||||
return obj is InstrumentIdentity other && this.Equals(other);
|
||||
}
|
||||
|
||||
public bool Equals(InstrumentIdentity other)
|
||||
{
|
||||
return this.InstrumentType == other.InstrumentType
|
||||
&& this.MeterName == other.MeterName
|
||||
&& this.MeterVersion == other.MeterVersion
|
||||
&& this.InstrumentName == other.InstrumentName
|
||||
&& this.Unit == other.Unit
|
||||
&& this.Description == other.Description;
|
||||
}
|
||||
|
||||
public readonly override int GetHashCode() => this.hashCode;
|
||||
}
|
||||
}
|
||||
|
|
@ -30,68 +30,63 @@ namespace OpenTelemetry.Metrics
|
|||
private readonly AggregatorStore aggStore;
|
||||
|
||||
internal Metric(
|
||||
Instrument instrument,
|
||||
InstrumentIdentity instrumentIdentity,
|
||||
AggregationTemporality temporality,
|
||||
string metricName,
|
||||
string metricDescription,
|
||||
int maxMetricPointsPerMetricStream,
|
||||
double[] histogramBounds = null,
|
||||
string[] tagKeysInteresting = null)
|
||||
{
|
||||
this.Name = metricName;
|
||||
this.Description = metricDescription ?? string.Empty;
|
||||
this.Unit = instrument.Unit ?? string.Empty;
|
||||
this.Meter = instrument.Meter;
|
||||
this.InstrumentIdentity = instrumentIdentity;
|
||||
|
||||
AggregationType aggType;
|
||||
if (instrument.GetType() == typeof(ObservableCounter<long>)
|
||||
|| instrument.GetType() == typeof(ObservableCounter<int>)
|
||||
|| instrument.GetType() == typeof(ObservableCounter<short>)
|
||||
|| instrument.GetType() == typeof(ObservableCounter<byte>))
|
||||
if (instrumentIdentity.InstrumentType == typeof(ObservableCounter<long>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(ObservableCounter<int>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(ObservableCounter<short>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(ObservableCounter<byte>))
|
||||
{
|
||||
aggType = AggregationType.LongSumIncomingCumulative;
|
||||
this.MetricType = MetricType.LongSum;
|
||||
}
|
||||
else if (instrument.GetType() == typeof(Counter<long>)
|
||||
|| instrument.GetType() == typeof(Counter<int>)
|
||||
|| instrument.GetType() == typeof(Counter<short>)
|
||||
|| instrument.GetType() == typeof(Counter<byte>))
|
||||
else if (instrumentIdentity.InstrumentType == typeof(Counter<long>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(Counter<int>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(Counter<short>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(Counter<byte>))
|
||||
{
|
||||
aggType = AggregationType.LongSumIncomingDelta;
|
||||
this.MetricType = MetricType.LongSum;
|
||||
}
|
||||
else if (instrument.GetType() == typeof(Counter<double>)
|
||||
|| instrument.GetType() == typeof(Counter<float>))
|
||||
else if (instrumentIdentity.InstrumentType == typeof(Counter<double>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(Counter<float>))
|
||||
{
|
||||
aggType = AggregationType.DoubleSumIncomingDelta;
|
||||
this.MetricType = MetricType.DoubleSum;
|
||||
}
|
||||
else if (instrument.GetType() == typeof(ObservableCounter<double>)
|
||||
|| instrument.GetType() == typeof(ObservableCounter<float>))
|
||||
else if (instrumentIdentity.InstrumentType == typeof(ObservableCounter<double>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(ObservableCounter<float>))
|
||||
{
|
||||
aggType = AggregationType.DoubleSumIncomingCumulative;
|
||||
this.MetricType = MetricType.DoubleSum;
|
||||
}
|
||||
else if (instrument.GetType() == typeof(ObservableGauge<double>)
|
||||
|| instrument.GetType() == typeof(ObservableGauge<float>))
|
||||
else if (instrumentIdentity.InstrumentType == typeof(ObservableGauge<double>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(ObservableGauge<float>))
|
||||
{
|
||||
aggType = AggregationType.DoubleGauge;
|
||||
this.MetricType = MetricType.DoubleGauge;
|
||||
}
|
||||
else if (instrument.GetType() == typeof(ObservableGauge<long>)
|
||||
|| instrument.GetType() == typeof(ObservableGauge<int>)
|
||||
|| instrument.GetType() == typeof(ObservableGauge<short>)
|
||||
|| instrument.GetType() == typeof(ObservableGauge<byte>))
|
||||
else if (instrumentIdentity.InstrumentType == typeof(ObservableGauge<long>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(ObservableGauge<int>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(ObservableGauge<short>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(ObservableGauge<byte>))
|
||||
{
|
||||
aggType = AggregationType.LongGauge;
|
||||
this.MetricType = MetricType.LongGauge;
|
||||
}
|
||||
else if (instrument.GetType() == typeof(Histogram<long>)
|
||||
|| instrument.GetType() == typeof(Histogram<int>)
|
||||
|| instrument.GetType() == typeof(Histogram<short>)
|
||||
|| instrument.GetType() == typeof(Histogram<byte>)
|
||||
|| instrument.GetType() == typeof(Histogram<float>)
|
||||
|| instrument.GetType() == typeof(Histogram<double>))
|
||||
else if (instrumentIdentity.InstrumentType == typeof(Histogram<long>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(Histogram<int>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(Histogram<short>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(Histogram<byte>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(Histogram<float>)
|
||||
|| instrumentIdentity.InstrumentType == typeof(Histogram<double>))
|
||||
{
|
||||
this.MetricType = MetricType.Histogram;
|
||||
|
||||
|
|
@ -107,10 +102,10 @@ namespace OpenTelemetry.Metrics
|
|||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"Unsupported Instrument Type: {instrument.GetType().FullName}");
|
||||
throw new NotSupportedException($"Unsupported Instrument Type: {instrumentIdentity.InstrumentType.FullName}");
|
||||
}
|
||||
|
||||
this.aggStore = new AggregatorStore(metricName, aggType, temporality, maxMetricPointsPerMetricStream, histogramBounds ?? DefaultHistogramBounds, tagKeysInteresting);
|
||||
this.aggStore = new AggregatorStore(instrumentIdentity.InstrumentName, aggType, temporality, maxMetricPointsPerMetricStream, histogramBounds ?? DefaultHistogramBounds, tagKeysInteresting);
|
||||
this.Temporality = temporality;
|
||||
this.InstrumentDisposed = false;
|
||||
}
|
||||
|
|
@ -119,13 +114,17 @@ namespace OpenTelemetry.Metrics
|
|||
|
||||
public AggregationTemporality Temporality { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
public string Name => this.InstrumentIdentity.InstrumentName;
|
||||
|
||||
public string Description { get; private set; }
|
||||
public string Description => this.InstrumentIdentity.Description;
|
||||
|
||||
public string Unit { get; private set; }
|
||||
public string Unit => this.InstrumentIdentity.Unit;
|
||||
|
||||
public Meter Meter { get; private set; }
|
||||
public string MeterName => this.InstrumentIdentity.MeterName;
|
||||
|
||||
public string MeterVersion => this.InstrumentIdentity.MeterVersion;
|
||||
|
||||
internal InstrumentIdentity InstrumentIdentity { get; private set; }
|
||||
|
||||
internal bool InstrumentDisposed { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Metrics;
|
||||
using OpenTelemetry.Internal;
|
||||
|
|
@ -27,6 +28,7 @@ namespace OpenTelemetry.Metrics
|
|||
public abstract partial class MetricReader
|
||||
{
|
||||
private readonly HashSet<string> metricStreamNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly ConcurrentDictionary<InstrumentIdentity, Metric> instrumentIdentityToMetric = new ConcurrentDictionary<InstrumentIdentity, Metric>();
|
||||
private readonly object instrumentCreationLock = new object();
|
||||
private int maxMetricStreams;
|
||||
private int maxMetricPointsPerMetricStream;
|
||||
|
|
@ -37,14 +39,24 @@ namespace OpenTelemetry.Metrics
|
|||
internal Metric AddMetricWithNoViews(Instrument instrument)
|
||||
{
|
||||
var meterName = instrument.Meter.Name;
|
||||
var meterVersion = instrument.Meter.Version;
|
||||
var metricName = instrument.Name;
|
||||
var metricStreamName = $"{meterName}.{metricName}";
|
||||
var metricStreamName = $"{meterName}.{meterVersion}.{metricName}";
|
||||
var instrumentIdentity = new InstrumentIdentity(instrument.Meter, metricName, instrument.Unit, instrument.Description, instrument.GetType());
|
||||
lock (this.instrumentCreationLock)
|
||||
{
|
||||
if (this.instrumentIdentityToMetric.TryGetValue(instrumentIdentity, out var existingMetric))
|
||||
{
|
||||
return existingMetric;
|
||||
}
|
||||
|
||||
if (this.metricStreamNames.Contains(metricStreamName))
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored(metricName, instrument.Meter.Name, "Metric name conflicting with existing name.", "Either change the name of the instrument or change name using View.");
|
||||
return null;
|
||||
OpenTelemetrySdkEventSource.Log.DuplicateMetricInstrument(
|
||||
metricName,
|
||||
meterName,
|
||||
"Metric instrument has the same name as an existing one but differs by description, unit, or instrument type. Measurements from this instrument will still be exported but may result in conflicts.",
|
||||
"Either change the name of the instrument or use MeterProviderBuilder.AddView to resolve the conflict.");
|
||||
}
|
||||
|
||||
var index = ++this.metricIndex;
|
||||
|
|
@ -55,7 +67,8 @@ namespace OpenTelemetry.Metrics
|
|||
}
|
||||
else
|
||||
{
|
||||
var metric = new Metric(instrument, this.Temporality, metricName, instrument.Description, this.maxMetricPointsPerMetricStream);
|
||||
var metric = new Metric(instrumentIdentity, this.Temporality, this.maxMetricPointsPerMetricStream);
|
||||
this.instrumentIdentityToMetric[instrumentIdentity] = metric;
|
||||
this.metrics[index] = metric;
|
||||
this.metricStreamNames.Add(metricStreamName);
|
||||
return metric;
|
||||
|
|
@ -88,8 +101,11 @@ namespace OpenTelemetry.Metrics
|
|||
{
|
||||
var metricStreamConfig = metricStreamConfigs[i];
|
||||
var meterName = instrument.Meter.Name;
|
||||
var meterVersion = instrument.Meter.Version;
|
||||
var metricName = metricStreamConfig?.Name ?? instrument.Name;
|
||||
var metricStreamName = $"{meterName}.{metricName}";
|
||||
var metricStreamName = $"{meterName}.{meterVersion}.{metricName}";
|
||||
var metricDescription = metricStreamConfig?.Description ?? instrument.Description;
|
||||
var instrumentIdentity = new InstrumentIdentity(instrument.Meter, metricName, instrument.Unit, metricDescription, instrument.GetType());
|
||||
|
||||
if (!MeterProviderBuilderSdk.IsValidInstrumentName(metricName))
|
||||
{
|
||||
|
|
@ -102,10 +118,19 @@ namespace OpenTelemetry.Metrics
|
|||
continue;
|
||||
}
|
||||
|
||||
if (this.instrumentIdentityToMetric.TryGetValue(instrumentIdentity, out var existingMetric))
|
||||
{
|
||||
metrics.Add(existingMetric);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.metricStreamNames.Contains(metricStreamName))
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored(metricName, instrument.Meter.Name, "Metric name conflicting with existing name.", "Either change the name of the instrument or change name using MeterProviderBuilder.AddView.");
|
||||
continue;
|
||||
OpenTelemetrySdkEventSource.Log.DuplicateMetricInstrument(
|
||||
metricName,
|
||||
meterName,
|
||||
"Metric instrument has the same name as an existing one but differs by description, unit, or instrument type. Measurements from this instrument will still be exported but may result in conflicts.",
|
||||
"Either change the name of the instrument or use MeterProviderBuilder.AddView to resolve the conflict.");
|
||||
}
|
||||
|
||||
if (metricStreamConfig?.Aggregation == Aggregation.Drop)
|
||||
|
|
@ -122,12 +147,12 @@ namespace OpenTelemetry.Metrics
|
|||
else
|
||||
{
|
||||
Metric metric;
|
||||
var metricDescription = metricStreamConfig?.Description ?? instrument.Description;
|
||||
string[] tagKeysInteresting = metricStreamConfig?.TagKeys;
|
||||
double[] histogramBucketBounds = (metricStreamConfig is ExplicitBucketHistogramConfiguration histogramConfig
|
||||
&& histogramConfig.Boundaries != null) ? histogramConfig.Boundaries : null;
|
||||
metric = new Metric(instrument, this.Temporality, metricName, metricDescription, this.maxMetricPointsPerMetricStream, histogramBucketBounds, tagKeysInteresting);
|
||||
metric = new Metric(instrumentIdentity, this.Temporality, this.maxMetricPointsPerMetricStream, histogramBucketBounds, tagKeysInteresting);
|
||||
|
||||
this.instrumentIdentityToMetric[instrumentIdentity] = metric;
|
||||
this.metrics[index] = metric;
|
||||
metrics.Add(metric);
|
||||
this.metricStreamNames.Add(metricStreamName);
|
||||
|
|
@ -215,6 +240,7 @@ namespace OpenTelemetry.Metrics
|
|||
if (metric.InstrumentDisposed)
|
||||
{
|
||||
metricPointSize = metric.Snapshot();
|
||||
this.instrumentIdentityToMetric.TryRemove(metric.InstrumentIdentity, out var _);
|
||||
this.metrics[i] = null;
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -144,43 +144,29 @@ namespace OpenTelemetry.Metrics.Tests
|
|||
Assert.Equal(description ?? string.Empty, metric.Description);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(AggregationTemporality.Cumulative, true)]
|
||||
[InlineData(AggregationTemporality.Cumulative, false)]
|
||||
[InlineData(AggregationTemporality.Delta, true)]
|
||||
[InlineData(AggregationTemporality.Delta, false)]
|
||||
public void DuplicateInstrumentNamesFromSameMeterAreNotAllowed(AggregationTemporality temporality, bool hasView)
|
||||
[Fact]
|
||||
public void DuplicateInstrumentRegistration_NoViews_IdenticalInstruments()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}.{temporality}");
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddInMemoryExporter(exportedItems, metricReaderOptions =>
|
||||
{
|
||||
metricReaderOptions.Temporality = temporality;
|
||||
});
|
||||
|
||||
if (hasView)
|
||||
{
|
||||
meterProviderBuilder.AddView("name1", new MetricStreamConfiguration() { Description = "description" });
|
||||
}
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var counterLong = meter.CreateCounter<long>("name1");
|
||||
var anotherCounterSameName = meter.CreateCounter<long>("name1");
|
||||
var instrument = meter.CreateCounter<long>("instrumentName", "instrumentUnit", "instrumentDescription");
|
||||
var duplicateInstrument = meter.CreateCounter<long>("instrumentName", "instrumentUnit", "instrumentDescription");
|
||||
|
||||
counterLong.Add(10);
|
||||
anotherCounterSameName.Add(20);
|
||||
counterLong.Add(10);
|
||||
anotherCounterSameName.Add(20);
|
||||
instrument.Add(10);
|
||||
duplicateInstrument.Add(20);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
Assert.Single(exportedItems);
|
||||
|
||||
var metric = exportedItems[0];
|
||||
Assert.Equal("name1", metric.Name);
|
||||
Assert.Equal("instrumentName", metric.Name);
|
||||
List<MetricPoint> metricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric.GetMetricPoints())
|
||||
{
|
||||
|
|
@ -189,7 +175,325 @@ namespace OpenTelemetry.Metrics.Tests
|
|||
|
||||
Assert.Single(metricPoints);
|
||||
var metricPoint1 = metricPoints[0];
|
||||
Assert.Equal(20, metricPoint1.GetSumLong());
|
||||
Assert.Equal(30, metricPoint1.GetSumLong());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateInstrumentRegistration_NoViews_DuplicateInstruments_DifferentDescription()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument = meter.CreateCounter<long>("instrumentName", "instrumentUnit", "instrumentDescription1");
|
||||
var duplicateInstrument = meter.CreateCounter<long>("instrumentName", "instrumentUnit", "instrumentDescription2");
|
||||
|
||||
instrument.Add(10);
|
||||
duplicateInstrument.Add(20);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
|
||||
var metric1 = exportedItems[0];
|
||||
var metric2 = exportedItems[1];
|
||||
Assert.Equal("instrumentDescription1", metric1.Description);
|
||||
Assert.Equal("instrumentDescription2", metric2.Description);
|
||||
|
||||
List<MetricPoint> metric1MetricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric1.GetMetricPoints())
|
||||
{
|
||||
metric1MetricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metric1MetricPoints);
|
||||
var metricPoint1 = metric1MetricPoints[0];
|
||||
Assert.Equal(10, metricPoint1.GetSumLong());
|
||||
|
||||
List<MetricPoint> metric2MetricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric2.GetMetricPoints())
|
||||
{
|
||||
metric2MetricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metric2MetricPoints);
|
||||
var metricPoint2 = metric2MetricPoints[0];
|
||||
Assert.Equal(20, metricPoint2.GetSumLong());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateInstrumentRegistration_NoViews_DuplicateInstruments_DifferentUnit()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument = meter.CreateCounter<long>("instrumentName", "instrumentUnit1", "instrumentDescription");
|
||||
var duplicateInstrument = meter.CreateCounter<long>("instrumentName", "instrumentUnit2", "instrumentDescription");
|
||||
|
||||
instrument.Add(10);
|
||||
duplicateInstrument.Add(20);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
|
||||
var metric1 = exportedItems[0];
|
||||
var metric2 = exportedItems[1];
|
||||
Assert.Equal("instrumentUnit1", metric1.Unit);
|
||||
Assert.Equal("instrumentUnit2", metric2.Unit);
|
||||
|
||||
List<MetricPoint> metric1MetricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric1.GetMetricPoints())
|
||||
{
|
||||
metric1MetricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metric1MetricPoints);
|
||||
var metricPoint1 = metric1MetricPoints[0];
|
||||
Assert.Equal(10, metricPoint1.GetSumLong());
|
||||
|
||||
List<MetricPoint> metric2MetricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric2.GetMetricPoints())
|
||||
{
|
||||
metric2MetricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metric2MetricPoints);
|
||||
var metricPoint2 = metric2MetricPoints[0];
|
||||
Assert.Equal(20, metricPoint2.GetSumLong());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateInstrumentRegistration_NoViews_DuplicateInstruments_DifferentDataType()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument = meter.CreateCounter<long>("instrumentName", "instrumentUnit", "instrumentDescription");
|
||||
var duplicateInstrument = meter.CreateCounter<double>("instrumentName", "instrumentUnit", "instrumentDescription");
|
||||
|
||||
instrument.Add(10);
|
||||
duplicateInstrument.Add(20);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
|
||||
var metric1 = exportedItems[0];
|
||||
var metric2 = exportedItems[1];
|
||||
|
||||
List<MetricPoint> metric1MetricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric1.GetMetricPoints())
|
||||
{
|
||||
metric1MetricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metric1MetricPoints);
|
||||
var metricPoint1 = metric1MetricPoints[0];
|
||||
Assert.Equal(10, metricPoint1.GetSumLong());
|
||||
|
||||
List<MetricPoint> metric2MetricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric2.GetMetricPoints())
|
||||
{
|
||||
metric2MetricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metric2MetricPoints);
|
||||
var metricPoint2 = metric2MetricPoints[0];
|
||||
Assert.Equal(20D, metricPoint2.GetSumDouble());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateInstrumentRegistration_NoViews_DuplicateInstruments_DifferentInstrumentType()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument = meter.CreateCounter<long>("instrumentName", "instrumentUnit", "instrumentDescription");
|
||||
var duplicateInstrument = meter.CreateHistogram<long>("instrumentName", "instrumentUnit", "instrumentDescription");
|
||||
|
||||
instrument.Add(10);
|
||||
duplicateInstrument.Record(20);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
|
||||
var metric1 = exportedItems[0];
|
||||
var metric2 = exportedItems[1];
|
||||
|
||||
List<MetricPoint> metric1MetricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric1.GetMetricPoints())
|
||||
{
|
||||
metric1MetricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metric1MetricPoints);
|
||||
var metricPoint1 = metric1MetricPoints[0];
|
||||
Assert.Equal(10, metricPoint1.GetSumLong());
|
||||
|
||||
List<MetricPoint> metric2MetricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric2.GetMetricPoints())
|
||||
{
|
||||
metric2MetricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metric2MetricPoints);
|
||||
var metricPoint2 = metric2MetricPoints[0];
|
||||
Assert.Equal(1, metricPoint2.GetHistogramCount());
|
||||
Assert.Equal(20D, metricPoint2.GetHistogramSum());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateInstrumentRegistration_WithViews_DuplicateInstruments_DifferentDescription()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddView("instrumentName", new MetricStreamConfiguration { Description = "newDescription1" })
|
||||
.AddView("instrumentName", new MetricStreamConfiguration { Description = "newDescription2" })
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument = meter.CreateCounter<long>("instrumentName", "instrumentUnit", "instrumentDescription");
|
||||
|
||||
instrument.Add(10);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
|
||||
var metric1 = exportedItems[0];
|
||||
var metric2 = exportedItems[1];
|
||||
Assert.Equal("newDescription1", metric1.Description);
|
||||
Assert.Equal("newDescription2", metric2.Description);
|
||||
|
||||
List<MetricPoint> metric1MetricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric1.GetMetricPoints())
|
||||
{
|
||||
metric1MetricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metric1MetricPoints);
|
||||
var metricPoint1 = metric1MetricPoints[0];
|
||||
Assert.Equal(10, metricPoint1.GetSumLong());
|
||||
|
||||
List<MetricPoint> metric2MetricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric2.GetMetricPoints())
|
||||
{
|
||||
metric2MetricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metric2MetricPoints);
|
||||
var metricPoint2 = metric2MetricPoints[0];
|
||||
Assert.Equal(10, metricPoint2.GetSumLong());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateInstrumentRegistration_WithViews_TwoInstruments_ThreeStreams()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return new MetricStreamConfiguration { Name = "MetricStreamA", Description = "description" };
|
||||
})
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return instrument.Description == "description1"
|
||||
? new MetricStreamConfiguration { Name = "MetricStreamB" }
|
||||
: new MetricStreamConfiguration { Name = "MetricStreamC" };
|
||||
})
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument1 = meter.CreateCounter<long>("name", "unit", "description1");
|
||||
var instrument2 = meter.CreateCounter<long>("name", "unit", "description2");
|
||||
|
||||
instrument1.Add(10);
|
||||
instrument2.Add(10);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
Assert.Equal(3, exportedItems.Count);
|
||||
|
||||
var metricA = exportedItems[0];
|
||||
var metricB = exportedItems[1];
|
||||
var metricC = exportedItems[2];
|
||||
|
||||
Assert.Equal("MetricStreamA", metricA.Name);
|
||||
Assert.Equal(20, GetAggregatedValue(metricA));
|
||||
|
||||
Assert.Equal("MetricStreamB", metricB.Name);
|
||||
Assert.Equal(10, GetAggregatedValue(metricB));
|
||||
|
||||
Assert.Equal("MetricStreamC", metricC.Name);
|
||||
Assert.Equal(10, GetAggregatedValue(metricC));
|
||||
|
||||
long GetAggregatedValue(Metric metric)
|
||||
{
|
||||
var metricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric.GetMetricPoints())
|
||||
{
|
||||
metricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metricPoints);
|
||||
return metricPoints[0].GetSumLong();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateInstrumentNamesFromDifferentMetersWithSameNameDifferentVersion()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter1 = new Meter($"{Utils.GetCurrentMethodName()}", "1.0");
|
||||
using var meter2 = new Meter($"{Utils.GetCurrentMethodName()}", "2.0");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter1.Name)
|
||||
.AddMeter(meter2.Name)
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
// Expecting one metric stream.
|
||||
var counterLong = meter1.CreateCounter<long>("name1");
|
||||
counterLong.Add(10);
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
Assert.Single(exportedItems);
|
||||
|
||||
// Expeecting another metric stream since the meter differs by version
|
||||
var anotherCounterSameNameDiffMeter = meter2.CreateCounter<long>("name1");
|
||||
anotherCounterSameNameDiffMeter.Add(10);
|
||||
counterLong.Add(10);
|
||||
exportedItems.Clear();
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
|
|||
Loading…
Reference in New Issue