Refactor Aggregator and Export data structure (#2214)

This commit is contained in:
Cijo Thomas 2021-08-02 09:15:18 -07:00 committed by GitHub
parent d6d815e6a8
commit bd69bd6d29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 635 additions and 294 deletions

View File

@ -56,44 +56,53 @@ namespace OpenTelemetry.Exporter
var tags = metric.Attributes.ToArray().Select(k => $"{k.Key}={k.Value?.ToString()}");
string valueDisplay = string.Empty;
if (metric is ISumMetric sumMetric)
// Switch would be faster than the if.else ladder
// of try and cast.
switch (metric.MetricType)
{
if (sumMetric.Sum.Value is double doubleSum)
case MetricType.LongSum:
{
valueDisplay = ((double)doubleSum).ToString(CultureInfo.InvariantCulture);
}
else if (sumMetric.Sum.Value is long longSum)
{
valueDisplay = ((long)longSum).ToString();
}
}
else if (metric is IGaugeMetric gaugeMetric)
{
if (gaugeMetric.LastValue.Value is double doubleValue)
{
valueDisplay = ((double)doubleValue).ToString();
}
else if (gaugeMetric.LastValue.Value is long longValue)
{
valueDisplay = ((long)longValue).ToString();
valueDisplay = (metric as ISumMetricLong).LongSum.ToString(CultureInfo.InvariantCulture);
break;
}
// Qn: tags again ? gaugeMetric.LastValue.Tags
}
else if (metric is ISummaryMetric summaryMetric)
case MetricType.DoubleSum:
{
valueDisplay = string.Format("Sum: {0} Count: {1}", summaryMetric.PopulationSum, summaryMetric.PopulationCount);
valueDisplay = (metric as ISumMetricDouble).DoubleSum.ToString(CultureInfo.InvariantCulture);
break;
}
else if (metric is IHistogramMetric histogramMetric)
case MetricType.LongGauge:
{
// TODOs
break;
}
case MetricType.DoubleGauge:
{
// TODOs
break;
}
case MetricType.Histogram:
{
var histogramMetric = metric as IHistogramMetric;
valueDisplay = string.Format("Sum: {0} Count: {1}", histogramMetric.PopulationSum, histogramMetric.PopulationCount);
break;
}
var kind = metric.GetType().Name;
case MetricType.Summary:
{
var summaryMetric = metric as ISummaryMetric;
valueDisplay = string.Format("Sum: {0} Count: {1}", summaryMetric.PopulationSum, summaryMetric.PopulationCount);
break;
}
}
string time = $"{metric.StartTimeExclusive.ToLocalTime().ToString("HH:mm:ss.fff")} {metric.EndTimeInclusive.ToLocalTime().ToString("HH:mm:ss.fff")}";
var msg = new StringBuilder($"Export {time} {metric.Name} [{string.Join(";", tags)}] {kind} Value: {valueDisplay}");
var msg = new StringBuilder($"Export {time} {metric.Name} [{string.Join(";", tags)}] {metric.MetricType} Value: {valueDisplay}");
if (!string.IsNullOrEmpty(metric.Description))
{

View File

@ -132,8 +132,11 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
otlpMetric.Unit = metric.Unit;
}
if (metric is ISumMetric sumMetric)
switch (metric.MetricType)
{
case MetricType.LongSum:
{
var sumMetric = metric as ISumMetricLong;
var sum = new OtlpMetrics.Sum
{
IsMonotonic = sumMetric.IsMonotonic,
@ -141,49 +144,51 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
? OtlpMetrics.AggregationTemporality.Delta
: OtlpMetrics.AggregationTemporality.Cumulative,
};
var dataPoint = metric.ToNumberDataPoint(sumMetric.Sum.Value, sumMetric.Exemplars);
var dataPoint = metric.ToNumberDataPoint(sumMetric.LongSum, sumMetric.Exemplars);
sum.DataPoints.Add(dataPoint);
otlpMetric.Sum = sum;
break;
}
else if (metric is IGaugeMetric gaugeMetric)
case MetricType.DoubleSum:
{
var sumMetric = metric as ISumMetricDouble;
var sum = new OtlpMetrics.Sum
{
IsMonotonic = sumMetric.IsMonotonic,
AggregationTemporality = sumMetric.IsDeltaTemporality
? OtlpMetrics.AggregationTemporality.Delta
: OtlpMetrics.AggregationTemporality.Cumulative,
};
var dataPoint = metric.ToNumberDataPoint(sumMetric.DoubleSum, sumMetric.Exemplars);
sum.DataPoints.Add(dataPoint);
otlpMetric.Sum = sum;
break;
}
case MetricType.LongGauge:
{
var gaugeMetric = metric as IGaugeMetric;
var gauge = new OtlpMetrics.Gauge();
var dataPoint = metric.ToNumberDataPoint(gaugeMetric.LastValue.Value, gaugeMetric.Exemplars);
gauge.DataPoints.Add(dataPoint);
otlpMetric.Gauge = gauge;
}
else if (metric is ISummaryMetric summaryMetric)
{
var summary = new OtlpMetrics.Summary();
var dataPoint = new OtlpMetrics.SummaryDataPoint
{
StartTimeUnixNano = (ulong)metric.StartTimeExclusive.ToUnixTimeNanoseconds(),
TimeUnixNano = (ulong)metric.EndTimeInclusive.ToUnixTimeNanoseconds(),
Count = (ulong)summaryMetric.PopulationCount,
Sum = summaryMetric.PopulationSum,
};
// TODO: Do TagEnumerationState thing.
foreach (var attribute in metric.Attributes)
{
dataPoint.Attributes.Add(attribute.ToOtlpAttribute());
break;
}
foreach (var quantile in summaryMetric.Quantiles)
case MetricType.DoubleGauge:
{
var quantileValue = new OtlpMetrics.SummaryDataPoint.Types.ValueAtQuantile
{
Quantile = quantile.Quantile,
Value = quantile.Value,
};
dataPoint.QuantileValues.Add(quantileValue);
var gaugeMetric = metric as IGaugeMetric;
var gauge = new OtlpMetrics.Gauge();
var dataPoint = metric.ToNumberDataPoint(gaugeMetric.LastValue.Value, gaugeMetric.Exemplars);
gauge.DataPoints.Add(dataPoint);
otlpMetric.Gauge = gauge;
break;
}
otlpMetric.Summary = summary;
}
else if (metric is IHistogramMetric histogramMetric)
case MetricType.Histogram:
{
var histogramMetric = metric as IHistogramMetric;
var histogram = new OtlpMetrics.Histogram
{
AggregationTemporality = histogramMetric.IsDeltaTemporality
@ -221,6 +226,41 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
}
otlpMetric.Histogram = histogram;
break;
}
case MetricType.Summary:
{
var summaryMetric = metric as ISummaryMetric;
var summary = new OtlpMetrics.Summary();
var dataPoint = new OtlpMetrics.SummaryDataPoint
{
StartTimeUnixNano = (ulong)metric.StartTimeExclusive.ToUnixTimeNanoseconds(),
TimeUnixNano = (ulong)metric.EndTimeInclusive.ToUnixTimeNanoseconds(),
Count = (ulong)summaryMetric.PopulationCount,
Sum = summaryMetric.PopulationSum,
};
// TODO: Do TagEnumerationState thing.
foreach (var attribute in metric.Attributes)
{
dataPoint.Attributes.Add(attribute.ToOtlpAttribute());
}
foreach (var quantile in summaryMetric.Quantiles)
{
var quantileValue = new OtlpMetrics.SummaryDataPoint.Types.ValueAtQuantile
{
Quantile = quantile.Quantile,
Value = quantile.Value,
};
dataPoint.QuantileValues.Add(quantileValue);
}
otlpMetric.Summary = summary;
break;
}
}
return otlpMetric;

