Refactor Aggregator and Export data structure (#2214)
This commit is contained in:
parent
d6d815e6a8
commit
bd69bd6d29
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ namespace OpenTelemetry.Metrics
|
|||
|
||||
KeyValuePair<string, object>[] Attributes { get; }
|
||||
|
||||
MetricType MetricType { get; }
|
||||
|
||||
string ToDisplayString();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,5 @@ namespace OpenTelemetry.Metrics
|
|||
bool IsMonotonic { get; }
|
||||
|
||||
IEnumerable<IExemplar> Exemplars { get; }
|
||||
|
||||
IDataValue Sum { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue