Skip to content

Commit

Permalink
Allow duplicate instrument registration (#2916)
Browse files Browse the repository at this point in the history
  • Loading branch information
alanwest authored Mar 5, 2022
1 parent 62217ce commit ffffe5c
Show file tree
Hide file tree
Showing 10 changed files with 514 additions and 81 deletions.
8 changes: 4 additions & 4 deletions src/OpenTelemetry.Exporter.Console/ConsoleMetricExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,13 @@ public override ExportResult Export(in Batch<Metric> batch)
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}");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ internal static void AddMetrics(
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);
Expand Down
3 changes: 2 additions & 1 deletion src/OpenTelemetry/.publicApi/net461/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 18 additions & 3 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down
6 changes: 6 additions & 0 deletions src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,12 @@ public void ProviderDisposed(string providerName)
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
{
Expand Down
81 changes: 81 additions & 0 deletions src/OpenTelemetry/Metrics/InstrumentIdentity.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
73 changes: 36 additions & 37 deletions src/OpenTelemetry/Metrics/Metric.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,68 +30,63 @@ public sealed class Metric
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;

Expand All @@ -107,10 +102,10 @@ internal Metric(
}
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;
}
Expand All @@ -119,13 +114,17 @@ internal Metric(

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; }

Expand Down
Loading

0 comments on commit ffffe5c

Please sign in to comment.