View File

@ -50,16 +50,14 @@ namespace OpenTelemetry.Exporter
.WithName(metric.Name)
.WithDescription(metric.Name);
if (metric is ISumMetric sumMetric)
// TODO: Use switch case for higher perf.
if (metric.MetricType == MetricType.LongSum)
{
if (sumMetric.Sum.Value is double doubleSum)
{
WriteSum(writer, builder, metric.Attributes, doubleSum);
WriteSum(writer, builder, metric.Attributes, (metric as ISumMetricLong).LongSum);
}
else if (sumMetric.Sum.Value is long longSum)
else if (metric.MetricType == MetricType.DoubleSum)
{
WriteSum(writer, builder, metric.Attributes, longSum);
}
WriteSum(writer, builder, metric.Attributes, (metric as ISumMetricDouble).DoubleSum);
}
}
}

View File

@ -51,9 +51,20 @@ namespace OpenTelemetry.Metrics
var dt = DateTimeOffset.UtcNow;
// TODO: Need to map each instrument to metrics (based on View API)
if (this.instrument.GetType().Name.Contains("Counter"))
// TODO: move most of this logic out of hotpath, and to MeterProvider's
// InstrumentPublished event, which is once per instrument creation.
if (this.instrument.GetType() == typeof(Counter<long>)
|| this.instrument.GetType() == typeof(Counter<int>)
|| this.instrument.GetType() == typeof(Counter<short>)
|| this.instrument.GetType() == typeof(Counter<byte>))
{
aggregators.Add(new SumMetricAggregator(this.instrument.Name, this.instrument.Description, this.instrument.Unit, this.instrument.Meter, dt, tags));
aggregators.Add(new SumMetricAggregatorLong(this.instrument.Name, this.instrument.Description, this.instrument.Unit, this.instrument.Meter, dt, tags));
}
else if (this.instrument.GetType() == typeof(Counter<double>)
|| this.instrument.GetType() == typeof(Counter<float>))
{
aggregators.Add(new SumMetricAggregatorDouble(this.instrument.Name, this.instrument.Description, this.instrument.Unit, this.instrument.Meter, dt, tags));
}
else if (this.instrument.GetType().Name.Contains("Gauge"))
{

View File

@ -33,6 +33,9 @@ namespace OpenTelemetry.Metrics
this.Meter = meter;
this.StartTimeExclusive = startTimeExclusive;
this.Attributes = attributes;
// TODO: Split this class into two or leverage generic
this.MetricType = MetricType.LongGauge;
}
public string Name { get; private set; }
@ -53,6 +56,8 @@ namespace OpenTelemetry.Metrics
public IDataValue LastValue => this.value;
public MetricType MetricType { get; private set; }
public void Update<T>(T value)
where T : struct
{

View File

@ -50,6 +50,7 @@ namespace OpenTelemetry.Metrics
this.boundaries = boundaries;
this.buckets = this.InitializeBucket(boundaries);
this.MetricType = MetricType.Summary;
}
public string Name { get; private set; }
@ -74,6 +75,8 @@ namespace OpenTelemetry.Metrics
public double PopulationSum { get; private set; }
public MetricType MetricType { get; private set; }
public IEnumerable<HistogramBucket> Buckets => this.buckets;
public void Update<T>(T value)

View File

@ -36,6 +36,8 @@ namespace OpenTelemetry.Metrics
KeyValuePair<string, object>[] Attributes { get; }
MetricType MetricType { get; }
string ToDisplayString();
}
}

View File

@ -25,7 +25,5 @@ namespace OpenTelemetry.Metrics
bool IsMonotonic { get; }
IEnumerable<IExemplar> Exemplars { get; }
IDataValue Sum { get; }
}
}

View File

@ -0,0 +1,23 @@
// <copyright file="ISumMetricDouble.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>
namespace OpenTelemetry.Metrics
{
public interface ISumMetricDouble : ISumMetric
{
double DoubleSum { get; }
}
}

View File

@ -0,0 +1,23 @@
// <copyright file="ISumMetricLong.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>
namespace OpenTelemetry.Metrics
{
public interface ISumMetricLong : ISumMetric
{
long LongSum { get; }
}
}

View File

@ -0,0 +1,51 @@
// <copyright file="MetricType.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>
namespace OpenTelemetry.Metrics
{
public enum MetricType
{
/// <summary>
/// Sum of Long type.
/// </summary>
LongSum = 0,
/// <summary>
/// Sum of Double type.
/// </summary>
DoubleSum = 1,
/// <summary>
/// Gauge of Long type.
/// </summary>
LongGauge = 2,
/// <summary>
/// Gauge of Double type.
/// </summary>
DoubleGauge = 3,
/// <summary>
/// Histogram.
/// </summary>
Histogram = 4,
/// <summary>
/// Summary.
/// </summary>
Summary = 5,
}
}

View File

@ -1,147 +0,0 @@
// <copyright file="SumMetricAggregator.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.Collections.Generic;
using System.Diagnostics.Metrics;
namespace OpenTelemetry.Metrics
{
internal class SumMetricAggregator : ISumMetric, IAggregator
{
private readonly object lockUpdate = new object();
private Type valueType;
private long sumLong = 0;
private double sumDouble = 0;
internal SumMetricAggregator(string name, string description, string unit, Meter meter, DateTimeOffset startTimeExclusive, KeyValuePair<string, object>[] attributes)
{
this.Name = name;
this.Description = description;
this.Unit = unit;
this.Meter = meter;
this.StartTimeExclusive = startTimeExclusive;
this.Attributes = attributes;
this.IsMonotonic = true;
}
public string Name { get; private set; }
public string Description { get; private set; }
public string Unit { get; private set; }
public Meter Meter { get; private set; }
public DateTimeOffset StartTimeExclusive { get; private set; }
public DateTimeOffset EndTimeInclusive { get; private set; }
public KeyValuePair<string, object>[] Attributes { get; private set; }
public bool IsDeltaTemporality { get; private set; }
public bool IsMonotonic { get; }
public IEnumerable<IExemplar> Exemplars { get; private set; } = new List<IExemplar>();
public IDataValue Sum
{
get
{
if (this.valueType == typeof(long))
{
return new DataValue(this.sumLong);
}
else if (this.valueType == typeof(double))
{
return new DataValue(this.sumDouble);
}
throw new Exception("Unsupported Type");
}
}
public void Update<T>(T value)
where T : struct
{
lock (this.lockUpdate)
{
if (typeof(T) == typeof(long))
{
this.valueType = typeof(T);
var val = (long)(object)value;
if (val < 0)
{
// TODO: log?
// Also, this validation can be done in earlier stage.
}
else
{
this.sumLong += val;
}
}
else if (typeof(T) == typeof(double))
{
this.valueType = typeof(T);
var val = (double)(object)value;
if (val < 0)
{
// TODO: log?
// Also, this validation can be done in earlier stage.
}
else
{
this.sumDouble += val;
}
}
else
{
throw new Exception("Unsupported Type");
}
}
}
public IMetric Collect(DateTimeOffset dt, bool isDelta)
{
var cloneItem = new SumMetricAggregator(this.Name, this.Description, this.Unit, this.Meter, this.StartTimeExclusive, this.Attributes);
lock (this.lockUpdate)
{
cloneItem.Exemplars = this.Exemplars;
cloneItem.EndTimeInclusive = dt;
cloneItem.valueType = this.valueType;
cloneItem.sumLong = this.sumLong;
cloneItem.sumDouble = this.sumDouble;
cloneItem.IsDeltaTemporality = isDelta;
if (isDelta)
{
this.StartTimeExclusive = dt;
this.sumLong = 0;
this.sumDouble = 0;
}
}
return cloneItem;
}
public string ToDisplayString()
{
return $"Sum={this.Sum.Value}";
}
}
}

View File

