Skip to content

Commit 7ae7b13

Browse files
dulikvorcijothomasKielekCodeBlanch
authored
[sdk-metrics] Include Meter.Tags in metric identity resolution (open-telemetry#5982)
Co-authored-by: Cijo Thomas <[email protected]> Co-authored-by: Piotr Kiełkowicz <[email protected]> Co-authored-by: Mikel Blanchard <[email protected]>
1 parent 408a52d commit 7ae7b13

File tree

4 files changed

+37
-20
lines changed

4 files changed

+37
-20
lines changed

src/OpenTelemetry/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ Notes](../../RELEASENOTES.md).
66

77
## Unreleased
88

9+
* [Meter.Tags](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.metrics.meter.tags?view=net-9.0)
10+
will now be considered when resolving the SDK metric to update when
11+
measurements are recorded. Meters with the same name and different tags will
12+
now lead to unique metrics.
13+
([#5982](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5982))
14+
915
## 1.11.0-rc.1
1016

1117
Released 2024-Dec-11

src/OpenTelemetry/Metrics/Metric.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ internal Metric(
234234
/// <summary>
235235
/// Gets the attributes (tags) for the metric stream.
236236
/// </summary>
237-
public IEnumerable<KeyValuePair<string, object?>>? MeterTags => this.InstrumentIdentity.MeterTags;
237+
public IEnumerable<KeyValuePair<string, object?>>? MeterTags => this.InstrumentIdentity.MeterTags?.KeyValuePairs;
238238

239239
/// <summary>
240240
/// Gets the <see cref="MetricStreamIdentity"/> for the metric stream.

src/OpenTelemetry/Metrics/MetricStreamIdentity.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public MetricStreamIdentity(Instrument instrument, MetricStreamConfiguration? me
1414
{
1515
this.MeterName = instrument.Meter.Name;
1616
this.MeterVersion = instrument.Meter.Version ?? string.Empty;
17-
this.MeterTags = instrument.Meter.Tags;
17+
this.MeterTags = instrument.Meter.Tags != null ? new Tags(instrument.Meter.Tags.ToArray()) : null;
1818
this.InstrumentName = metricStreamConfiguration?.Name ?? instrument.Name;
1919
this.Unit = instrument.Unit ?? string.Empty;
2020
this.Description = metricStreamConfiguration?.Description ?? instrument.Description ?? string.Empty;
@@ -32,6 +32,7 @@ public MetricStreamIdentity(Instrument instrument, MetricStreamConfiguration? me
3232
hashCode.Add(this.InstrumentType);
3333
hashCode.Add(this.MeterName);
3434
hashCode.Add(this.MeterVersion);
35+
hashCode.Add(this.MeterTags);
3536
hashCode.Add(this.InstrumentName);
3637
hashCode.Add(this.HistogramRecordMinMax);
3738
hashCode.Add(this.Unit);
@@ -63,8 +64,7 @@ public MetricStreamIdentity(Instrument instrument, MetricStreamConfiguration? me
6364
hash = (hash * 31) + this.InstrumentType.GetHashCode();
6465
hash = (hash * 31) + this.MeterName.GetHashCode();
6566
hash = (hash * 31) + this.MeterVersion.GetHashCode();
66-
67-
// MeterTags is not part of identity, so not included here.
67+
hash = (hash * 31) + this.MeterTags?.GetHashCode() ?? 0;
6868
hash = (hash * 31) + this.InstrumentName.GetHashCode();
6969
hash = (hash * 31) + this.HistogramRecordMinMax.GetHashCode();
7070
hash = (hash * 31) + this.ExponentialHistogramMaxSize.GetHashCode();
@@ -91,7 +91,7 @@ public MetricStreamIdentity(Instrument instrument, MetricStreamConfiguration? me
9191

9292
public string MeterVersion { get; }
9393

94-
public IEnumerable<KeyValuePair<string, object?>>? MeterTags { get; }
94+
public Tags? MeterTags { get; }
9595

9696
public string InstrumentName { get; }
9797

@@ -141,6 +141,7 @@ public bool Equals(MetricStreamIdentity other)
141141
&& this.Unit == other.Unit
142142
&& this.Description == other.Description
143143
&& this.ViewId == other.ViewId
144+
&& this.MeterTags == other.MeterTags
144145
&& this.HistogramRecordMinMax == other.HistogramRecordMinMax
145146
&& this.ExponentialHistogramMaxSize == other.ExponentialHistogramMaxSize
146147
&& this.ExponentialHistogramMaxScale == other.ExponentialHistogramMaxScale

test/OpenTelemetry.Tests/Metrics/MetricApiTests.cs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,10 @@ public void MetricInstrumentationScopeIsExportedCorrectly()
192192
}
193193

194194
[Fact]
195-
public void MetricInstrumentationScopeAttributesAreNotTreatedAsIdentifyingProperty()
195+
public void MetricInstrumentationScopeAttributesAreTreatedAsIdentifyingProperty()
196196
{
197197
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#get-a-meter
198-
// Meters are identified by name, version, and schema_url fields
199-
// and not with tags.
198+
// Meters are identified by name, version, meter tags and schema_url fields.
200199
var exportedItems = new List<Metric>();
201200
var meterName = "MyMeter";
202201
var meterVersion = "1.0";
@@ -224,19 +223,16 @@ public void MetricInstrumentationScopeAttributesAreNotTreatedAsIdentifyingProper
224223
counter2.Add(15);
225224
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
226225

227-
// The instruments differ only in the Meter.Tags, which is not an identifying property.
228-
// The first instrument's Meter.Tags is exported.
229-
// It is considered a user-error to create Meters with same name,version but with
230-
// different tags. TODO: See if we can emit an internal log about this.
231-
Assert.Single(exportedItems);
232-
var metric = exportedItems[0];
233-
Assert.Equal(meterName, metric.MeterName);
234-
Assert.Equal(meterVersion, metric.MeterVersion);
226+
Assert.Equal(2, exportedItems.Count);
235227

236-
Assert.NotNull(metric.MeterTags);
228+
bool TagComparator(KeyValuePair<string, object?> lhs, KeyValuePair<string, object?> rhs)
229+
{
230+
return lhs.Key.Equals(rhs.Key) && lhs.Value!.GetHashCode().Equals(rhs.Value!.GetHashCode());
231+
}
237232

238-
Assert.Single(metric.MeterTags.Where(kvp => kvp.Key == meterTags1[0].Key && kvp.Value == meterTags1[0].Value));
239-
Assert.DoesNotContain(metric.MeterTags, kvp => kvp.Key == meterTags2[0].Key && kvp.Value == meterTags2[0].Value);
233+
var metric = exportedItems.First(m => TagComparator(m.MeterTags!.First(), meterTags1!.First()));
234+
Assert.Equal(meterName, metric.MeterName);
235+
Assert.Equal(meterVersion, metric.MeterVersion);
240236

241237
List<MetricPoint> metricPoints = new List<MetricPoint>();
242238
foreach (ref readonly var mp in metric.GetMetricPoints())
@@ -246,7 +242,21 @@ public void MetricInstrumentationScopeAttributesAreNotTreatedAsIdentifyingProper
246242

247243
Assert.Single(metricPoints);
248244
var metricPoint1 = metricPoints[0];
249-
Assert.Equal(25, metricPoint1.GetSumLong());
245+
Assert.Equal(10, metricPoint1.GetSumLong());
246+
247+
metric = exportedItems.First(m => TagComparator(m.MeterTags!.First(), meterTags2!.First()));
248+
Assert.Equal(meterName, metric.MeterName);
249+
Assert.Equal(meterVersion, metric.MeterVersion);
250+
251+
metricPoints = new List<MetricPoint>();
252+
foreach (ref readonly var mp in metric.GetMetricPoints())
253+
{
254+
metricPoints.Add(mp);
255+
}
256+
257+
Assert.Single(metricPoints);
258+
metricPoint1 = metricPoints[0];
259+
Assert.Equal(15, metricPoint1.GetSumLong());
250260
}
251261

252262
[Fact]

0 commit comments

Comments
 (0)