@ -0,0 +1,93 @@
// <copyright file="SumMetricAggregatorDouble.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.Collections.Generic;
using System.Diagnostics.Metrics;
namespace OpenTelemetry.Metrics
{
internal class SumMetricAggregatorDouble : IAggregator
{
private readonly object lockUpdate = new object();
private double sumDouble = 0;
private SumMetricDouble sumMetricDouble;
private DateTimeOffset startTimeExclusive;
internal SumMetricAggregatorDouble(string name, string description, string unit, Meter meter, DateTimeOffset startTimeExclusive, KeyValuePair<string, object>[] attributes)
{
this.startTimeExclusive = startTimeExclusive;
this.sumMetricDouble = new SumMetricDouble(name, description, unit, meter, startTimeExclusive, attributes);
}
public void Update<T>(T value)
where T : struct
{
// TODO: Replace Lock with
// TryAdd..{Spin..TryAdd..Repeat} if "lost race to another thread"
lock (this.lockUpdate)
{
if (typeof(T) == typeof(double))
{
// TODO: Confirm this doesn't cause boxing.
var val = (double)(object)value;
if (val < 0)
{
// TODO: log?
// Also, this validation can be done in earlier stage.
}
else
{
this.sumDouble += val;
}
}
else
{
throw new Exception("Unsupported Type");
}
}
}
public IMetric Collect(DateTimeOffset dt, bool isDelta)
{
lock (this.lockUpdate)
{
this.sumMetricDouble.StartTimeExclusive = this.startTimeExclusive;
this.sumMetricDouble.EndTimeInclusive = dt;
this.sumMetricDouble.DoubleSum = this.sumDouble;
this.sumMetricDouble.IsDeltaTemporality = isDelta;
if (isDelta)
{
this.startTimeExclusive = dt;
this.sumDouble = 0;
}
}
// TODO: Confirm that this approach of
// re-using the same instance is correct.
// This avoids allocating a new instance.
// It is read only for Exporters,
// and also there is no parallel
// Collect allowed.
return this.sumMetricDouble;
}
public string ToDisplayString()
{
return $"Sum={this.sumDouble}";
}
}
}

View File

@ -0,0 +1,92 @@
// <copyright file="SumMetricAggregatorLong.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.Collections.Generic;
using System.Diagnostics.Metrics;
namespace OpenTelemetry.Metrics
{
internal class SumMetricAggregatorLong : IAggregator
{
private readonly object lockUpdate = new object();
private long sumLong = 0;
private SumMetricLong sumMetricLong;
private DateTimeOffset startTimeExclusive;
internal SumMetricAggregatorLong(string name, string description, string unit, Meter meter, DateTimeOffset startTimeExclusive, KeyValuePair<string, object>[] attributes)
{
this.startTimeExclusive = startTimeExclusive;
this.sumMetricLong = new SumMetricLong(name, description, unit, meter, startTimeExclusive, attributes);
}
public void Update<T>(T value)
where T : struct
{
// TODO: Replace Lock with Interlocked.Add
lock (this.lockUpdate)
{
if (typeof(T) == typeof(long))
{
// TODO: Confirm this doesn't cause boxing.
var val = (long)(object)value;
if (val < 0)
{
// TODO: log?
// Also, this validation can be done in earlier stage.
}
else
{
this.sumLong += val;
}
}
else
{
throw new Exception("Unsupported Type");
}
}
}
public IMetric Collect(DateTimeOffset dt, bool isDelta)
{
lock (this.lockUpdate)
{
this.sumMetricLong.StartTimeExclusive = this.startTimeExclusive;
this.sumMetricLong.EndTimeInclusive = dt;
this.sumMetricLong.LongSum = this.sumLong;
this.sumMetricLong.IsDeltaTemporality = isDelta;
if (isDelta)
{
this.startTimeExclusive = dt;
this.sumLong = 0;
}
}
// TODO: Confirm that this approach of
// re-using the same instance is correct.
// This avoids allocating a new instance.
// It is read only for Exporters,
// and also there is no parallel
// Collect allowed.
return this.sumMetricLong;
}
public string ToDisplayString()
{
return $"Sum={this.sumLong}";
}
}
}

View File

@ -0,0 +1,66 @@
// <copyright file="SumMetricDouble.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.Collections.Generic;
using System.Diagnostics.Metrics;
namespace OpenTelemetry.Metrics
{
internal class SumMetricDouble : ISumMetricDouble
{
internal SumMetricDouble(string name, string description, string unit, Meter meter, DateTimeOffset startTimeExclusive, KeyValuePair<string, object>[] attributes)
{
this.Name = name;
this.Description = description;
this.Unit = unit;
this.Meter = meter;
this.StartTimeExclusive = startTimeExclusive;
this.Attributes = attributes;
this.IsMonotonic = true;
this.MetricType = MetricType.DoubleSum;
}
public string Name { get; private set; }
public string Description { get; private set; }
public string Unit { get; private set; }
public Meter Meter { get; private set; }
public DateTimeOffset StartTimeExclusive { get; internal set; }
public DateTimeOffset EndTimeInclusive { get; internal set; }
public KeyValuePair<string, object>[] Attributes { get; private set; }
public bool IsDeltaTemporality { get; internal set; }
public bool IsMonotonic { get; }
public IEnumerable<IExemplar> Exemplars { get; private set; } = new List<IExemplar>();
public double DoubleSum { get; internal set; }
public MetricType MetricType { get; private set; }
public string ToDisplayString()
{
return $"Sum={this.DoubleSum}";
}
}
}

View File

@ -0,0 +1,66 @@
// <copyright file="SumMetricLong.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.Collections.Generic;
using System.Diagnostics.Metrics;
namespace OpenTelemetry.Metrics
{
internal class SumMetricLong : ISumMetricLong
{
internal SumMetricLong(string name, string description, string unit, Meter meter, DateTimeOffset startTimeExclusive, KeyValuePair<string, object>[] attributes)
{
this.Name = name;
this.Description = description;
this.Unit = unit;
this.Meter = meter;
this.StartTimeExclusive = startTimeExclusive;
this.Attributes = attributes;
this.IsMonotonic = true;
this.MetricType = MetricType.LongSum;
}
public string Name { get; private set; }
public string Description { get; private set; }
public string Unit { get; private set; }
public Meter Meter { get; private set; }
public DateTimeOffset StartTimeExclusive { get; internal set; }
public DateTimeOffset EndTimeInclusive { get; internal set; }
public KeyValuePair<string, object>[] Attributes { get; private set; }
public bool IsDeltaTemporality { get; internal set; }
public bool IsMonotonic { get; }
public IEnumerable<IExemplar> Exemplars { get; private set; } = new List<IExemplar>();
public long LongSum { get; internal set; }
public MetricType MetricType { get; private set; }
public string ToDisplayString()
{
return $"Sum={this.LongSum}";
}
}
}

View File

@ -35,6 +35,7 @@ namespace OpenTelemetry.Metrics
this.StartTimeExclusive = startTimeExclusive;
this.Attributes = attributes;
this.IsMonotonic = isMonotonic;
this.MetricType = MetricType.Summary;
}
public string Name { get; private set; }
@ -59,6 +60,8 @@ namespace OpenTelemetry.Metrics
public IEnumerable<ValueAtQuantile> Quantiles => this.quantiles;
public MetricType MetricType { get; private set; }
public void Update<T>(T value)
where T : struct
{

View File

@ -64,9 +64,10 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
}
}
var processor = new PullMetricProcessor(metricExporter, true);
this.meterProvider = Sdk.CreateMeterProviderBuilder()
.AddAspNetCoreInstrumentation()
.AddMetricProcessor(new PushMetricProcessor(exporter: metricExporter, exportIntervalMs: 10, isDelta: true))
.AddMetricProcessor(processor)
.Build();
using (var client = this.factory.CreateClient())
@ -75,22 +76,20 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
response.EnsureSuccessStatusCode();
}
// Wait for at least two exporter invocations
WaitForMetricItems(metricItems, 2);
// Invokes the TestExporter which will invoke ProcessExport
processor.PullRequest();
this.meterProvider.Dispose();
// There should be more than one result here since we waited for at least two exporter invocations.
// The exporter continues to receive a metric even if it has not changed since the last export.
var requestMetrics = metricItems
.SelectMany(item => item.Metrics.Where(metric => metric.Name == "http.server.request_count"))
.ToArray();
Assert.True(requestMetrics.Length > 1);
Assert.True(requestMetrics.Length == 1);
var metric = requestMetrics[0] as ISumMetric;
var metric = requestMetrics[0] as ISumMetricLong;
Assert.NotNull(metric);
Assert.Equal(1L, metric.Sum.Value);
Assert.Equal(1L, metric.LongSum);
var method = new KeyValuePair<string, object>(SemanticConventions.AttributeHttpMethod, "GET");
var scheme = new KeyValuePair<string, object>(SemanticConventions.AttributeHttpScheme, "http");
@ -101,13 +100,6 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
Assert.Contains(statusCode, metric.Attributes);
Assert.Contains(flavor, metric.Attributes);
Assert.Equal(4, metric.Attributes.Length);
for (var i = 1; i < requestMetrics.Length; ++i)
{
metric = requestMetrics[i] as ISumMetric;
Assert.NotNull(metric);
Assert.Equal(0L, metric.Sum.Value);
}
}
public void Dispose()

View File

@ -16,18 +16,26 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Metrics;
using System.Threading;
using OpenTelemetry.Tests;
using Xunit;
using Xunit.Abstractions;
namespace OpenTelemetry.Metrics.Tests
{
public class MetricApiTest
{
private static int numberOfThreads = 10;
private static int numberOfThreads = Environment.ProcessorCount;
private static long deltaValueUpdatedByEachCall = 10;
private static int numberOfMetricUpdateByEachThread = 1000000;
private static int numberOfMetricUpdateByEachThread = 100000;
private readonly ITestOutputHelper output;
public MetricApiTest(ITestOutputHelper output)
{
this.output = output;
}
[Fact]
public void SimpleTest()
@ -42,11 +50,13 @@ namespace OpenTelemetry.Metrics.Tests
}
}
var pullProcessor = new PullMetricProcessor(metricExporter, true);
var meter = new Meter("TestMeter");
var counterLong = meter.CreateCounter<long>("mycounter");
var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddSource("TestMeter")
.AddMetricProcessor(new PushMetricProcessor(metricExporter, 100, isDelta: true))
.AddMetricProcessor(pullProcessor)
.Build();
// setup args to threads.
@ -69,6 +79,9 @@ namespace OpenTelemetry.Metrics.Tests
// Block until all threads started.
mreToEnsureAllThreadsStarted.WaitOne();
Stopwatch sw = new Stopwatch();
sw.Start();
// unblock all the threads.
// (i.e let them start counter.Add)
mreToBlockUpdateThreads.Set();
@ -79,11 +92,11 @@ namespace OpenTelemetry.Metrics.Tests
t[i].Join();
}
meterProvider.Dispose();
var timeTakenInMilliseconds = sw.ElapsedMilliseconds;
this.output.WriteLine($"Took {timeTakenInMilliseconds} msecs. Total threads: {numberOfThreads}, each thread doing {numberOfMetricUpdateByEachThread} recordings.");
// TODO: Once Dispose does flush, we may not need this
// unknown sleep below.
Thread.Sleep(1000);
meterProvider.Dispose();
pullProcessor.PullRequest();
long sumReceived = 0;
foreach (var metricItem in metricItems)
@ -91,7 +104,7 @@ namespace OpenTelemetry.Metrics.Tests
var metrics = metricItem.Metrics;
foreach (var metric in metrics)
{
sumReceived += (long)(metric as ISumMetric).Sum.Value;
sumReceived += (metric as ISumMetricLong).LongSum;
}
}