From a69121ccd4c6323f2c6356be3efa43a83debd810 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Wed, 4 May 2022 15:35:13 -0700 Subject: [PATCH 01/58] Binary search for large bucket count histograms --- src/OpenTelemetry/CHANGELOG.md | 5 +- src/OpenTelemetry/Metrics/Metric.cs | 2 + src/OpenTelemetry/Metrics/MetricPoint.cs | 59 ++++++++++++++++--- .../Benchmarks/Metrics/HistogramBenchmarks.cs | 4 +- 4 files changed, 58 insertions(+), 12 deletions(-) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 2b3bd30138f..b13adfe4b1b 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -2,9 +2,12 @@ ## Unreleased +* Use binary search for histograms with a large amount of buckets. + ([#3251](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3251)) + * Exposed public setters for `LogRecord.State`, `LogRecord.StateValues`, and `LogRecord.FormattedMessage`. - ([#3217](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3217)) + ([#3217](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3217)) ## 1.3.0-beta.1 diff --git a/src/OpenTelemetry/Metrics/Metric.cs b/src/OpenTelemetry/Metrics/Metric.cs index 61443173f70..78c66787d6a 100644 --- a/src/OpenTelemetry/Metrics/Metric.cs +++ b/src/OpenTelemetry/Metrics/Metric.cs @@ -27,6 +27,8 @@ public sealed class Metric { internal static readonly double[] DefaultHistogramBounds = new double[] { 0, 5, 10, 25, 50, 75, 100, 250, 500, 1000 }; + internal static readonly int DefaultHistogramCountForBinarySearch = 140; + private readonly AggregatorStore aggStore; internal Metric( diff --git a/src/OpenTelemetry/Metrics/MetricPoint.cs b/src/OpenTelemetry/Metrics/MetricPoint.cs index f848c319594..53ab36d6505 100644 --- a/src/OpenTelemetry/Metrics/MetricPoint.cs +++ b/src/OpenTelemetry/Metrics/MetricPoint.cs @@ -30,6 +30,8 @@ public struct MetricPoint private readonly AggregationType aggType; + private readonly Func findHistogramBucketIndex; + private HistogramBuckets histogramBuckets; // Represents temporality adjusted "value" for double/long metric types or "count" when histogram @@ -61,6 +63,9 @@ internal MetricPoint( if (this.aggType == AggregationType.Histogram) { this.histogramBuckets = new HistogramBuckets(histogramExplicitBounds); + this.findHistogramBucketIndex = histogramExplicitBounds.Length >= Metric.DefaultHistogramCountForBinarySearch + ? FindHistogramBucketIndexBinary + : FindHistogramBucketIndexLinear; } else if (this.aggType == AggregationType.HistogramSumCount) { @@ -324,15 +329,9 @@ internal void Update(double number) case AggregationType.Histogram: { - int i; - for (i = 0; i < this.histogramBuckets.ExplicitBounds.Length; i++) - { - // Upper bound is inclusive - if (number <= this.histogramBuckets.ExplicitBounds[i]) - { - break; - } - } + int i = double.IsNaN(number) + ? this.histogramBuckets.ExplicitBounds.Length + : this.findHistogramBucketIndex(this.histogramBuckets.ExplicitBounds, number); var sw = default(SpinWait); while (true) @@ -545,5 +544,47 @@ private readonly void ThrowNotSupportedMetricTypeException(string methodName) { throw new NotSupportedException($"{methodName} is not supported for this metric type."); } + +#pragma warning disable SA1204 // Static elements should appear before instance elements + private static int FindHistogramBucketIndexLinear(double[] bounds, double number) +#pragma warning restore SA1204 // Static elements should appear before instance elements + { + int i; + + for (i = 0; i < bounds.Length; i++) + { + if (number <= bounds[i]) + { + break; + } + } + + return i; + } + + private static int FindHistogramBucketIndexBinary(double[] bounds, double number) + { + var left = 0; + var right = bounds.Length - 1; + + while (left <= right) + { + var mid = (int)Math.Floor((double)(left + right) / 2); + if (number == bounds[mid]) + { + return mid; + } + else if (number > bounds[mid]) + { + left = mid + 1; + } + else + { + right = mid - 1; + } + } + + return right + 1; + } } } diff --git a/test/Benchmarks/Metrics/HistogramBenchmarks.cs b/test/Benchmarks/Metrics/HistogramBenchmarks.cs index 5511b82a0eb..70fdc7362a7 100644 --- a/test/Benchmarks/Metrics/HistogramBenchmarks.cs +++ b/test/Benchmarks/Metrics/HistogramBenchmarks.cs @@ -59,7 +59,7 @@ namespace Benchmarks.Metrics { public class HistogramBenchmarks { - private const int MaxValue = 1000; + private const int MaxValue = 10000; private readonly Random random = new(); private readonly string[] dimensionValues = new string[] { "DimVal1", "DimVal2", "DimVal3", "DimVal4", "DimVal5", "DimVal6", "DimVal7", "DimVal8", "DimVal9", "DimVal10" }; private Histogram histogram; @@ -67,7 +67,7 @@ public class HistogramBenchmarks private Meter meter; private double[] bounds; - [Params(10, 20, 50, 100)] + [Params(10, 20, 50, 100, 200, 500, 1000)] public int BoundCount { get; set; } [GlobalSetup] From 6e446cdd45e9f4da73e398dbe9ab30b69cb303bc Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Wed, 4 May 2022 15:39:17 -0700 Subject: [PATCH 02/58] Update CHANGELOG.md --- src/OpenTelemetry/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index b13adfe4b1b..baf4717353e 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -3,7 +3,7 @@ ## Unreleased * Use binary search for histograms with a large amount of buckets. - ([#3251](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3251)) + ([#3252](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3252)) * Exposed public setters for `LogRecord.State`, `LogRecord.StateValues`, and `LogRecord.FormattedMessage`. From e420a15287b325b97895189bf5ba5b2062a7fd17 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Wed, 4 May 2022 15:49:27 -0700 Subject: [PATCH 03/58] netcoreapp3.1 was complaining --- src/OpenTelemetry/Metrics/MetricPoint.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/OpenTelemetry/Metrics/MetricPoint.cs b/src/OpenTelemetry/Metrics/MetricPoint.cs index 53ab36d6505..4a1f16c122f 100644 --- a/src/OpenTelemetry/Metrics/MetricPoint.cs +++ b/src/OpenTelemetry/Metrics/MetricPoint.cs @@ -30,7 +30,7 @@ public struct MetricPoint private readonly AggregationType aggType; - private readonly Func findHistogramBucketIndex; + private readonly Func findHistogramBucketIndex = FindHistogramBucketIndexLinear; private HistogramBuckets histogramBuckets; @@ -63,9 +63,10 @@ internal MetricPoint( if (this.aggType == AggregationType.Histogram) { this.histogramBuckets = new HistogramBuckets(histogramExplicitBounds); - this.findHistogramBucketIndex = histogramExplicitBounds.Length >= Metric.DefaultHistogramCountForBinarySearch - ? FindHistogramBucketIndexBinary - : FindHistogramBucketIndexLinear; + if (histogramExplicitBounds.Length >= Metric.DefaultHistogramCountForBinarySearch) + { + this.findHistogramBucketIndex = FindHistogramBucketIndexBinary; + } } else if (this.aggType == AggregationType.HistogramSumCount) { From e8913b242b14026c024daeb8b03db13ce257ef1c Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Wed, 4 May 2022 15:58:38 -0700 Subject: [PATCH 04/58] ci rerun From 4ade35a974d8ebfb0f409fa11d8a673290527bb7 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Wed, 4 May 2022 16:35:53 -0700 Subject: [PATCH 05/58] ci rerun From 52e2c43c708bcaa50167dbdd28f0c8ada252f1d3 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Wed, 4 May 2022 16:56:01 -0700 Subject: [PATCH 06/58] Update MetricTestData.cs --- test/OpenTelemetry.Tests/Metrics/MetricTestData.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs b/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs index 0664dc18c00..2e7052b773a 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs @@ -44,6 +44,7 @@ public static IEnumerable ValidInstrumentNames public static IEnumerable InvalidHistogramBoundaries => new List { + new object[] { new double[] { double.NaN } }, new object[] { new double[] { 0, 0 } }, new object[] { new double[] { 1, 0 } }, new object[] { new double[] { 0, 1, 1, 2 } }, From f7e88af9dcd0fc1ecaf23b6bada4567851a4cd7f Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Thu, 12 May 2022 11:50:30 -0700 Subject: [PATCH 07/58] use 400 buckets --- src/OpenTelemetry/Metrics/Metric.cs | 2 +- .../Benchmarks/Metrics/HistogramBenchmarks.cs | 52 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/OpenTelemetry/Metrics/Metric.cs b/src/OpenTelemetry/Metrics/Metric.cs index 78c66787d6a..961d0b06ed5 100644 --- a/src/OpenTelemetry/Metrics/Metric.cs +++ b/src/OpenTelemetry/Metrics/Metric.cs @@ -27,7 +27,7 @@ public sealed class Metric { internal static readonly double[] DefaultHistogramBounds = new double[] { 0, 5, 10, 25, 50, 75, 100, 250, 500, 1000 }; - internal static readonly int DefaultHistogramCountForBinarySearch = 140; + internal static readonly int DefaultHistogramCountForBinarySearch = 400; private readonly AggregatorStore aggStore; diff --git a/test/Benchmarks/Metrics/HistogramBenchmarks.cs b/test/Benchmarks/Metrics/HistogramBenchmarks.cs index 70fdc7362a7..dbca35b2924 100644 --- a/test/Benchmarks/Metrics/HistogramBenchmarks.cs +++ b/test/Benchmarks/Metrics/HistogramBenchmarks.cs @@ -24,35 +24,35 @@ using OpenTelemetry.Tests; /* -BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000 -Intel Core i7-9700 CPU 3.00GHz, 1 CPU, 8 logical and 8 physical cores -.NET SDK=6.0.200 - [Host] : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT - DefaultJob : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT +BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19044.1706 (21H2) +AMD Ryzen 9 3900X, 1 CPU, 24 logical and 12 physical cores +.NET SDK=6.0.203 + [Host] : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT + DefaultJob : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT | Method | BoundCount | Mean | Error | StdDev | Allocated | |---------------------------- |----------- |----------:|---------:|---------:|----------:| -| HistogramHotPath | 10 | 45.27 ns | 0.384 ns | 0.359 ns | - | -| HistogramWith1LabelHotPath | 10 | 89.99 ns | 0.373 ns | 0.312 ns | - | -| HistogramWith3LabelsHotPath | 10 | 185.34 ns | 3.184 ns | 3.667 ns | - | -| HistogramWith5LabelsHotPath | 10 | 266.69 ns | 1.391 ns | 1.301 ns | - | -| HistogramWith7LabelsHotPath | 10 | 323.20 ns | 1.834 ns | 1.531 ns | - | -| HistogramHotPath | 20 | 48.69 ns | 0.347 ns | 0.307 ns | - | -| HistogramWith1LabelHotPath | 20 | 93.84 ns | 0.696 ns | 0.651 ns | - | -| HistogramWith3LabelsHotPath | 20 | 189.82 ns | 1.208 ns | 1.071 ns | - | -| HistogramWith5LabelsHotPath | 20 | 269.23 ns | 2.027 ns | 1.693 ns | - | -| HistogramWith7LabelsHotPath | 20 | 329.92 ns | 1.272 ns | 1.128 ns | - | -| HistogramHotPath | 50 | 55.73 ns | 0.339 ns | 0.317 ns | - | -| HistogramWith1LabelHotPath | 50 | 100.38 ns | 0.455 ns | 0.425 ns | - | -| HistogramWith3LabelsHotPath | 50 | 200.02 ns | 1.011 ns | 0.844 ns | - | -| HistogramWith5LabelsHotPath | 50 | 279.94 ns | 1.595 ns | 1.492 ns | - | -| HistogramWith7LabelsHotPath | 50 | 346.88 ns | 1.064 ns | 0.943 ns | - | -| HistogramHotPath | 100 | 66.39 ns | 0.167 ns | 0.148 ns | - | -| HistogramWith1LabelHotPath | 100 | 114.98 ns | 1.340 ns | 1.253 ns | - | -| HistogramWith3LabelsHotPath | 100 | 220.52 ns | 1.723 ns | 1.528 ns | - | -| HistogramWith5LabelsHotPath | 100 | 299.10 ns | 1.950 ns | 1.629 ns | - | -| HistogramWith7LabelsHotPath | 100 | 356.25 ns | 2.153 ns | 1.798 ns | - | +| HistogramHotPath | 10 | 42.68 ns | 0.116 ns | 0.109 ns | - | +| HistogramWith1LabelHotPath | 10 | 89.94 ns | 0.195 ns | 0.173 ns | - | +| HistogramWith3LabelsHotPath | 10 | 175.81 ns | 0.597 ns | 0.558 ns | - | +| HistogramWith5LabelsHotPath | 10 | 259.52 ns | 0.435 ns | 0.363 ns | - | +| HistogramWith7LabelsHotPath | 10 | 316.83 ns | 0.530 ns | 0.470 ns | - | +| HistogramHotPath | 50 | 50.70 ns | 0.356 ns | 0.333 ns | - | +| HistogramWith1LabelHotPath | 50 | 101.23 ns | 0.155 ns | 0.145 ns | - | +| HistogramWith3LabelsHotPath | 50 | 185.92 ns | 0.290 ns | 0.271 ns | - | +| HistogramWith5LabelsHotPath | 50 | 275.40 ns | 0.357 ns | 0.316 ns | - | +| HistogramWith7LabelsHotPath | 50 | 333.33 ns | 0.646 ns | 0.540 ns | - | +| HistogramHotPath | 390 | 115.16 ns | 0.115 ns | 0.108 ns | - | +| HistogramWith1LabelHotPath | 390 | 165.81 ns | 0.378 ns | 0.353 ns | - | +| HistogramWith3LabelsHotPath | 390 | 265.34 ns | 1.043 ns | 0.975 ns | - | +| HistogramWith5LabelsHotPath | 390 | 374.90 ns | 0.938 ns | 0.878 ns | - | +| HistogramWith7LabelsHotPath | 390 | 437.83 ns | 1.014 ns | 0.847 ns | - | +| HistogramHotPath | 410 | 118.25 ns | 0.103 ns | 0.096 ns | - | +| HistogramWith1LabelHotPath | 410 | 171.96 ns | 0.139 ns | 0.130 ns | - | +| HistogramWith3LabelsHotPath | 410 | 269.87 ns | 0.679 ns | 0.635 ns | - | +| HistogramWith5LabelsHotPath | 410 | 355.99 ns | 0.831 ns | 0.778 ns | - | +| HistogramWith7LabelsHotPath | 410 | 421.68 ns | 0.663 ns | 0.587 ns | - | */ namespace Benchmarks.Metrics @@ -67,7 +67,7 @@ public class HistogramBenchmarks private Meter meter; private double[] bounds; - [Params(10, 20, 50, 100, 200, 500, 1000)] + [Params(10, 50, 390, 410)] public int BoundCount { get; set; } [GlobalSetup] From 51d7219d140a937cfc3baef63ffbe78c3a62bff7 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Wed, 4 May 2022 15:35:13 -0700 Subject: [PATCH 08/58] Update ExplicitBucketHistogramConfiguration.cs Binary search for large bucket count histograms Update CHANGELOG.md netcoreapp3.1 was complaining ci rerun ci rerun Update MetricTestData.cs use 400 buckets --- src/OpenTelemetry/CHANGELOG.md | 5 +- .../ExplicitBucketHistogramConfiguration.cs | 11 +++- src/OpenTelemetry/Metrics/Metric.cs | 2 + src/OpenTelemetry/Metrics/MetricPoint.cs | 60 ++++++++++++++++--- .../Benchmarks/Metrics/HistogramBenchmarks.cs | 54 ++++++++--------- .../Metrics/MetricTestData.cs | 1 + 6 files changed, 93 insertions(+), 40 deletions(-) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 2b3bd30138f..baf4717353e 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -2,9 +2,12 @@ ## Unreleased +* Use binary search for histograms with a large amount of buckets. + ([#3252](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3252)) + * Exposed public setters for `LogRecord.State`, `LogRecord.StateValues`, and `LogRecord.FormattedMessage`. - ([#3217](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3217)) + ([#3217](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3217)) ## 1.3.0-beta.1 diff --git a/src/OpenTelemetry/Metrics/ExplicitBucketHistogramConfiguration.cs b/src/OpenTelemetry/Metrics/ExplicitBucketHistogramConfiguration.cs index 58bfc245b10..bf2f896d404 100644 --- a/src/OpenTelemetry/Metrics/ExplicitBucketHistogramConfiguration.cs +++ b/src/OpenTelemetry/Metrics/ExplicitBucketHistogramConfiguration.cs @@ -30,7 +30,7 @@ public class ExplicitBucketHistogramConfiguration : MetricStreamConfiguration /// Requirements: /// /// The array must be in ascending order with distinct - /// values. + /// values. double.NaN is not allowed /// An empty array would result in no histogram buckets being /// calculated. /// A null value would result in default bucket boundaries being @@ -58,7 +58,7 @@ public double[] Boundaries { if (!IsSortedAndDistinct(value)) { - throw new ArgumentException($"Histogram boundaries are invalid. Histogram boundaries must be in ascending order with distinct values.", nameof(value)); + throw new ArgumentException($"Histogram boundaries are invalid. Histogram boundaries must be in ascending order with distinct values. double.NaN is not allowed.", nameof(value)); } double[] copy = new double[value.Length]; @@ -76,9 +76,14 @@ public double[] Boundaries private static bool IsSortedAndDistinct(double[] values) { + if (double.IsNaN(values[0])) + { + return false; + } + for (int i = 1; i < values.Length; i++) { - if (values[i] <= values[i - 1]) + if (double.IsNaN(values[i]) || values[i] <= values[i - 1]) { return false; } diff --git a/src/OpenTelemetry/Metrics/Metric.cs b/src/OpenTelemetry/Metrics/Metric.cs index 61443173f70..961d0b06ed5 100644 --- a/src/OpenTelemetry/Metrics/Metric.cs +++ b/src/OpenTelemetry/Metrics/Metric.cs @@ -27,6 +27,8 @@ public sealed class Metric { internal static readonly double[] DefaultHistogramBounds = new double[] { 0, 5, 10, 25, 50, 75, 100, 250, 500, 1000 }; + internal static readonly int DefaultHistogramCountForBinarySearch = 400; + private readonly AggregatorStore aggStore; internal Metric( diff --git a/src/OpenTelemetry/Metrics/MetricPoint.cs b/src/OpenTelemetry/Metrics/MetricPoint.cs index f848c319594..4a1f16c122f 100644 --- a/src/OpenTelemetry/Metrics/MetricPoint.cs +++ b/src/OpenTelemetry/Metrics/MetricPoint.cs @@ -30,6 +30,8 @@ public struct MetricPoint private readonly AggregationType aggType; + private readonly Func findHistogramBucketIndex = FindHistogramBucketIndexLinear; + private HistogramBuckets histogramBuckets; // Represents temporality adjusted "value" for double/long metric types or "count" when histogram @@ -61,6 +63,10 @@ internal MetricPoint( if (this.aggType == AggregationType.Histogram) { this.histogramBuckets = new HistogramBuckets(histogramExplicitBounds); + if (histogramExplicitBounds.Length >= Metric.DefaultHistogramCountForBinarySearch) + { + this.findHistogramBucketIndex = FindHistogramBucketIndexBinary; + } } else if (this.aggType == AggregationType.HistogramSumCount) { @@ -324,15 +330,9 @@ internal void Update(double number) case AggregationType.Histogram: { - int i; - for (i = 0; i < this.histogramBuckets.ExplicitBounds.Length; i++) - { - // Upper bound is inclusive - if (number <= this.histogramBuckets.ExplicitBounds[i]) - { - break; - } - } + int i = double.IsNaN(number) + ? this.histogramBuckets.ExplicitBounds.Length + : this.findHistogramBucketIndex(this.histogramBuckets.ExplicitBounds, number); var sw = default(SpinWait); while (true) @@ -545,5 +545,47 @@ private readonly void ThrowNotSupportedMetricTypeException(string methodName) { throw new NotSupportedException($"{methodName} is not supported for this metric type."); } + +#pragma warning disable SA1204 // Static elements should appear before instance elements + private static int FindHistogramBucketIndexLinear(double[] bounds, double number) +#pragma warning restore SA1204 // Static elements should appear before instance elements + { + int i; + + for (i = 0; i < bounds.Length; i++) + { + if (number <= bounds[i]) + { + break; + } + } + + return i; + } + + private static int FindHistogramBucketIndexBinary(double[] bounds, double number) + { + var left = 0; + var right = bounds.Length - 1; + + while (left <= right) + { + var mid = (int)Math.Floor((double)(left + right) / 2); + if (number == bounds[mid]) + { + return mid; + } + else if (number > bounds[mid]) + { + left = mid + 1; + } + else + { + right = mid - 1; + } + } + + return right + 1; + } } } diff --git a/test/Benchmarks/Metrics/HistogramBenchmarks.cs b/test/Benchmarks/Metrics/HistogramBenchmarks.cs index 5511b82a0eb..dbca35b2924 100644 --- a/test/Benchmarks/Metrics/HistogramBenchmarks.cs +++ b/test/Benchmarks/Metrics/HistogramBenchmarks.cs @@ -24,42 +24,42 @@ using OpenTelemetry.Tests; /* -BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000 -Intel Core i7-9700 CPU 3.00GHz, 1 CPU, 8 logical and 8 physical cores -.NET SDK=6.0.200 - [Host] : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT - DefaultJob : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT +BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19044.1706 (21H2) +AMD Ryzen 9 3900X, 1 CPU, 24 logical and 12 physical cores +.NET SDK=6.0.203 + [Host] : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT + DefaultJob : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT | Method | BoundCount | Mean | Error | StdDev | Allocated | |---------------------------- |----------- |----------:|---------:|---------:|----------:| -| HistogramHotPath | 10 | 45.27 ns | 0.384 ns | 0.359 ns | - | -| HistogramWith1LabelHotPath | 10 | 89.99 ns | 0.373 ns | 0.312 ns | - | -| HistogramWith3LabelsHotPath | 10 | 185.34 ns | 3.184 ns | 3.667 ns | - | -| HistogramWith5LabelsHotPath | 10 | 266.69 ns | 1.391 ns | 1.301 ns | - | -| HistogramWith7LabelsHotPath | 10 | 323.20 ns | 1.834 ns | 1.531 ns | - | -| HistogramHotPath | 20 | 48.69 ns | 0.347 ns | 0.307 ns | - | -| HistogramWith1LabelHotPath | 20 | 93.84 ns | 0.696 ns | 0.651 ns | - | -| HistogramWith3LabelsHotPath | 20 | 189.82 ns | 1.208 ns | 1.071 ns | - | -| HistogramWith5LabelsHotPath | 20 | 269.23 ns | 2.027 ns | 1.693 ns | - | -| HistogramWith7LabelsHotPath | 20 | 329.92 ns | 1.272 ns | 1.128 ns | - | -| HistogramHotPath | 50 | 55.73 ns | 0.339 ns | 0.317 ns | - | -| HistogramWith1LabelHotPath | 50 | 100.38 ns | 0.455 ns | 0.425 ns | - | -| HistogramWith3LabelsHotPath | 50 | 200.02 ns | 1.011 ns | 0.844 ns | - | -| HistogramWith5LabelsHotPath | 50 | 279.94 ns | 1.595 ns | 1.492 ns | - | -| HistogramWith7LabelsHotPath | 50 | 346.88 ns | 1.064 ns | 0.943 ns | - | -| HistogramHotPath | 100 | 66.39 ns | 0.167 ns | 0.148 ns | - | -| HistogramWith1LabelHotPath | 100 | 114.98 ns | 1.340 ns | 1.253 ns | - | -| HistogramWith3LabelsHotPath | 100 | 220.52 ns | 1.723 ns | 1.528 ns | - | -| HistogramWith5LabelsHotPath | 100 | 299.10 ns | 1.950 ns | 1.629 ns | - | -| HistogramWith7LabelsHotPath | 100 | 356.25 ns | 2.153 ns | 1.798 ns | - | +| HistogramHotPath | 10 | 42.68 ns | 0.116 ns | 0.109 ns | - | +| HistogramWith1LabelHotPath | 10 | 89.94 ns | 0.195 ns | 0.173 ns | - | +| HistogramWith3LabelsHotPath | 10 | 175.81 ns | 0.597 ns | 0.558 ns | - | +| HistogramWith5LabelsHotPath | 10 | 259.52 ns | 0.435 ns | 0.363 ns | - | +| HistogramWith7LabelsHotPath | 10 | 316.83 ns | 0.530 ns | 0.470 ns | - | +| HistogramHotPath | 50 | 50.70 ns | 0.356 ns | 0.333 ns | - | +| HistogramWith1LabelHotPath | 50 | 101.23 ns | 0.155 ns | 0.145 ns | - | +| HistogramWith3LabelsHotPath | 50 | 185.92 ns | 0.290 ns | 0.271 ns | - | +| HistogramWith5LabelsHotPath | 50 | 275.40 ns | 0.357 ns | 0.316 ns | - | +| HistogramWith7LabelsHotPath | 50 | 333.33 ns | 0.646 ns | 0.540 ns | - | +| HistogramHotPath | 390 | 115.16 ns | 0.115 ns | 0.108 ns | - | +| HistogramWith1LabelHotPath | 390 | 165.81 ns | 0.378 ns | 0.353 ns | - | +| HistogramWith3LabelsHotPath | 390 | 265.34 ns | 1.043 ns | 0.975 ns | - | +| HistogramWith5LabelsHotPath | 390 | 374.90 ns | 0.938 ns | 0.878 ns | - | +| HistogramWith7LabelsHotPath | 390 | 437.83 ns | 1.014 ns | 0.847 ns | - | +| HistogramHotPath | 410 | 118.25 ns | 0.103 ns | 0.096 ns | - | +| HistogramWith1LabelHotPath | 410 | 171.96 ns | 0.139 ns | 0.130 ns | - | +| HistogramWith3LabelsHotPath | 410 | 269.87 ns | 0.679 ns | 0.635 ns | - | +| HistogramWith5LabelsHotPath | 410 | 355.99 ns | 0.831 ns | 0.778 ns | - | +| HistogramWith7LabelsHotPath | 410 | 421.68 ns | 0.663 ns | 0.587 ns | - | */ namespace Benchmarks.Metrics { public class HistogramBenchmarks { - private const int MaxValue = 1000; + private const int MaxValue = 10000; private readonly Random random = new(); private readonly string[] dimensionValues = new string[] { "DimVal1", "DimVal2", "DimVal3", "DimVal4", "DimVal5", "DimVal6", "DimVal7", "DimVal8", "DimVal9", "DimVal10" }; private Histogram histogram; @@ -67,7 +67,7 @@ public class HistogramBenchmarks private Meter meter; private double[] bounds; - [Params(10, 20, 50, 100)] + [Params(10, 50, 390, 410)] public int BoundCount { get; set; } [GlobalSetup] diff --git a/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs b/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs index 0664dc18c00..2e7052b773a 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs @@ -44,6 +44,7 @@ public static IEnumerable ValidInstrumentNames public static IEnumerable InvalidHistogramBoundaries => new List { + new object[] { new double[] { double.NaN } }, new object[] { new double[] { 0, 0 } }, new object[] { new double[] { 1, 0 } }, new object[] { new double[] { 0, 1, 1, 2 } }, From 1bb61e9b8d13e5672e24634ef8040d3dcaa20662 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Thu, 12 May 2022 12:52:59 -0700 Subject: [PATCH 09/58] separate nan part --- .../Metrics/ExplicitBucketHistogramConfiguration.cs | 11 +++-------- src/OpenTelemetry/Metrics/MetricPoint.cs | 4 +--- test/OpenTelemetry.Tests/Metrics/MetricTestData.cs | 1 - 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/OpenTelemetry/Metrics/ExplicitBucketHistogramConfiguration.cs b/src/OpenTelemetry/Metrics/ExplicitBucketHistogramConfiguration.cs index bf2f896d404..58bfc245b10 100644 --- a/src/OpenTelemetry/Metrics/ExplicitBucketHistogramConfiguration.cs +++ b/src/OpenTelemetry/Metrics/ExplicitBucketHistogramConfiguration.cs @@ -30,7 +30,7 @@ public class ExplicitBucketHistogramConfiguration : MetricStreamConfiguration /// Requirements: /// /// The array must be in ascending order with distinct - /// values. double.NaN is not allowed + /// values. /// An empty array would result in no histogram buckets being /// calculated. /// A null value would result in default bucket boundaries being @@ -58,7 +58,7 @@ public double[] Boundaries { if (!IsSortedAndDistinct(value)) { - throw new ArgumentException($"Histogram boundaries are invalid. Histogram boundaries must be in ascending order with distinct values. double.NaN is not allowed.", nameof(value)); + throw new ArgumentException($"Histogram boundaries are invalid. Histogram boundaries must be in ascending order with distinct values.", nameof(value)); } double[] copy = new double[value.Length]; @@ -76,14 +76,9 @@ public double[] Boundaries private static bool IsSortedAndDistinct(double[] values) { - if (double.IsNaN(values[0])) - { - return false; - } - for (int i = 1; i < values.Length; i++) { - if (double.IsNaN(values[i]) || values[i] <= values[i - 1]) + if (values[i] <= values[i - 1]) { return false; } diff --git a/src/OpenTelemetry/Metrics/MetricPoint.cs b/src/OpenTelemetry/Metrics/MetricPoint.cs index 4a1f16c122f..41921f6586e 100644 --- a/src/OpenTelemetry/Metrics/MetricPoint.cs +++ b/src/OpenTelemetry/Metrics/MetricPoint.cs @@ -330,9 +330,7 @@ internal void Update(double number) case AggregationType.Histogram: { - int i = double.IsNaN(number) - ? this.histogramBuckets.ExplicitBounds.Length - : this.findHistogramBucketIndex(this.histogramBuckets.ExplicitBounds, number); + int i = this.findHistogramBucketIndex(this.histogramBuckets.ExplicitBounds, number); var sw = default(SpinWait); while (true) diff --git a/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs b/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs index 2e7052b773a..0664dc18c00 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs @@ -44,7 +44,6 @@ public static IEnumerable ValidInstrumentNames public static IEnumerable InvalidHistogramBoundaries => new List { - new object[] { new double[] { double.NaN } }, new object[] { new double[] { 0, 0 } }, new object[] { new double[] { 1, 0 } }, new object[] { new double[] { 0, 1, 1, 2 } }, From 685b46e11034eeb681815f8186ed6f4ddea2a949 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Thu, 12 May 2022 15:12:07 -0700 Subject: [PATCH 10/58] Address PR comments - Remove conversion to float from `FindHistogramBucketIndexBinary` - Make `DefaultHistogramCountForBinarySearch` a `const` instead of a `static readonly` --- src/OpenTelemetry/Metrics/Metric.cs | 4 ++-- src/OpenTelemetry/Metrics/MetricPoint.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/OpenTelemetry/Metrics/Metric.cs b/src/OpenTelemetry/Metrics/Metric.cs index 961d0b06ed5..90752f19f76 100644 --- a/src/OpenTelemetry/Metrics/Metric.cs +++ b/src/OpenTelemetry/Metrics/Metric.cs @@ -25,9 +25,9 @@ namespace OpenTelemetry.Metrics /// public sealed class Metric { - internal static readonly double[] DefaultHistogramBounds = new double[] { 0, 5, 10, 25, 50, 75, 100, 250, 500, 1000 }; + internal const int DefaultHistogramCountForBinarySearch = 400; - internal static readonly int DefaultHistogramCountForBinarySearch = 400; + internal static readonly double[] DefaultHistogramBounds = new double[] { 0, 5, 10, 25, 50, 75, 100, 250, 500, 1000 }; private readonly AggregatorStore aggStore; diff --git a/src/OpenTelemetry/Metrics/MetricPoint.cs b/src/OpenTelemetry/Metrics/MetricPoint.cs index 41921f6586e..6c7ba9dcf86 100644 --- a/src/OpenTelemetry/Metrics/MetricPoint.cs +++ b/src/OpenTelemetry/Metrics/MetricPoint.cs @@ -563,12 +563,12 @@ private static int FindHistogramBucketIndexLinear(double[] bounds, double number private static int FindHistogramBucketIndexBinary(double[] bounds, double number) { - var left = 0; - var right = bounds.Length - 1; + int left = 0; + int right = bounds.Length - 1; while (left <= right) { - var mid = (int)Math.Floor((double)(left + right) / 2); + int mid = (left + right) / 2; if (number == bounds[mid]) { return mid; From de60b0e520f769e77a69d05a7b83e53bca00d961 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Thu, 12 May 2022 16:39:41 -0700 Subject: [PATCH 11/58] update to 140 --- src/OpenTelemetry/Metrics/Metric.cs | 4 +- .../Benchmarks/Metrics/HistogramBenchmarks.cs | 48 +++++++++++-------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/OpenTelemetry/Metrics/Metric.cs b/src/OpenTelemetry/Metrics/Metric.cs index 887aa61aeed..9bda48aea24 100644 --- a/src/OpenTelemetry/Metrics/Metric.cs +++ b/src/OpenTelemetry/Metrics/Metric.cs @@ -25,12 +25,10 @@ namespace OpenTelemetry.Metrics /// public sealed class Metric { - internal const int DefaultHistogramCountForBinarySearch = 400; + internal const int DefaultHistogramCountForBinarySearch = 140; internal static readonly double[] DefaultHistogramBounds = new double[] { 0, 5, 10, 25, 50, 75, 100, 250, 500, 1000 }; - internal static readonly int DefaultHistogramCountForBinarySearch = 400; - private readonly AggregatorStore aggStore; internal Metric( diff --git a/test/Benchmarks/Metrics/HistogramBenchmarks.cs b/test/Benchmarks/Metrics/HistogramBenchmarks.cs index dbca35b2924..ca5ed2246ac 100644 --- a/test/Benchmarks/Metrics/HistogramBenchmarks.cs +++ b/test/Benchmarks/Metrics/HistogramBenchmarks.cs @@ -33,26 +33,31 @@ | Method | BoundCount | Mean | Error | StdDev | Allocated | |---------------------------- |----------- |----------:|---------:|---------:|----------:| -| HistogramHotPath | 10 | 42.68 ns | 0.116 ns | 0.109 ns | - | -| HistogramWith1LabelHotPath | 10 | 89.94 ns | 0.195 ns | 0.173 ns | - | -| HistogramWith3LabelsHotPath | 10 | 175.81 ns | 0.597 ns | 0.558 ns | - | -| HistogramWith5LabelsHotPath | 10 | 259.52 ns | 0.435 ns | 0.363 ns | - | -| HistogramWith7LabelsHotPath | 10 | 316.83 ns | 0.530 ns | 0.470 ns | - | -| HistogramHotPath | 50 | 50.70 ns | 0.356 ns | 0.333 ns | - | -| HistogramWith1LabelHotPath | 50 | 101.23 ns | 0.155 ns | 0.145 ns | - | -| HistogramWith3LabelsHotPath | 50 | 185.92 ns | 0.290 ns | 0.271 ns | - | -| HistogramWith5LabelsHotPath | 50 | 275.40 ns | 0.357 ns | 0.316 ns | - | -| HistogramWith7LabelsHotPath | 50 | 333.33 ns | 0.646 ns | 0.540 ns | - | -| HistogramHotPath | 390 | 115.16 ns | 0.115 ns | 0.108 ns | - | -| HistogramWith1LabelHotPath | 390 | 165.81 ns | 0.378 ns | 0.353 ns | - | -| HistogramWith3LabelsHotPath | 390 | 265.34 ns | 1.043 ns | 0.975 ns | - | -| HistogramWith5LabelsHotPath | 390 | 374.90 ns | 0.938 ns | 0.878 ns | - | -| HistogramWith7LabelsHotPath | 390 | 437.83 ns | 1.014 ns | 0.847 ns | - | -| HistogramHotPath | 410 | 118.25 ns | 0.103 ns | 0.096 ns | - | -| HistogramWith1LabelHotPath | 410 | 171.96 ns | 0.139 ns | 0.130 ns | - | -| HistogramWith3LabelsHotPath | 410 | 269.87 ns | 0.679 ns | 0.635 ns | - | -| HistogramWith5LabelsHotPath | 410 | 355.99 ns | 0.831 ns | 0.778 ns | - | -| HistogramWith7LabelsHotPath | 410 | 421.68 ns | 0.663 ns | 0.587 ns | - | +| HistogramHotPath | 10 | 41.79 ns | 0.096 ns | 0.089 ns | - | +| HistogramWith1LabelHotPath | 10 | 93.32 ns | 0.185 ns | 0.173 ns | - | +| HistogramWith3LabelsHotPath | 10 | 173.11 ns | 0.090 ns | 0.079 ns | - | +| HistogramWith5LabelsHotPath | 10 | 263.42 ns | 0.542 ns | 0.507 ns | - | +| HistogramWith7LabelsHotPath | 10 | 318.65 ns | 0.388 ns | 0.344 ns | - | +| HistogramHotPath | 50 | 51.52 ns | 0.234 ns | 0.208 ns | - | +| HistogramWith1LabelHotPath | 50 | 102.16 ns | 0.201 ns | 0.178 ns | - | +| HistogramWith3LabelsHotPath | 50 | 188.54 ns | 0.263 ns | 0.246 ns | - | +| HistogramWith5LabelsHotPath | 50 | 274.89 ns | 0.471 ns | 0.441 ns | - | +| HistogramWith7LabelsHotPath | 50 | 334.87 ns | 0.541 ns | 0.451 ns | - | +| HistogramHotPath | 139 | 75.40 ns | 0.085 ns | 0.075 ns | - | +| HistogramWith1LabelHotPath | 139 | 123.86 ns | 0.510 ns | 0.477 ns | - | +| HistogramWith3LabelsHotPath | 139 | 211.11 ns | 0.415 ns | 0.368 ns | - | +| HistogramWith5LabelsHotPath | 139 | 298.31 ns | 0.788 ns | 0.737 ns | - | +| HistogramWith7LabelsHotPath | 139 | 357.28 ns | 0.619 ns | 0.548 ns | - | +| HistogramHotPath | 140 | 69.13 ns | 0.171 ns | 0.160 ns | - | +| HistogramWith1LabelHotPath | 140 | 117.86 ns | 0.182 ns | 0.171 ns | - | +| HistogramWith3LabelsHotPath | 140 | 208.26 ns | 0.382 ns | 0.319 ns | - | +| HistogramWith5LabelsHotPath | 140 | 297.56 ns | 0.769 ns | 0.682 ns | - | +| HistogramWith7LabelsHotPath | 140 | 349.53 ns | 0.581 ns | 0.515 ns | - | +| HistogramHotPath | 1000 | 85.90 ns | 0.263 ns | 0.246 ns | - | +| HistogramWith1LabelHotPath | 1000 | 136.94 ns | 0.475 ns | 0.444 ns | - | +| HistogramWith3LabelsHotPath | 1000 | 230.74 ns | 0.465 ns | 0.435 ns | - | +| HistogramWith5LabelsHotPath | 1000 | 325.73 ns | 2.040 ns | 1.908 ns | - | +| HistogramWith7LabelsHotPath | 1000 | 379.81 ns | 2.100 ns | 1.964 ns | - | */ namespace Benchmarks.Metrics @@ -67,7 +72,8 @@ public class HistogramBenchmarks private Meter meter; private double[] bounds; - [Params(10, 50, 390, 410)] + // Note: Values related to `Metric.DefaultHistogramCountForBinarySearch` + [Params(10, 50, 139, 140, 1000)] public int BoundCount { get; set; } [GlobalSetup] From 020c42e7efa1ab2c7d6b8b30d699a1bc9e37171e Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Thu, 12 May 2022 16:41:03 -0700 Subject: [PATCH 12/58] remove double.nan from invalid hist bounds --- test/OpenTelemetry.Tests/Metrics/MetricTestData.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs b/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs index 2e7052b773a..0664dc18c00 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs @@ -44,7 +44,6 @@ public static IEnumerable ValidInstrumentNames public static IEnumerable InvalidHistogramBoundaries => new List { - new object[] { new double[] { double.NaN } }, new object[] { new double[] { 0, 0 } }, new object[] { new double[] { 1, 0 } }, new object[] { new double[] { 0, 1, 1, 2 } }, From daf1a806151dc194bb8ea4e95d6015deb979b3ca Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Wed, 25 May 2022 15:27:49 -0700 Subject: [PATCH 13/58] Refactor and perf to histogram bucket index find Thanks to @CodeBlanch !! --- src/OpenTelemetry/Metrics/HistogramBuckets.cs | 92 +++++++++++++++++++ src/OpenTelemetry/Metrics/MetricPoint.cs | 52 +---------- 2 files changed, 93 insertions(+), 51 deletions(-) diff --git a/src/OpenTelemetry/Metrics/HistogramBuckets.cs b/src/OpenTelemetry/Metrics/HistogramBuckets.cs index 626779d4901..4ccf984414b 100644 --- a/src/OpenTelemetry/Metrics/HistogramBuckets.cs +++ b/src/OpenTelemetry/Metrics/HistogramBuckets.cs @@ -15,6 +15,8 @@ // using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; namespace OpenTelemetry.Metrics { @@ -36,9 +38,38 @@ public class HistogramBuckets internal int IsCriticalSectionOccupied = 0; + private readonly Bucket root; + + private readonly Func findHistogramBucketIndex; + internal HistogramBuckets(double[] explicitBounds) { this.ExplicitBounds = explicitBounds; + this.findHistogramBucketIndex = this.FindBucketIndexLinear; + if (explicitBounds != null && explicitBounds.Length >= Metric.DefaultHistogramCountForBinarySearch) + { + this.root = ConstructBalancedTree(explicitBounds, 0, explicitBounds.Length); + this.findHistogramBucketIndex = this.FindBucketIndexBinary; + + static Bucket ConstructBalancedTree(double[] values, int min, int max) + { + if (min == max) + { + return null; + } + + int median = min + ((max - min) / 2); + return new Bucket + { + Index = median, + UpperBoundInclusive = values[median], + LowerBoundExclusive = median > 0 ? values[median - 1] : double.MinValue, + Left = ConstructBalancedTree(values, min, median), + Right = ConstructBalancedTree(values, median + 1, max), + }; + } + } + this.RunningBucketCounts = explicitBounds != null ? new long[explicitBounds.Length + 1] : null; this.SnapshotBucketCounts = explicitBounds != null ? new long[explicitBounds.Length + 1] : new long[0]; } @@ -57,6 +88,54 @@ internal HistogramBuckets Copy() return copy; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int FindBucketIndex(double value) + { + return this.findHistogramBucketIndex(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int FindBucketIndexBinary(double value) + { + Bucket current = this.root; + + Debug.Assert(current != null, "Bucket root was null."); + + while (current != null) + { + if (value <= current.LowerBoundExclusive) + { + current = current.Left; + } + else if (value > current.UpperBoundInclusive) + { + current = current.Right; + } + else + { + return current.Index; + } + } + + return this.ExplicitBounds.Length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int FindBucketIndexLinear(double value) + { + int i; + for (i = 0; i < this.ExplicitBounds.Length; i++) + { + // Upper bound is inclusive + if (value <= this.ExplicitBounds[i]) + { + break; + } + } + + return i; + } + /// /// Enumerates the elements of a . /// @@ -104,5 +183,18 @@ public bool MoveNext() return false; } } + + private class Bucket + { + public double UpperBoundInclusive { get; set; } + + public double LowerBoundExclusive { get; set; } + + public int Index { get; set; } + + public Bucket Left { get; set; } + + public Bucket Right { get; set; } + } } } diff --git a/src/OpenTelemetry/Metrics/MetricPoint.cs b/src/OpenTelemetry/Metrics/MetricPoint.cs index b0eb3f24687..149f20bc8f8 100644 --- a/src/OpenTelemetry/Metrics/MetricPoint.cs +++ b/src/OpenTelemetry/Metrics/MetricPoint.cs @@ -30,8 +30,6 @@ public struct MetricPoint private readonly AggregationType aggType; - private readonly Func findHistogramBucketIndex = FindHistogramBucketIndexLinear; - private HistogramBuckets histogramBuckets; // Represents temporality adjusted "value" for double/long metric types or "count" when histogram @@ -63,10 +61,6 @@ internal MetricPoint( if (this.aggType == AggregationType.Histogram) { this.histogramBuckets = new HistogramBuckets(histogramExplicitBounds); - if (histogramExplicitBounds.Length >= Metric.DefaultHistogramCountForBinarySearch) - { - this.findHistogramBucketIndex = FindHistogramBucketIndexBinary; - } } else if (this.aggType == AggregationType.HistogramSumCount) { @@ -330,9 +324,7 @@ internal void Update(double number) case AggregationType.Histogram: { - int i = double.IsNaN(number) - ? this.histogramBuckets.ExplicitBounds.Length - : this.findHistogramBucketIndex(this.histogramBuckets.ExplicitBounds, number); + int i = this.histogramBuckets.FindBucketIndex(number); var sw = default(SpinWait); while (true) @@ -545,47 +537,5 @@ private readonly void ThrowNotSupportedMetricTypeException(string methodName) { throw new NotSupportedException($"{methodName} is not supported for this metric type."); } - -#pragma warning disable SA1204 // Static elements should appear before instance elements - private static int FindHistogramBucketIndexLinear(double[] bounds, double number) -#pragma warning restore SA1204 // Static elements should appear before instance elements - { - int i; - - for (i = 0; i < bounds.Length; i++) - { - if (number <= bounds[i]) - { - break; - } - } - - return i; - } - - private static int FindHistogramBucketIndexBinary(double[] bounds, double number) - { - int left = 0; - int right = bounds.Length - 1; - - while (left <= right) - { - int mid = (left + right) / 2; - if (number == bounds[mid]) - { - return mid; - } - else if (number > bounds[mid]) - { - left = mid + 1; - } - else - { - right = mid - 1; - } - } - - return right + 1; - } } } From 5bc4998d64adb9932fa2df57835d44bc4d689d25 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Wed, 25 May 2022 17:19:38 -0700 Subject: [PATCH 14/58] fine tune bound limit to switch to binary search --- src/OpenTelemetry/Metrics/HistogramBuckets.cs | 4 +++- src/OpenTelemetry/Metrics/Metric.cs | 2 -- test/Benchmarks/Metrics/HistogramBenchmarks.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/OpenTelemetry/Metrics/HistogramBuckets.cs b/src/OpenTelemetry/Metrics/HistogramBuckets.cs index 4ccf984414b..9ac390fde2f 100644 --- a/src/OpenTelemetry/Metrics/HistogramBuckets.cs +++ b/src/OpenTelemetry/Metrics/HistogramBuckets.cs @@ -26,6 +26,8 @@ namespace OpenTelemetry.Metrics // Note: Does not implement IEnumerable<> to prevent accidental boxing. public class HistogramBuckets { + internal const int DefaultHistogramCountForBinarySearch = 50; + internal readonly double[] ExplicitBounds; internal readonly long[] RunningBucketCounts; @@ -46,7 +48,7 @@ internal HistogramBuckets(double[] explicitBounds) { this.ExplicitBounds = explicitBounds; this.findHistogramBucketIndex = this.FindBucketIndexLinear; - if (explicitBounds != null && explicitBounds.Length >= Metric.DefaultHistogramCountForBinarySearch) + if (explicitBounds != null && explicitBounds.Length >= DefaultHistogramCountForBinarySearch) { this.root = ConstructBalancedTree(explicitBounds, 0, explicitBounds.Length); this.findHistogramBucketIndex = this.FindBucketIndexBinary; diff --git a/src/OpenTelemetry/Metrics/Metric.cs b/src/OpenTelemetry/Metrics/Metric.cs index 9bda48aea24..61443173f70 100644 --- a/src/OpenTelemetry/Metrics/Metric.cs +++ b/src/OpenTelemetry/Metrics/Metric.cs @@ -25,8 +25,6 @@ namespace OpenTelemetry.Metrics /// public sealed class Metric { - internal const int DefaultHistogramCountForBinarySearch = 140; - internal static readonly double[] DefaultHistogramBounds = new double[] { 0, 5, 10, 25, 50, 75, 100, 250, 500, 1000 }; private readonly AggregatorStore aggStore; diff --git a/test/Benchmarks/Metrics/HistogramBenchmarks.cs b/test/Benchmarks/Metrics/HistogramBenchmarks.cs index ca5ed2246ac..e82164c21fa 100644 --- a/test/Benchmarks/Metrics/HistogramBenchmarks.cs +++ b/test/Benchmarks/Metrics/HistogramBenchmarks.cs @@ -72,8 +72,8 @@ public class HistogramBenchmarks private Meter meter; private double[] bounds; - // Note: Values related to `Metric.DefaultHistogramCountForBinarySearch` - [Params(10, 50, 139, 140, 1000)] + // Note: Values related to `HistogramBuckets.DefaultHistogramCountForBinarySearch` + [Params(10, 49, 50, 1000)] public int BoundCount { get; set; } [GlobalSetup] From e84127ad58f58ab6dfb9a8a01ca2fdc747b89cc1 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Wed, 25 May 2022 18:19:39 -0700 Subject: [PATCH 15/58] included benchmark results in comment --- .../Benchmarks/Metrics/HistogramBenchmarks.cs | 53 +++++++++---------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/test/Benchmarks/Metrics/HistogramBenchmarks.cs b/test/Benchmarks/Metrics/HistogramBenchmarks.cs index e82164c21fa..fdab0386ac3 100644 --- a/test/Benchmarks/Metrics/HistogramBenchmarks.cs +++ b/test/Benchmarks/Metrics/HistogramBenchmarks.cs @@ -24,40 +24,35 @@ using OpenTelemetry.Tests; /* -BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19044.1706 (21H2) -AMD Ryzen 9 3900X, 1 CPU, 24 logical and 12 physical cores +BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000 +Intel Core i7-1065G7 CPU 1.30GHz, 1 CPU, 8 logical and 4 physical cores .NET SDK=6.0.203 [Host] : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT DefaultJob : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT -| Method | BoundCount | Mean | Error | StdDev | Allocated | -|---------------------------- |----------- |----------:|---------:|---------:|----------:| -| HistogramHotPath | 10 | 41.79 ns | 0.096 ns | 0.089 ns | - | -| HistogramWith1LabelHotPath | 10 | 93.32 ns | 0.185 ns | 0.173 ns | - | -| HistogramWith3LabelsHotPath | 10 | 173.11 ns | 0.090 ns | 0.079 ns | - | -| HistogramWith5LabelsHotPath | 10 | 263.42 ns | 0.542 ns | 0.507 ns | - | -| HistogramWith7LabelsHotPath | 10 | 318.65 ns | 0.388 ns | 0.344 ns | - | -| HistogramHotPath | 50 | 51.52 ns | 0.234 ns | 0.208 ns | - | -| HistogramWith1LabelHotPath | 50 | 102.16 ns | 0.201 ns | 0.178 ns | - | -| HistogramWith3LabelsHotPath | 50 | 188.54 ns | 0.263 ns | 0.246 ns | - | -| HistogramWith5LabelsHotPath | 50 | 274.89 ns | 0.471 ns | 0.441 ns | - | -| HistogramWith7LabelsHotPath | 50 | 334.87 ns | 0.541 ns | 0.451 ns | - | -| HistogramHotPath | 139 | 75.40 ns | 0.085 ns | 0.075 ns | - | -| HistogramWith1LabelHotPath | 139 | 123.86 ns | 0.510 ns | 0.477 ns | - | -| HistogramWith3LabelsHotPath | 139 | 211.11 ns | 0.415 ns | 0.368 ns | - | -| HistogramWith5LabelsHotPath | 139 | 298.31 ns | 0.788 ns | 0.737 ns | - | -| HistogramWith7LabelsHotPath | 139 | 357.28 ns | 0.619 ns | 0.548 ns | - | -| HistogramHotPath | 140 | 69.13 ns | 0.171 ns | 0.160 ns | - | -| HistogramWith1LabelHotPath | 140 | 117.86 ns | 0.182 ns | 0.171 ns | - | -| HistogramWith3LabelsHotPath | 140 | 208.26 ns | 0.382 ns | 0.319 ns | - | -| HistogramWith5LabelsHotPath | 140 | 297.56 ns | 0.769 ns | 0.682 ns | - | -| HistogramWith7LabelsHotPath | 140 | 349.53 ns | 0.581 ns | 0.515 ns | - | -| HistogramHotPath | 1000 | 85.90 ns | 0.263 ns | 0.246 ns | - | -| HistogramWith1LabelHotPath | 1000 | 136.94 ns | 0.475 ns | 0.444 ns | - | -| HistogramWith3LabelsHotPath | 1000 | 230.74 ns | 0.465 ns | 0.435 ns | - | -| HistogramWith5LabelsHotPath | 1000 | 325.73 ns | 2.040 ns | 1.908 ns | - | -| HistogramWith7LabelsHotPath | 1000 | 379.81 ns | 2.100 ns | 1.964 ns | - | +| Method | BoundCount | Mean | Error | StdDev | +|---------------------------- |----------- |----------:|----------:|----------:| +| HistogramHotPath | 10 | 55.07 ns | 0.664 ns | 1.091 ns | +| HistogramWith1LabelHotPath | 10 | 108.66 ns | 1.324 ns | 1.174 ns | +| HistogramWith3LabelsHotPath | 10 | 193.79 ns | 3.261 ns | 3.349 ns | +| HistogramWith5LabelsHotPath | 10 | 279.44 ns | 4.608 ns | 3.848 ns | +| HistogramWith7LabelsHotPath | 10 | 334.28 ns | 6.650 ns | 5.895 ns | +| HistogramHotPath | 49 | 68.27 ns | 0.744 ns | 0.581 ns | +| HistogramWith1LabelHotPath | 49 | 125.55 ns | 2.265 ns | 2.518 ns | +| HistogramWith3LabelsHotPath | 49 | 207.95 ns | 4.023 ns | 3.951 ns | +| HistogramWith5LabelsHotPath | 49 | 293.45 ns | 5.689 ns | 5.842 ns | +| HistogramWith7LabelsHotPath | 49 | 362.19 ns | 5.610 ns | 6.003 ns | +| HistogramHotPath | 50 | 69.64 ns | 1.422 ns | 1.330 ns | +| HistogramWith1LabelHotPath | 50 | 118.15 ns | 2.040 ns | 1.908 ns | +| HistogramWith3LabelsHotPath | 50 | 250.31 ns | 4.617 ns | 9.326 ns | +| HistogramWith5LabelsHotPath | 50 | 335.31 ns | 3.904 ns | 3.461 ns | +| HistogramWith7LabelsHotPath | 50 | 398.02 ns | 6.815 ns | 6.374 ns | +| HistogramHotPath | 1000 | 94.05 ns | 1.890 ns | 2.100 ns | +| HistogramWith1LabelHotPath | 1000 | 148.57 ns | 2.055 ns | 1.822 ns | +| HistogramWith3LabelsHotPath | 1000 | 661.78 ns | 11.599 ns | 20.314 ns | +| HistogramWith5LabelsHotPath | 1000 | 761.54 ns | 15.049 ns | 16.727 ns | +| HistogramWith7LabelsHotPath | 1000 | 830.14 ns | 16.063 ns | 17.853 ns | */ namespace Benchmarks.Metrics From fb1ad6b2ccc47234f13d23bdb5983373c30eac30 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Thu, 9 Jun 2022 13:45:16 -0700 Subject: [PATCH 16/58] histogram stress test update --- src/OpenTelemetry/Metrics/HistogramBuckets.cs | 3 +- .../Program.cs | 36 ++++++++----------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/OpenTelemetry/Metrics/HistogramBuckets.cs b/src/OpenTelemetry/Metrics/HistogramBuckets.cs index 9ac390fde2f..38a49be706a 100644 --- a/src/OpenTelemetry/Metrics/HistogramBuckets.cs +++ b/src/OpenTelemetry/Metrics/HistogramBuckets.cs @@ -103,7 +103,7 @@ internal int FindBucketIndexBinary(double value) Debug.Assert(current != null, "Bucket root was null."); - while (current != null) + do { if (value <= current.LowerBoundExclusive) { @@ -118,6 +118,7 @@ internal int FindBucketIndexBinary(double value) return current.Index; } } + while (current != null); return this.ExplicitBounds.Length; } diff --git a/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs b/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs index 8838a1854c9..cecfc984931 100644 --- a/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs +++ b/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs @@ -24,18 +24,15 @@ namespace OpenTelemetry.Tests.Stress; public partial class Program { - private const int ArraySize = 10; - - // Note: Uncomment the below line if you want to run Histogram stress test - // private const int MaxHistogramMeasurement = 1000; + private const int ArraySize = 49; + private const int MaxValue = 1000; private static readonly Meter TestMeter = new(Utils.GetCurrentMethodName()); - private static readonly Counter TestCounter = TestMeter.CreateCounter("TestCounter"); private static readonly string[] DimensionValues = new string[ArraySize]; private static readonly ThreadLocal ThreadLocalRandom = new(() => new Random()); - // Note: Uncomment the below line if you want to run Histogram stress test - // private static readonly Histogram TestHistogram = TestMeter.CreateHistogram("TestHistogram"); + private static readonly Counter TestCounter = TestMeter.CreateCounter("TestCounter"); + private static readonly Histogram TestHistogram = TestMeter.CreateHistogram("TestHistogram"); public static void Main() { @@ -44,6 +41,12 @@ public static void Main() DimensionValues[i] = $"DimValue{i}"; } + var bounds = new double[ArraySize]; + for (int i = 0; i < bounds.Length; i++) + { + bounds[i] = i * MaxValue / bounds.Length; + } + using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(TestMeter.Name) .AddPrometheusExporter(options => @@ -52,6 +55,7 @@ public static void Main() options.HttpListenerPrefixes = new string[] { $"http://localhost:9185/" }; options.ScrapeResponseCacheDurationMilliseconds = 0; }) + .AddView(TestHistogram.Name, new ExplicitBucketHistogramConfiguration() { Boundaries = bounds }) .Build(); Stress(prometheusPort: 9184); @@ -61,22 +65,12 @@ public static void Main() protected static void Run() { var random = ThreadLocalRandom.Value; - TestCounter.Add( + /*TestCounter.Add( 100, new("DimName1", DimensionValues[random.Next(0, ArraySize)]), new("DimName2", DimensionValues[random.Next(0, ArraySize)]), - new("DimName3", DimensionValues[random.Next(0, ArraySize)])); - } + new("DimName3", DimensionValues[random.Next(0, ArraySize)]));*/ - // Note: Uncomment the below lines if you want to run Histogram stress test - // [MethodImpl(MethodImplOptions.AggressiveInlining)] - // protected static void Run() - // { - // var random = ThreadLocalRandom.Value; - // TestHistogram.Record( - // random.Next(MaxHistogramMeasurement), - // new("DimName1", DimensionValues[random.Next(0, ArraySize)]), - // new("DimName2", DimensionValues[random.Next(0, ArraySize)]), - // new("DimName3", DimensionValues[random.Next(0, ArraySize)])); - // } + TestHistogram.Record(random.Next(MaxValue)); + } } From 05cc3c133c1bff19c6dd308ad94d5388607bbbdc Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Tue, 28 Jun 2022 15:55:15 -0700 Subject: [PATCH 17/58] fix changelog --- src/OpenTelemetry/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index a77590f915a..18eefd19eff 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -52,7 +52,7 @@ Released 2022-May-16 * Exposed public setters for `LogRecord.State`, `LogRecord.StateValues`, and `LogRecord.FormattedMessage`. - ([#3217](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3217)) + ([#3217](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3217)) ## 1.3.0-beta.1 From 15c34641bd91cf6cf4e6f8dbb71225e45b38427c Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Tue, 28 Jun 2022 15:56:19 -0700 Subject: [PATCH 18/58] spacing fix --- src/OpenTelemetry/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 18eefd19eff..a77590f915a 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -52,7 +52,7 @@ Released 2022-May-16 * Exposed public setters for `LogRecord.State`, `LogRecord.StateValues`, and `LogRecord.FormattedMessage`. - ([#3217](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3217)) + ([#3217](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3217)) ## 1.3.0-beta.1 From a2c0e5dbc1da3e3377cfb9c113ca05920038298a Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Fri, 1 Jul 2022 15:16:38 -0700 Subject: [PATCH 19/58] sealed bucket class --- src/OpenTelemetry/Metrics/HistogramBuckets.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry/Metrics/HistogramBuckets.cs b/src/OpenTelemetry/Metrics/HistogramBuckets.cs index 38a49be706a..438f38284ef 100644 --- a/src/OpenTelemetry/Metrics/HistogramBuckets.cs +++ b/src/OpenTelemetry/Metrics/HistogramBuckets.cs @@ -187,7 +187,7 @@ public bool MoveNext() } } - private class Bucket + private sealed class Bucket { public double UpperBoundInclusive { get; set; } From ef28da0f4e814dd7c3ac61ce499f90a7040732b3 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Fri, 1 Jul 2022 15:44:25 -0700 Subject: [PATCH 20/58] ci From 543a6d53be5754d231f656f729c09eb3e476df29 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Tue, 5 Jul 2022 12:57:35 -0700 Subject: [PATCH 21/58] double.negative infinity --- src/OpenTelemetry/Metrics/HistogramBuckets.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry/Metrics/HistogramBuckets.cs b/src/OpenTelemetry/Metrics/HistogramBuckets.cs index 438f38284ef..11c3f2512b1 100644 --- a/src/OpenTelemetry/Metrics/HistogramBuckets.cs +++ b/src/OpenTelemetry/Metrics/HistogramBuckets.cs @@ -65,7 +65,7 @@ static Bucket ConstructBalancedTree(double[] values, int min, int max) { Index = median, UpperBoundInclusive = values[median], - LowerBoundExclusive = median > 0 ? values[median - 1] : double.MinValue, + LowerBoundExclusive = median > 0 ? values[median - 1] : double.NegativeInfinity, Left = ConstructBalancedTree(values, min, median), Right = ConstructBalancedTree(values, median + 1, max), }; From f2a20f7f8ed332ed130914dfc3694c2480d49581 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Tue, 5 Jul 2022 13:11:53 -0700 Subject: [PATCH 22/58] update order of operations when testing with large MaxValue this would end up having same value bounds --- test/OpenTelemetry.Tests.Stress.Metrics/Program.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs b/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs index cecfc984931..eba957d42f5 100644 --- a/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs +++ b/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs @@ -26,6 +26,7 @@ public partial class Program { private const int ArraySize = 49; private const int MaxValue = 1000; + private const int MaxValue = int.MaxValue; private static readonly Meter TestMeter = new(Utils.GetCurrentMethodName()); private static readonly string[] DimensionValues = new string[ArraySize]; @@ -44,7 +45,7 @@ public static void Main() var bounds = new double[ArraySize]; for (int i = 0; i < bounds.Length; i++) { - bounds[i] = i * MaxValue / bounds.Length; + bounds[i] = i * (MaxValue / bounds.Length); } using var meterProvider = Sdk.CreateMeterProviderBuilder() From c9f3a3d151aab4e5963165cbeb92f44b9788eb72 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Tue, 5 Jul 2022 14:38:31 -0700 Subject: [PATCH 23/58] remove stress test change --- .../Program.cs | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs b/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs index eba957d42f5..8838a1854c9 100644 --- a/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs +++ b/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs @@ -24,16 +24,18 @@ namespace OpenTelemetry.Tests.Stress; public partial class Program { - private const int ArraySize = 49; - private const int MaxValue = 1000; - private const int MaxValue = int.MaxValue; + private const int ArraySize = 10; + + // Note: Uncomment the below line if you want to run Histogram stress test + // private const int MaxHistogramMeasurement = 1000; private static readonly Meter TestMeter = new(Utils.GetCurrentMethodName()); + private static readonly Counter TestCounter = TestMeter.CreateCounter("TestCounter"); private static readonly string[] DimensionValues = new string[ArraySize]; private static readonly ThreadLocal ThreadLocalRandom = new(() => new Random()); - private static readonly Counter TestCounter = TestMeter.CreateCounter("TestCounter"); - private static readonly Histogram TestHistogram = TestMeter.CreateHistogram("TestHistogram"); + // Note: Uncomment the below line if you want to run Histogram stress test + // private static readonly Histogram TestHistogram = TestMeter.CreateHistogram("TestHistogram"); public static void Main() { @@ -42,12 +44,6 @@ public static void Main() DimensionValues[i] = $"DimValue{i}"; } - var bounds = new double[ArraySize]; - for (int i = 0; i < bounds.Length; i++) - { - bounds[i] = i * (MaxValue / bounds.Length); - } - using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(TestMeter.Name) .AddPrometheusExporter(options => @@ -56,7 +52,6 @@ public static void Main() options.HttpListenerPrefixes = new string[] { $"http://localhost:9185/" }; options.ScrapeResponseCacheDurationMilliseconds = 0; }) - .AddView(TestHistogram.Name, new ExplicitBucketHistogramConfiguration() { Boundaries = bounds }) .Build(); Stress(prometheusPort: 9184); @@ -66,12 +61,22 @@ public static void Main() protected static void Run() { var random = ThreadLocalRandom.Value; - /*TestCounter.Add( + TestCounter.Add( 100, new("DimName1", DimensionValues[random.Next(0, ArraySize)]), new("DimName2", DimensionValues[random.Next(0, ArraySize)]), - new("DimName3", DimensionValues[random.Next(0, ArraySize)]));*/ - - TestHistogram.Record(random.Next(MaxValue)); + new("DimName3", DimensionValues[random.Next(0, ArraySize)])); } + + // Note: Uncomment the below lines if you want to run Histogram stress test + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // protected static void Run() + // { + // var random = ThreadLocalRandom.Value; + // TestHistogram.Record( + // random.Next(MaxHistogramMeasurement), + // new("DimName1", DimensionValues[random.Next(0, ArraySize)]), + // new("DimName2", DimensionValues[random.Next(0, ArraySize)]), + // new("DimName3", DimensionValues[random.Next(0, ArraySize)])); + // } } From a201b86469901438b939fcb326618022f4b6bf51 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Tue, 5 Jul 2022 15:49:59 -0700 Subject: [PATCH 24/58] ci From d0d92a773d902734f6ac2a51260f3290f320c603 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Wed, 6 Jul 2022 12:39:48 -0700 Subject: [PATCH 25/58] Add histogram binary mode tests --- .../Metrics/AggregatorTest.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/OpenTelemetry.Tests/Metrics/AggregatorTest.cs b/test/OpenTelemetry.Tests/Metrics/AggregatorTest.cs index 9d40a3bd387..8bc3ab64b57 100644 --- a/test/OpenTelemetry.Tests/Metrics/AggregatorTest.cs +++ b/test/OpenTelemetry.Tests/Metrics/AggregatorTest.cs @@ -104,6 +104,34 @@ public void HistogramDistributeToAllBucketsCustom() Assert.Equal(boundaries.Length + 1, actualCount); } + [Fact] + public void HistogramBinaryBucketTest() + { + // Arrange + var boundaries = new double[HistogramBuckets.DefaultHistogramCountForBinarySearch]; + for (var i = 0; i < boundaries.Length; i++) + { + boundaries[i] = i; + } + + var histogramPoint = new MetricPoint(this.aggregatorStore, AggregationType.Histogram, null, null, boundaries); + + // Act + histogramPoint.Update(-1); + for (var i = 0.5; i < boundaries.Length; i++) + { + histogramPoint.Update(i); + } + + histogramPoint.TakeSnapshot(true); + + // Assert + foreach (var histogramMeasurement in histogramPoint.GetHistogramBuckets()) + { + Assert.Equal(1, histogramMeasurement.BucketCount); + } + } + [Fact] public void HistogramWithOnlySumCount() { From 7f8d32b605aed89128d1cb0375a4a9f07dded822 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Wed, 6 Jul 2022 13:17:20 -0700 Subject: [PATCH 26/58] ci From dbc5f3e5c2a1e2d5e9c22c1b7f5f1afa362ddf11 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Mon, 11 Jul 2022 15:59:49 -0700 Subject: [PATCH 27/58] pr review changes --- src/OpenTelemetry/CHANGELOG.md | 2 +- src/OpenTelemetry/Metrics/HistogramBuckets.cs | 24 +++++++++---------- .../Metrics/AggregatorTest.cs | 16 +++++++++++-- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 31fa3125838..9a557defdc2 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -* Use binary search for histograms with a large amount of buckets. +* Use binary search for histograms with 50 or more supplied boundaries. ([#3252](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3252)) * `TracerProviderSDK` modified for spans with remote parent. For such spans diff --git a/src/OpenTelemetry/Metrics/HistogramBuckets.cs b/src/OpenTelemetry/Metrics/HistogramBuckets.cs index 11c3f2512b1..d45379fbc84 100644 --- a/src/OpenTelemetry/Metrics/HistogramBuckets.cs +++ b/src/OpenTelemetry/Metrics/HistogramBuckets.cs @@ -26,7 +26,7 @@ namespace OpenTelemetry.Metrics // Note: Does not implement IEnumerable<> to prevent accidental boxing. public class HistogramBuckets { - internal const int DefaultHistogramCountForBinarySearch = 50; + internal const int DefaultBoundaryCountForBinarySearch = 50; internal readonly double[] ExplicitBounds; @@ -40,7 +40,7 @@ public class HistogramBuckets internal int IsCriticalSectionOccupied = 0; - private readonly Bucket root; + private readonly BucketLookupNode bucketLookupTreeRoot; private readonly Func findHistogramBucketIndex; @@ -48,12 +48,12 @@ internal HistogramBuckets(double[] explicitBounds) { this.ExplicitBounds = explicitBounds; this.findHistogramBucketIndex = this.FindBucketIndexLinear; - if (explicitBounds != null && explicitBounds.Length >= DefaultHistogramCountForBinarySearch) + if (explicitBounds != null && explicitBounds.Length >= DefaultBoundaryCountForBinarySearch) { - this.root = ConstructBalancedTree(explicitBounds, 0, explicitBounds.Length); + this.bucketLookupTreeRoot = ConstructBalancedBST(explicitBounds, 0, explicitBounds.Length); this.findHistogramBucketIndex = this.FindBucketIndexBinary; - static Bucket ConstructBalancedTree(double[] values, int min, int max) + static BucketLookupNode ConstructBalancedBST(double[] values, int min, int max) { if (min == max) { @@ -61,13 +61,13 @@ static Bucket ConstructBalancedTree(double[] values, int min, int max) } int median = min + ((max - min) / 2); - return new Bucket + return new BucketLookupNode { Index = median, UpperBoundInclusive = values[median], LowerBoundExclusive = median > 0 ? values[median - 1] : double.NegativeInfinity, - Left = ConstructBalancedTree(values, min, median), - Right = ConstructBalancedTree(values, median + 1, max), + Left = ConstructBalancedBST(values, min, median), + Right = ConstructBalancedBST(values, median + 1, max), }; } } @@ -99,7 +99,7 @@ internal int FindBucketIndex(double value) [MethodImpl(MethodImplOptions.AggressiveInlining)] internal int FindBucketIndexBinary(double value) { - Bucket current = this.root; + BucketLookupNode current = this.bucketLookupTreeRoot; Debug.Assert(current != null, "Bucket root was null."); @@ -187,7 +187,7 @@ public bool MoveNext() } } - private sealed class Bucket + private sealed class BucketLookupNode { public double UpperBoundInclusive { get; set; } @@ -195,9 +195,9 @@ private sealed class Bucket public int Index { get; set; } - public Bucket Left { get; set; } + public BucketLookupNode Left { get; set; } - public Bucket Right { get; set; } + public BucketLookupNode Right { get; set; } } } } diff --git a/test/OpenTelemetry.Tests/Metrics/AggregatorTest.cs b/test/OpenTelemetry.Tests/Metrics/AggregatorTest.cs index 8bc3ab64b57..0b598d4fa34 100644 --- a/test/OpenTelemetry.Tests/Metrics/AggregatorTest.cs +++ b/test/OpenTelemetry.Tests/Metrics/AggregatorTest.cs @@ -108,7 +108,8 @@ public void HistogramDistributeToAllBucketsCustom() public void HistogramBinaryBucketTest() { // Arrange - var boundaries = new double[HistogramBuckets.DefaultHistogramCountForBinarySearch]; + // Bounds = (-Inf, 0] (0, 1], ... (49, +Inf) + var boundaries = new double[HistogramBuckets.DefaultBoundaryCountForBinarySearch]; for (var i = 0; i < boundaries.Length; i++) { boundaries[i] = i; @@ -118,6 +119,8 @@ public void HistogramBinaryBucketTest() // Act histogramPoint.Update(-1); + histogramPoint.Update(boundaries[0]); + histogramPoint.Update(boundaries[boundaries.Length - 1]); for (var i = 0.5; i < boundaries.Length; i++) { histogramPoint.Update(i); @@ -126,9 +129,18 @@ public void HistogramBinaryBucketTest() histogramPoint.TakeSnapshot(true); // Assert + var index = 0; foreach (var histogramMeasurement in histogramPoint.GetHistogramBuckets()) { - Assert.Equal(1, histogramMeasurement.BucketCount); + var expectedCount = 1; + + if (index == 0 || index == boundaries.Length - 1) + { + expectedCount = 2; + } + + Assert.Equal(expectedCount, histogramMeasurement.BucketCount); + index++; } } From 370fb1afb479546869e5084ace55423a416103cd Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Tue, 12 Jul 2022 12:11:44 -0700 Subject: [PATCH 28/58] allocated column - hist benchmark --- .../Benchmarks/Metrics/HistogramBenchmarks.cs | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/test/Benchmarks/Metrics/HistogramBenchmarks.cs b/test/Benchmarks/Metrics/HistogramBenchmarks.cs index fdab0386ac3..e9a6b94d39d 100644 --- a/test/Benchmarks/Metrics/HistogramBenchmarks.cs +++ b/test/Benchmarks/Metrics/HistogramBenchmarks.cs @@ -31,28 +31,28 @@ DefaultJob : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT -| Method | BoundCount | Mean | Error | StdDev | -|---------------------------- |----------- |----------:|----------:|----------:| -| HistogramHotPath | 10 | 55.07 ns | 0.664 ns | 1.091 ns | -| HistogramWith1LabelHotPath | 10 | 108.66 ns | 1.324 ns | 1.174 ns | -| HistogramWith3LabelsHotPath | 10 | 193.79 ns | 3.261 ns | 3.349 ns | -| HistogramWith5LabelsHotPath | 10 | 279.44 ns | 4.608 ns | 3.848 ns | -| HistogramWith7LabelsHotPath | 10 | 334.28 ns | 6.650 ns | 5.895 ns | -| HistogramHotPath | 49 | 68.27 ns | 0.744 ns | 0.581 ns | -| HistogramWith1LabelHotPath | 49 | 125.55 ns | 2.265 ns | 2.518 ns | -| HistogramWith3LabelsHotPath | 49 | 207.95 ns | 4.023 ns | 3.951 ns | -| HistogramWith5LabelsHotPath | 49 | 293.45 ns | 5.689 ns | 5.842 ns | -| HistogramWith7LabelsHotPath | 49 | 362.19 ns | 5.610 ns | 6.003 ns | -| HistogramHotPath | 50 | 69.64 ns | 1.422 ns | 1.330 ns | -| HistogramWith1LabelHotPath | 50 | 118.15 ns | 2.040 ns | 1.908 ns | -| HistogramWith3LabelsHotPath | 50 | 250.31 ns | 4.617 ns | 9.326 ns | -| HistogramWith5LabelsHotPath | 50 | 335.31 ns | 3.904 ns | 3.461 ns | -| HistogramWith7LabelsHotPath | 50 | 398.02 ns | 6.815 ns | 6.374 ns | -| HistogramHotPath | 1000 | 94.05 ns | 1.890 ns | 2.100 ns | -| HistogramWith1LabelHotPath | 1000 | 148.57 ns | 2.055 ns | 1.822 ns | -| HistogramWith3LabelsHotPath | 1000 | 661.78 ns | 11.599 ns | 20.314 ns | -| HistogramWith5LabelsHotPath | 1000 | 761.54 ns | 15.049 ns | 16.727 ns | -| HistogramWith7LabelsHotPath | 1000 | 830.14 ns | 16.063 ns | 17.853 ns | +| Method | BoundCount | Mean | Error | StdDev | Allocated | +|---------------------------- |----------- |----------:|----------:|----------:|----------:| +| HistogramHotPath | 10 | 55.07 ns | 0.664 ns | 1.091 ns | - | +| HistogramWith1LabelHotPath | 10 | 108.66 ns | 1.324 ns | 1.174 ns | - | +| HistogramWith3LabelsHotPath | 10 | 193.79 ns | 3.261 ns | 3.349 ns | - | +| HistogramWith5LabelsHotPath | 10 | 279.44 ns | 4.608 ns | 3.848 ns | - | +| HistogramWith7LabelsHotPath | 10 | 334.28 ns | 6.650 ns | 5.895 ns | - | +| HistogramHotPath | 49 | 68.27 ns | 0.744 ns | 0.581 ns | - | +| HistogramWith1LabelHotPath | 49 | 125.55 ns | 2.265 ns | 2.518 ns | - | +| HistogramWith3LabelsHotPath | 49 | 207.95 ns | 4.023 ns | 3.951 ns | - | +| HistogramWith5LabelsHotPath | 49 | 293.45 ns | 5.689 ns | 5.842 ns | - | +| HistogramWith7LabelsHotPath | 49 | 362.19 ns | 5.610 ns | 6.003 ns | - | +| HistogramHotPath | 50 | 69.64 ns | 1.422 ns | 1.330 ns | - | +| HistogramWith1LabelHotPath | 50 | 118.15 ns | 2.040 ns | 1.908 ns | - | +| HistogramWith3LabelsHotPath | 50 | 250.31 ns | 4.617 ns | 9.326 ns | - | +| HistogramWith5LabelsHotPath | 50 | 335.31 ns | 3.904 ns | 3.461 ns | - | +| HistogramWith7LabelsHotPath | 50 | 398.02 ns | 6.815 ns | 6.374 ns | - | +| HistogramHotPath | 1000 | 94.05 ns | 1.890 ns | 2.100 ns | - | +| HistogramWith1LabelHotPath | 1000 | 148.57 ns | 2.055 ns | 1.822 ns | - | +| HistogramWith3LabelsHotPath | 1000 | 661.78 ns | 11.599 ns | 20.314 ns | - | +| HistogramWith5LabelsHotPath | 1000 | 761.54 ns | 15.049 ns | 16.727 ns | - | +| HistogramWith7LabelsHotPath | 1000 | 830.14 ns | 16.063 ns | 17.853 ns | - | */ namespace Benchmarks.Metrics From 3b2f762ed4a8dbccd2e767ef2d75051b2540fbc0 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Tue, 26 Jul 2022 11:10:00 -0700 Subject: [PATCH 29/58] CI From 4566b92f473fb05c2f64742e362dfd7e9b516a0b Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Tue, 26 Jul 2022 16:23:31 -0700 Subject: [PATCH 30/58] Exponential Bucket Histogram - part 6 (#3494) --- src/OpenTelemetry/Internal/MathHelper.cs | 12 -- .../Metrics/ExponentialBucketHistogram.cs | 57 +++++- .../Metrics/ExponentialBucketHistogramTest.cs | 167 ++++++++++++------ .../Shared/IEEE754Double.cs | 103 +++++++++++ 4 files changed, 274 insertions(+), 65 deletions(-) create mode 100644 test/OpenTelemetry.Tests/Shared/IEEE754Double.cs diff --git a/src/OpenTelemetry/Internal/MathHelper.cs b/src/OpenTelemetry/Internal/MathHelper.cs index 043fcac6654..a3ffa316e8b 100644 --- a/src/OpenTelemetry/Internal/MathHelper.cs +++ b/src/OpenTelemetry/Internal/MathHelper.cs @@ -14,7 +14,6 @@ // limitations under the License. // -using System; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -136,15 +135,4 @@ public static bool IsFinite(double value) return !double.IsInfinity(value) && !double.IsNaN(value); #endif } - - public static string DoubleToString(double value) - { - var repr = Convert.ToString(BitConverter.DoubleToInt64Bits(value), 2); - return new string('0', 64 - repr.Length) + repr; - } - - public static double DoubleFromString(string value) - { - return BitConverter.Int64BitsToDouble(Convert.ToInt64(value, 2)); - } } diff --git a/src/OpenTelemetry/Metrics/ExponentialBucketHistogram.cs b/src/OpenTelemetry/Metrics/ExponentialBucketHistogram.cs index 0c2f2843244..f1670d3ba87 100644 --- a/src/OpenTelemetry/Metrics/ExponentialBucketHistogram.cs +++ b/src/OpenTelemetry/Metrics/ExponentialBucketHistogram.cs @@ -37,7 +37,49 @@ internal class ExponentialBucketHistogram public ExponentialBucketHistogram(int scale, int maxBuckets = 160) { - Guard.ThrowIfOutOfRange(scale, min: -20, max: 20); // TODO: calculate the actual range + /* + The following table is calculated based on [ MapToIndex(double.Epsilon), MapToIndex(double.MaxValue) ]: + + | Scale | Index Range | + | ----- | ------------------------- | + | < -11 | [-1, 0] | + | -11 | [-1, 0] | + | -10 | [-2, 0] | + | -9 | [-3, 1] | + | -8 | [-5, 3] | + | -7 | [-9, 7] | + | -6 | [-17, 15] | + | -5 | [-34, 31] | + | -4 | [-68, 63] | + | -3 | [-135, 127] | + | -2 | [-269, 255] | + | -1 | [-538, 511] | + | 0 | [-1075, 1023] | + | 1 | [-2149, 2047] | + | 2 | [-4297, 4095] | + | 3 | [-8593, 8191] | + | 4 | [-17185, 16383] | + | 5 | [-34369, 32767] | + | 6 | [-68737, 65535] | + | 7 | [-137473, 131071] | + | 8 | [-274945, 262143] | + | 9 | [-549889, 524287] | + | 10 | [-1099777, 1048575] | + | 11 | [-2199553, 2097151] | + | 12 | [-4399105, 4194303] | + | 13 | [-8798209, 8388607] | + | 14 | [-17596417, 16777215] | + | 15 | [-35192833, 33554431] | + | 16 | [-70385665, 67108863] | + | 17 | [-140771329, 134217727] | + | 18 | [-281542657, 268435455] | + | 19 | [-563085313, 536870911] | + | 20 | [-1126170625, 1073741823] | + | 21 | [underflow, 2147483647] | + | > 21 | [underflow, overflow] | + */ + Guard.ThrowIfOutOfRange(scale, min: -11, max: 20); + Guard.ThrowIfOutOfRange(maxBuckets, min: 1); this.Scale = scale; @@ -88,17 +130,26 @@ public int MapToIndex(double value) Debug.Assert(value != 0, "IEEE-754 zero values should be handled by ZeroCount."); Debug.Assert(!double.IsNegative(value), "IEEE-754 negative values should be normalized before calling this method."); + var bits = BitConverter.DoubleToInt64Bits(value); + var fraction = bits & 0xFFFFFFFFFFFFFL /* fraction mask */; + if (this.Scale > 0) { + // TODO: do we really need this given the lookup table is needed for scale>0 anyways? + if (fraction == 0) + { + var exp = (int)((bits & 0x7FF0000000000000L /* exponent mask */) >> 52 /* fraction width */); + return ((exp - 1023 /* exponent bias */) << this.Scale) - 1; + } + // TODO: due to precision issue, the values that are close to the bucket // boundaries should be closely examined to avoid off-by-one. + return (int)Math.Ceiling(Math.Log(value) * this.scalingFactor) - 1; } else { - var bits = BitConverter.DoubleToInt64Bits(value); var exp = (int)((bits & 0x7FF0000000000000L /* exponent mask */) >> 52 /* fraction width */); - var fraction = bits & 0xFFFFFFFFFFFFFL /* fraction mask */; if (exp == 0) { diff --git a/test/OpenTelemetry.Tests/Metrics/ExponentialBucketHistogramTest.cs b/test/OpenTelemetry.Tests/Metrics/ExponentialBucketHistogramTest.cs index 7a1ea4708db..1244449f429 100644 --- a/test/OpenTelemetry.Tests/Metrics/ExponentialBucketHistogramTest.cs +++ b/test/OpenTelemetry.Tests/Metrics/ExponentialBucketHistogramTest.cs @@ -17,6 +17,7 @@ #if NET6_0_OR_GREATER using System; +using OpenTelemetry.Tests; using Xunit; namespace OpenTelemetry.Metrics.Tests; @@ -40,47 +41,123 @@ public void IndexLookup() // bucket[3]: (8, 16] // ... - var histogram_scale0 = new ExponentialBucketHistogram(0); - - Assert.Equal(-1075, histogram_scale0.MapToIndex(double.Epsilon)); - - Assert.Equal(-1074, histogram_scale0.MapToIndex(double.Epsilon * 2)); - - Assert.Equal(-1073, histogram_scale0.MapToIndex(double.Epsilon * 3)); - Assert.Equal(-1073, histogram_scale0.MapToIndex(double.Epsilon * 4)); - - Assert.Equal(-1072, histogram_scale0.MapToIndex(double.Epsilon * 5)); - Assert.Equal(-1072, histogram_scale0.MapToIndex(double.Epsilon * 6)); - Assert.Equal(-1072, histogram_scale0.MapToIndex(double.Epsilon * 7)); - Assert.Equal(-1072, histogram_scale0.MapToIndex(double.Epsilon * 8)); - - Assert.Equal(-1023, histogram_scale0.MapToIndex(2.2250738585072009E-308)); - Assert.Equal(-1023, histogram_scale0.MapToIndex(2.2250738585072014E-308)); - - Assert.Equal(-3, histogram_scale0.MapToIndex(0.25)); - - Assert.Equal(-2, histogram_scale0.MapToIndex(0.375)); - Assert.Equal(-2, histogram_scale0.MapToIndex(0.5)); - - Assert.Equal(-1, histogram_scale0.MapToIndex(0.75)); - Assert.Equal(-1, histogram_scale0.MapToIndex(1)); - - Assert.Equal(0, histogram_scale0.MapToIndex(1.5)); - Assert.Equal(0, histogram_scale0.MapToIndex(2)); - - Assert.Equal(1, histogram_scale0.MapToIndex(3)); - Assert.Equal(1, histogram_scale0.MapToIndex(4)); - - Assert.Equal(2, histogram_scale0.MapToIndex(5)); - Assert.Equal(2, histogram_scale0.MapToIndex(6)); - Assert.Equal(2, histogram_scale0.MapToIndex(7)); - Assert.Equal(2, histogram_scale0.MapToIndex(8)); + var histogram = new ExponentialBucketHistogram(0); + + Assert.Equal(-1075, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000001"))); // ~4.9406564584124654E-324 (minimum subnormal positive, double.Epsilon) + Assert.Equal(-1074, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000010"))); // double.Epsilon * 2 + Assert.Equal(-1073, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000011"))); // double.Epsilon * 3 + Assert.Equal(-1073, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000100"))); // double.Epsilon * 4 + Assert.Equal(-1072, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000101"))); // double.Epsilon * 5 + Assert.Equal(-1072, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000110"))); // double.Epsilon * 6 + Assert.Equal(-1072, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000111"))); // double.Epsilon * 7 + Assert.Equal(-1072, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000001000"))); // double.Epsilon * 8 + Assert.Equal(-1024, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000000"))); // ~1.1125369292536007E-308 + Assert.Equal(-1023, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000001"))); // ~1.112536929253601E-308 + Assert.Equal(-1023, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1111111111111111111111111111111111111111111111111111"))); // ~2.2250738585072009E-308 (maximum subnormal positive) + Assert.Equal(-1023, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000000"))); // ~2.2250738585072014E-308 (minimum normal positive) + Assert.Equal(-1022, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000001"))); // ~2.225073858507202E-308 + Assert.Equal(-3, histogram.MapToIndex(IEEE754Double.FromString("0 01111111101 0000000000000000000000000000000000000000000000000000"))); // 0.25 + Assert.Equal(-2, histogram.MapToIndex(IEEE754Double.FromString("0 01111111110 0000000000000000000000000000000000000000000000000000"))); // 0.5 + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111110 0000000000000000000000000000000000000000000000000001"))); // ~0.5000000000000001 + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111111 0000000000000000000000000000000000000000000000000000"))); // 1 + Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 01111111111 0000000000000000000000000000000000000000000000000001"))); // ~1.0000000000000002 + Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 10000000000 0000000000000000000000000000000000000000000000000000"))); // 2 + Assert.Equal(1, histogram.MapToIndex(IEEE754Double.FromString("0 10000000001 0000000000000000000000000000000000000000000000000000"))); // 4 + Assert.Equal(2, histogram.MapToIndex(IEEE754Double.FromString("0 10000000010 0000000000000000000000000000000000000000000000000000"))); // 8 + Assert.Equal(3, histogram.MapToIndex(IEEE754Double.FromString("0 10000000011 0000000000000000000000000000000000000000000000000000"))); // 16 + Assert.Equal(4, histogram.MapToIndex(IEEE754Double.FromString("0 10000000100 0000000000000000000000000000000000000000000000000000"))); // 32 + Assert.Equal(1022, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 0000000000000000000000000000000000000000000000000000"))); // ~8.98846567431158E+307 + Assert.Equal(1023, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 0000000000000000000000000000000000000000000000000001"))); // ~8.988465674311582E+307 + Assert.Equal(1023, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111110"))); // ~1.7976931348623155E+308 + Assert.Equal(1023, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111111"))); // ~1.7976931348623157E+308 (maximum normal positive, double.MaxValue) + + // An exponential bucket histogram with scale = -1. + // The base is 2 ^ (2 ^ 1) = 4. + // The buckets are: + // + // ... + // bucket[-3]: (1/64, 1/16] + // bucket[-2]: (1/16, 1/4] + // bucket[-1]: (1/4, 1] + // bucket[0]: (1, 4] + // bucket[1]: (4, 16] + // bucket[2]: (16, 64] + // bucket[3]: (64, 256] + // ... - Assert.Equal(3, histogram_scale0.MapToIndex(9)); - Assert.Equal(3, histogram_scale0.MapToIndex(16)); + histogram = new ExponentialBucketHistogram(-1); + + Assert.Equal(-538, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000001"))); // ~4.9406564584124654E-324 (minimum subnormal positive, double.Epsilon) + Assert.Equal(-537, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000010"))); // double.Epsilon * 2 + Assert.Equal(-537, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000011"))); // double.Epsilon * 3 + Assert.Equal(-537, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000100"))); // double.Epsilon * 4 + Assert.Equal(-536, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000101"))); // double.Epsilon * 5 + Assert.Equal(-536, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000110"))); // double.Epsilon * 6 + Assert.Equal(-536, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000111"))); // double.Epsilon * 7 + Assert.Equal(-536, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000001000"))); // double.Epsilon * 8 + Assert.Equal(-512, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000000"))); // ~1.1125369292536007E-308 + Assert.Equal(-512, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000001"))); // ~1.112536929253601E-308 + Assert.Equal(-512, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1111111111111111111111111111111111111111111111111111"))); // ~2.2250738585072009E-308 (maximum subnormal positive) + Assert.Equal(-512, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000000"))); // ~2.2250738585072014E-308 (minimum normal positive) + Assert.Equal(-511, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000001"))); // ~2.225073858507202E-308 + Assert.Equal(-2, histogram.MapToIndex(IEEE754Double.FromString("0 01111111101 0000000000000000000000000000000000000000000000000000"))); // 0.25 + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111110 0000000000000000000000000000000000000000000000000000"))); // 0.5 + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111110 0000000000000000000000000000000000000000000000000001"))); // ~0.5000000000000001 + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111111 0000000000000000000000000000000000000000000000000000"))); // 1 + Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 01111111111 0000000000000000000000000000000000000000000000000001"))); // ~1.0000000000000002 + Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 10000000000 0000000000000000000000000000000000000000000000000000"))); // 2 + Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 10000000001 0000000000000000000000000000000000000000000000000000"))); // 4 + Assert.Equal(1, histogram.MapToIndex(IEEE754Double.FromString("0 10000000010 0000000000000000000000000000000000000000000000000000"))); // 8 + Assert.Equal(1, histogram.MapToIndex(IEEE754Double.FromString("0 10000000011 0000000000000000000000000000000000000000000000000000"))); // 16 + Assert.Equal(2, histogram.MapToIndex(IEEE754Double.FromString("0 10000000100 0000000000000000000000000000000000000000000000000000"))); // 32 + Assert.Equal(511, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 0000000000000000000000000000000000000000000000000000"))); // ~8.98846567431158E+307 + Assert.Equal(511, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 0000000000000000000000000000000000000000000000000001"))); // ~8.988465674311582E+307 + Assert.Equal(511, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111110"))); // ~1.7976931348623155E+308 + Assert.Equal(511, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111111"))); // ~1.7976931348623157E+308 (maximum normal positive, double.MaxValue) + + // An exponential bucket histogram with scale = -2. + // The base is 2 ^ (2 ^ 2) = 16. + // The buckets are: + // + // ... + // bucket[-3]: (1/4096, 1/256] + // bucket[-2]: (1/256, 1/16] + // bucket[-1]: (1/16, 1] + // bucket[0]: (1, 16] + // bucket[1]: (16, 256] + // bucket[2]: (256, 4096] + // bucket[3]: (4096, 65536] + // ... - Assert.Equal(4, histogram_scale0.MapToIndex(17)); - Assert.Equal(4, histogram_scale0.MapToIndex(32)); + histogram = new ExponentialBucketHistogram(-2); + + Assert.Equal(-269, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000001"))); // ~4.9406564584124654E-324 (minimum subnormal positive, double.Epsilon) + Assert.Equal(-269, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000010"))); // double.Epsilon * 2 + Assert.Equal(-269, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000011"))); // double.Epsilon * 3 + Assert.Equal(-269, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000100"))); // double.Epsilon * 4 + Assert.Equal(-268, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000101"))); // double.Epsilon * 5 + Assert.Equal(-268, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000110"))); // double.Epsilon * 6 + Assert.Equal(-268, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000111"))); // double.Epsilon * 7 + Assert.Equal(-268, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000001000"))); // double.Epsilon * 8 + Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000000"))); // ~1.1125369292536007E-308 + Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000001"))); // ~1.112536929253601E-308 + Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1111111111111111111111111111111111111111111111111111"))); // ~2.2250738585072009E-308 (maximum subnormal positive) + Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000000"))); // ~2.2250738585072014E-308 (minimum normal positive) + Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000001"))); // ~2.225073858507202E-308 + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111101 0000000000000000000000000000000000000000000000000000"))); // 0.25 + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111110 0000000000000000000000000000000000000000000000000000"))); // 0.5 + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111110 0000000000000000000000000000000000000000000000000001"))); // ~0.5000000000000001 + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111111 0000000000000000000000000000000000000000000000000000"))); // 1 + Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 01111111111 0000000000000000000000000000000000000000000000000001"))); // ~1.0000000000000002 + Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 10000000000 0000000000000000000000000000000000000000000000000000"))); // 2 + Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 10000000001 0000000000000000000000000000000000000000000000000000"))); // 4 + Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 10000000010 0000000000000000000000000000000000000000000000000000"))); // 8 + Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 10000000011 0000000000000000000000000000000000000000000000000000"))); // 16 + Assert.Equal(1, histogram.MapToIndex(IEEE754Double.FromString("0 10000000100 0000000000000000000000000000000000000000000000000000"))); // 32 + Assert.Equal(255, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 0000000000000000000000000000000000000000000000000000"))); // ~8.98846567431158E+307 + Assert.Equal(255, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 0000000000000000000000000000000000000000000000000001"))); // ~8.988465674311582E+307 + Assert.Equal(255, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111110"))); // ~1.7976931348623155E+308 + Assert.Equal(255, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111111"))); // ~1.7976931348623157E+308 (maximum normal positive, double.MaxValue) // An exponential bucket histogram with scale = 1. // The base is 2 ^ (2 ^ -1) = sqrt(2) = 1.41421356237. @@ -96,17 +173,7 @@ public void IndexLookup() // bucket[3]: (2.82842712474, 4] // ... - var histogram_scale1 = new ExponentialBucketHistogram(1); - - Assert.Equal(-3, histogram_scale1.MapToIndex(0.5)); - - Assert.Equal(-2, histogram_scale1.MapToIndex(0.6)); - - Assert.Equal(-1, histogram_scale1.MapToIndex(1)); - - Assert.Equal(1, histogram_scale1.MapToIndex(2)); - - Assert.Equal(3, histogram_scale1.MapToIndex(4)); + histogram = new ExponentialBucketHistogram(1); } } diff --git a/test/OpenTelemetry.Tests/Shared/IEEE754Double.cs b/test/OpenTelemetry.Tests/Shared/IEEE754Double.cs new file mode 100644 index 00000000000..4ae89faaba8 --- /dev/null +++ b/test/OpenTelemetry.Tests/Shared/IEEE754Double.cs @@ -0,0 +1,103 @@ +// +// 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. +// + +using System; +using System.Runtime.InteropServices; + +namespace OpenTelemetry.Tests; + +[StructLayout(LayoutKind.Explicit)] +public struct IEEE754Double +{ + [FieldOffset(0)] + public double DoubleValue = 0; + + [FieldOffset(0)] + public long LongValue = 0; + + [FieldOffset(0)] + public ulong ULongValue = 0; + + public IEEE754Double(double value) + { + this.DoubleValue = value; + } + + public static implicit operator double(IEEE754Double value) + { + return value.DoubleValue; + } + + public static IEEE754Double operator ++(IEEE754Double value) + { + value.ULongValue++; + return value; + } + + public static IEEE754Double operator --(IEEE754Double value) + { + value.ULongValue--; + return value; + } + + public static IEEE754Double FromDouble(double value) + { + return new IEEE754Double(value); + } + + public static IEEE754Double FromLong(long value) + { + return new IEEE754Double { LongValue = value }; + } + + public static IEEE754Double FromULong(ulong value) + { + return new IEEE754Double { ULongValue = value }; + } + + public static IEEE754Double FromString(string value) + { + return IEEE754Double.FromLong(Convert.ToInt64(value.Replace(" ", string.Empty), 2)); + } + + public override string ToString() + { + Span chars = stackalloc char[66]; + + var bits = this.ULongValue; + var index = chars.Length - 1; + + for (int i = 0; i < 52; i++) + { + chars[index--] = (char)(bits & 0x01 | 0x30); + bits >>= 1; + } + + chars[index--] = ' '; + + for (int i = 0; i < 11; i++) + { + chars[index--] = (char)(bits & 0x01 | 0x30); + bits >>= 1; + } + + chars[index--] = ' '; + + chars[index--] = (char)(bits & 0x01 | 0x30); + + return $"{chars.ToString()} ({this.DoubleValue})"; + } +} From 7f8801a082391b5005530c51caed3d2703b08223 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Wed, 27 Jul 2022 14:52:46 -0700 Subject: [PATCH 31/58] Minor fixes to Stress Tests (#3496) --- test/OpenTelemetry.Tests.Stress/README.md | 47 ++++++++++++++------- test/OpenTelemetry.Tests.Stress/Skeleton.cs | 12 ++---- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/test/OpenTelemetry.Tests.Stress/README.md b/test/OpenTelemetry.Tests.Stress/README.md index 4b7b10ddb77..a22d7b6788f 100644 --- a/test/OpenTelemetry.Tests.Stress/README.md +++ b/test/OpenTelemetry.Tests.Stress/README.md @@ -43,23 +43,32 @@ Running (concurrency = 1), press to stop... The stress test metrics are exposed via [PrometheusExporter](../../src/OpenTelemetry.Exporter.Prometheus/README.md), which can be accessed via -[http://localhost:9184/metrics/](http://localhost:9184/metrics/): +[http://localhost:9184/metrics/](http://localhost:9184/metrics/). -```text -# TYPE Process_NonpagedSystemMemorySize64 gauge -Process_NonpagedSystemMemorySize64 31651 1637385964580 - -# TYPE Process_PagedSystemMemorySize64 gauge -Process_PagedSystemMemorySize64 238672 1637385964580 - -# TYPE Process_PagedMemorySize64 gauge -Process_PagedMemorySize64 16187392 1637385964580 - -# TYPE Process_WorkingSet64 gauge -Process_WorkingSet64 29753344 1637385964580 +Following shows a section of the metrics exposed in prometheus format: -# TYPE Process_VirtualMemorySize64 gauge -Process_VirtualMemorySize64 2204045848576 1637385964580 +```text +# HELP OpenTelemetry_Tests_Stress_Loops The total number of `Run()` invocations that are completed. +# TYPE OpenTelemetry_Tests_Stress_Loops counter +OpenTelemetry_Tests_Stress_Loops 1844902947 1658950184752 + +# HELP OpenTelemetry_Tests_Stress_LoopsPerSecond The rate of `Run()` invocations based on a small sliding window of few hundreds of milliseconds. +# TYPE OpenTelemetry_Tests_Stress_LoopsPerSecond gauge +OpenTelemetry_Tests_Stress_LoopsPerSecond 9007731.132075472 1658950184752 + +# HELP OpenTelemetry_Tests_Stress_CpuCyclesPerLoop The average CPU cycles for each `Run()` invocation, based on a small sliding window of few hundreds of milliseconds. +# TYPE OpenTelemetry_Tests_Stress_CpuCyclesPerLoop gauge +OpenTelemetry_Tests_Stress_CpuCyclesPerLoop 3008 1658950184752 + +# HELP process_runtime_dotnet_gc_collections_count Number of garbage collections that have occurred since process start. +# TYPE process_runtime_dotnet_gc_collections_count counter +process_runtime_dotnet_gc_collections_count{generation="gen2"} 0 1658950184752 +process_runtime_dotnet_gc_collections_count{generation="gen1"} 0 1658950184752 +process_runtime_dotnet_gc_collections_count{generation="gen0"} 0 1658950184752 + +# HELP process_runtime_dotnet_gc_allocations_size_bytes Count of bytes allocated on the managed GC heap since the process start. .NET objects are allocated from this heap. Object allocations from unmanaged languages such as C/C++ do not use this heap. +# TYPE process_runtime_dotnet_gc_allocations_size_bytes counter +process_runtime_dotnet_gc_allocations_size_bytes 5485192 1658950184752 ``` ## Writing your own stress test @@ -92,6 +101,13 @@ Add the [`Skeleton.cs`](./Skeleton.cs) file to your `*.csproj` file: ``` +Add the following packages to the project: + +```shell +dotnet add package OpenTelemetry.Exporter.Prometheus --prerelease +dotnet add package OpenTelemetry.Instrumentation.Runtime --prerelease +``` + Now you are ready to run your own stress test. Some useful notes: @@ -114,3 +130,4 @@ Some useful notes: sliding window of few hundreds of milliseconds. * `CPU Cycles/Loop` represents the average CPU cycles for each `Run()` invocation, based on a small sliding window of few hundreds of milliseconds. +* `Runaway Time` represents the runaway time (seconds) since the test started. diff --git a/test/OpenTelemetry.Tests.Stress/Skeleton.cs b/test/OpenTelemetry.Tests.Stress/Skeleton.cs index adcf5cbbe22..2ff889d21a3 100644 --- a/test/OpenTelemetry.Tests.Stress/Skeleton.cs +++ b/test/OpenTelemetry.Tests.Stress/Skeleton.cs @@ -63,11 +63,8 @@ public static void Stress(int concurrency = 0, int prometheusPort = 0) () => dLoopsPerSecond, description: "The rate of `Run()` invocations based on a small sliding window of few hundreds of milliseconds."); var dCpuCyclesPerLoop = 0D; -#if NET462 - if (Environment.OSVersion.Platform == PlatformID.Win32NT) -#else + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) -#endif { meter.CreateObservableGauge( "OpenTelemetry.Tests.Stress.CpuCyclesPerLoop", @@ -146,7 +143,7 @@ public static void Stress(int concurrency = 0, int prometheusPort = 0) dLoopsPerSecond = (double)nLoops / ((double)watch.ElapsedMilliseconds / 1000.0); dCpuCyclesPerLoop = nLoops == 0 ? 0 : nCpuCycles / nLoops; - output = $"Loops: {cntLoopsTotal:n0}, Loops/Second: {dLoopsPerSecond:n0}, CPU Cycles/Loop: {dCpuCyclesPerLoop:n0}"; + output = $"Loops: {cntLoopsTotal:n0}, Loops/Second: {dLoopsPerSecond:n0}, CPU Cycles/Loop: {dCpuCyclesPerLoop:n0}, RunwayTime (Seconds): {watchForTotal.Elapsed.TotalSeconds:n0} "; Console.Title = output; } }, @@ -169,6 +166,7 @@ public static void Stress(int concurrency = 0, int prometheusPort = 0) var cntCpuCyclesTotal = GetCpuCycles(); var cpuCyclesPerLoopTotal = cntLoopsTotal == 0 ? 0 : cntCpuCyclesTotal / cntLoopsTotal; Console.WriteLine("Stopping the stress test..."); + Console.WriteLine($"* Total Runaway Time (seconds) {watchForTotal.Elapsed.TotalSeconds:n0}"); Console.WriteLine($"* Total Loops: {cntLoopsTotal:n0}"); Console.WriteLine($"* Average Loops/Second: {totalLoopsPerSecond:n0}"); Console.WriteLine($"* Average CPU Cycles/Loop: {cpuCyclesPerLoopTotal:n0}"); @@ -180,11 +178,7 @@ public static void Stress(int concurrency = 0, int prometheusPort = 0) private static ulong GetCpuCycles() { -#if NET462 - if (Environment.OSVersion.Platform != PlatformID.Win32NT) -#else if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) -#endif { return 0; } From 16f1e3f082b93aa2be51f6bac0b37926cc6fbd47 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Wed, 27 Jul 2022 15:27:44 -0700 Subject: [PATCH 32/58] Asp.Net Core Unit test [Activity started in middleware is modified by instrumentation] (#3490) --- .../BasicTests.cs | 56 +++++++++++++++++ .../ActivityMiddleware.cs | 61 +++++++++++++++++++ test/TestApp.AspNetCore.3.1/Startup.cs | 3 + .../ActivityMiddleware.cs | 61 +++++++++++++++++++ test/TestApp.AspNetCore.6.0/Startup.cs | 3 + 5 files changed, 184 insertions(+) create mode 100644 test/TestApp.AspNetCore.3.1/ActivityMiddleware.cs create mode 100644 test/TestApp.AspNetCore.6.0/ActivityMiddleware.cs diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs index 03dc2b9d6e3..208b12da777 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs @@ -550,6 +550,39 @@ void ConfigureTestServices(IServiceCollection services) Assert.Equal(shouldEnrichBeCalled, enrichCalled); } + [Fact(Skip = "Changes pending on instrumentation")] + public async Task ActivitiesStartedInMiddlewareShouldNotBeUpdatedByInstrumentation() + { + var exportedItems = new List(); + + var activitySourceName = "TestMiddlewareActivitySource"; + var activityName = "TestMiddlewareActivity"; + + // Arrange + using (var client = this.factory + .WithWebHostBuilder(builder => + builder.ConfigureTestServices((IServiceCollection services) => + { + services.AddSingleton(new TestActivityMiddlewareImpl(activitySourceName, activityName)); + services.AddOpenTelemetryTracing((builder) => builder.AddAspNetCoreInstrumentation() + .AddSource(activitySourceName) + .AddInMemoryExporter(exportedItems)); + })) + .CreateClient()) + { + var response = await client.GetAsync("/api/values/2"); + response.EnsureSuccessStatusCode(); + WaitForActivityExport(exportedItems, 2); + } + + Assert.Equal(2, exportedItems.Count); + + var middlewareActivity = exportedItems[0]; + + // Middleware activity name should not be changed + Assert.Equal(activityName, middlewareActivity.DisplayName); + } + public void Dispose() { this.tracerProvider?.Dispose(); @@ -661,5 +694,28 @@ public override void OnStopActivity(Activity activity, object payload) this.OnStopActivityCallback?.Invoke(activity, payload); } } + + private class TestActivityMiddlewareImpl : ActivityMiddleware.ActivityMiddlewareImpl + { + private ActivitySource activitySource; + private Activity activity; + private string activityName; + + public TestActivityMiddlewareImpl(string activitySourceName, string activityName) + { + this.activitySource = new ActivitySource(activitySourceName); + this.activityName = activityName; + } + + public override void PreProcess(HttpContext context) + { + this.activity = this.activitySource.StartActivity(this.activityName); + } + + public override void PostProcess(HttpContext context) + { + this.activity?.Stop(); + } + } } } diff --git a/test/TestApp.AspNetCore.3.1/ActivityMiddleware.cs b/test/TestApp.AspNetCore.3.1/ActivityMiddleware.cs new file mode 100644 index 00000000000..44c1eff7d14 --- /dev/null +++ b/test/TestApp.AspNetCore.3.1/ActivityMiddleware.cs @@ -0,0 +1,61 @@ +// +// 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. +// + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace TestApp.AspNetCore._3._1 +{ + public class ActivityMiddleware + { + private readonly ActivityMiddlewareImpl impl; + private readonly RequestDelegate next; + + public ActivityMiddleware(RequestDelegate next, ActivityMiddlewareImpl impl) + { + this.next = next; + this.impl = impl; + } + + public async Task InvokeAsync(HttpContext context) + { + if (this.impl != null) + { + this.impl.PreProcess(context); + } + + await this.next(context); + + if (this.impl != null) + { + this.impl.PostProcess(context); + } + } + + public class ActivityMiddlewareImpl + { + public virtual void PreProcess(HttpContext context) + { + // do nothing + } + + public virtual void PostProcess(HttpContext context) + { + // do nothing + } + } + } +} diff --git a/test/TestApp.AspNetCore.3.1/Startup.cs b/test/TestApp.AspNetCore.3.1/Startup.cs index eaf926c8af6..b8d84242aa1 100644 --- a/test/TestApp.AspNetCore.3.1/Startup.cs +++ b/test/TestApp.AspNetCore.3.1/Startup.cs @@ -39,6 +39,8 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(); services.AddSingleton( new CallbackMiddleware.CallbackMiddlewareImpl()); + services.AddSingleton( + new ActivityMiddleware.ActivityMiddlewareImpl()); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -50,6 +52,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) } app.UseMiddleware(); + app.UseMiddleware(); app.UseRouting(); app.UseAuthorization(); diff --git a/test/TestApp.AspNetCore.6.0/ActivityMiddleware.cs b/test/TestApp.AspNetCore.6.0/ActivityMiddleware.cs new file mode 100644 index 00000000000..c45b5e828ab --- /dev/null +++ b/test/TestApp.AspNetCore.6.0/ActivityMiddleware.cs @@ -0,0 +1,61 @@ +// +// 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. +// + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace TestApp.AspNetCore._6._0 +{ + public class ActivityMiddleware + { + private readonly ActivityMiddlewareImpl impl; + private readonly RequestDelegate next; + + public ActivityMiddleware(RequestDelegate next, ActivityMiddlewareImpl impl) + { + this.next = next; + this.impl = impl; + } + + public async Task InvokeAsync(HttpContext context) + { + if (this.impl != null) + { + this.impl.PreProcess(context); + } + + await this.next(context); + + if (this.impl != null) + { + this.impl.PostProcess(context); + } + } + + public class ActivityMiddlewareImpl + { + public virtual void PreProcess(HttpContext context) + { + // Do nothing + } + + public virtual void PostProcess(HttpContext context) + { + // Do nothing + } + } + } +} diff --git a/test/TestApp.AspNetCore.6.0/Startup.cs b/test/TestApp.AspNetCore.6.0/Startup.cs index 1da70536444..988ee74706c 100644 --- a/test/TestApp.AspNetCore.6.0/Startup.cs +++ b/test/TestApp.AspNetCore.6.0/Startup.cs @@ -39,6 +39,8 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(); services.AddSingleton( new CallbackMiddleware.CallbackMiddlewareImpl()); + services.AddSingleton( + new ActivityMiddleware.ActivityMiddlewareImpl()); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -50,6 +52,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) } app.UseMiddleware(); + app.UseMiddleware(); app.UseRouting(); app.UseAuthorization(); From 8854de38400ff504125762ac1603576a554f828c Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 27 Jul 2022 16:21:55 -0700 Subject: [PATCH 33/58] Exponential Bucket Histogram - part 7 (#3499) * hammer out the upper/lower bound of scale * comment --- .../Metrics/ExponentialBucketHistogram.cs | 20 +- .../Metrics/ExponentialBucketHistogramTest.cs | 209 ++++++++++++------ 2 files changed, 161 insertions(+), 68 deletions(-) diff --git a/src/OpenTelemetry/Metrics/ExponentialBucketHistogram.cs b/src/OpenTelemetry/Metrics/ExponentialBucketHistogram.cs index f1670d3ba87..87edd9908b9 100644 --- a/src/OpenTelemetry/Metrics/ExponentialBucketHistogram.cs +++ b/src/OpenTelemetry/Metrics/ExponentialBucketHistogram.cs @@ -35,7 +35,18 @@ internal class ExponentialBucketHistogram private int scale; private double scalingFactor; // 2 ^ scale / log(2) - public ExponentialBucketHistogram(int scale, int maxBuckets = 160) + /// + /// Initializes a new instance of the class. + /// + /// + /// The maximum number of buckets in each of the positive and negative ranges, not counting the special zero bucket. The default value is 160. + /// + public ExponentialBucketHistogram(int maxBuckets = 160) + : this(maxBuckets, 20) + { + } + + internal ExponentialBucketHistogram(int maxBuckets, int scale) { /* The following table is calculated based on [ MapToIndex(double.Epsilon), MapToIndex(double.MaxValue) ]: @@ -80,7 +91,12 @@ The following table is calculated based on [ MapToIndex(double.Epsilon), MapToIn */ Guard.ThrowIfOutOfRange(scale, min: -11, max: 20); - Guard.ThrowIfOutOfRange(maxBuckets, min: 1); + /* + Regardless of the scale, MapToIndex(1) will always be -1, so we need two buckets at minimum: + bucket[-1] = (1/base, 1] + bucket[0] = (1, base] + */ + Guard.ThrowIfOutOfRange(maxBuckets, min: 2); this.Scale = scale; this.PositiveBuckets = new CircularBufferBuckets(maxBuckets); diff --git a/test/OpenTelemetry.Tests/Metrics/ExponentialBucketHistogramTest.cs b/test/OpenTelemetry.Tests/Metrics/ExponentialBucketHistogramTest.cs index 1244449f429..59f80ad0984 100644 --- a/test/OpenTelemetry.Tests/Metrics/ExponentialBucketHistogramTest.cs +++ b/test/OpenTelemetry.Tests/Metrics/ExponentialBucketHistogramTest.cs @@ -27,21 +27,21 @@ public class ExponentialBucketHistogramTest [Fact] public void IndexLookup() { - // An exponential bucket histogram with scale = 0. - // The base is 2 ^ (2 ^ -0) = 2. - // The buckets are: - // - // ... - // bucket[-3]: (1/8, 1/4] - // bucket[-2]: (1/4, 1/2] - // bucket[-1]: (1/2, 1] - // bucket[0]: (1, 2] - // bucket[1]: (2, 4] - // bucket[2]: (4, 8] - // bucket[3]: (8, 16] - // ... - - var histogram = new ExponentialBucketHistogram(0); + /* + An exponential bucket histogram with scale = 0. + The base is 2 ^ (2 ^ 0) = 2. + The buckets are: + ... + bucket[-3]: (1/8, 1/4] + bucket[-2]: (1/4, 1/2] + bucket[-1]: (1/2, 1] + bucket[0]: (1, 2] + bucket[1]: (2, 4] + bucket[2]: (4, 8] + bucket[3]: (8, 16] + ... + */ + var histogram = new ExponentialBucketHistogram(maxBuckets: 2, scale: 0); Assert.Equal(-1075, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000001"))); // ~4.9406564584124654E-324 (minimum subnormal positive, double.Epsilon) Assert.Equal(-1074, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000010"))); // double.Epsilon * 2 @@ -51,10 +51,12 @@ public void IndexLookup() Assert.Equal(-1072, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000110"))); // double.Epsilon * 6 Assert.Equal(-1072, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000111"))); // double.Epsilon * 7 Assert.Equal(-1072, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000001000"))); // double.Epsilon * 8 - Assert.Equal(-1024, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000000"))); // ~1.1125369292536007E-308 + Assert.Equal(-1025, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0100000000000000000000000000000000000000000000000000"))); // ~5.562684646268003E-309 (2 ^ -1024) + Assert.Equal(-1024, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0100000000000000000000000000000000000000000000000001"))); // ~5.56268464626801E-309 + Assert.Equal(-1024, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000000"))); // ~1.1125369292536007E-308 (2 ^ -1023) Assert.Equal(-1023, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000001"))); // ~1.112536929253601E-308 Assert.Equal(-1023, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1111111111111111111111111111111111111111111111111111"))); // ~2.2250738585072009E-308 (maximum subnormal positive) - Assert.Equal(-1023, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000000"))); // ~2.2250738585072014E-308 (minimum normal positive) + Assert.Equal(-1023, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000000"))); // ~2.2250738585072014E-308 (minimum normal positive, 2 ^ -1022) Assert.Equal(-1022, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000001"))); // ~2.225073858507202E-308 Assert.Equal(-3, histogram.MapToIndex(IEEE754Double.FromString("0 01111111101 0000000000000000000000000000000000000000000000000000"))); // 0.25 Assert.Equal(-2, histogram.MapToIndex(IEEE754Double.FromString("0 01111111110 0000000000000000000000000000000000000000000000000000"))); // 0.5 @@ -71,21 +73,21 @@ public void IndexLookup() Assert.Equal(1023, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111110"))); // ~1.7976931348623155E+308 Assert.Equal(1023, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111111"))); // ~1.7976931348623157E+308 (maximum normal positive, double.MaxValue) - // An exponential bucket histogram with scale = -1. - // The base is 2 ^ (2 ^ 1) = 4. - // The buckets are: - // - // ... - // bucket[-3]: (1/64, 1/16] - // bucket[-2]: (1/16, 1/4] - // bucket[-1]: (1/4, 1] - // bucket[0]: (1, 4] - // bucket[1]: (4, 16] - // bucket[2]: (16, 64] - // bucket[3]: (64, 256] - // ... - - histogram = new ExponentialBucketHistogram(-1); + /* + An exponential bucket histogram with scale = -1. + The base is 2 ^ (2 ^ 1) = 4. + The buckets are: + ... + bucket[-3]: (1/64, 1/16] + bucket[-2]: (1/16, 1/4] + bucket[-1]: (1/4, 1] + bucket[0]: (1, 4] + bucket[1]: (4, 16] + bucket[2]: (16, 64] + bucket[3]: (64, 256] + ... + */ + histogram = new ExponentialBucketHistogram(maxBuckets: 2, scale: -1); Assert.Equal(-538, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000001"))); // ~4.9406564584124654E-324 (minimum subnormal positive, double.Epsilon) Assert.Equal(-537, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000010"))); // double.Epsilon * 2 @@ -95,10 +97,12 @@ public void IndexLookup() Assert.Equal(-536, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000110"))); // double.Epsilon * 6 Assert.Equal(-536, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000111"))); // double.Epsilon * 7 Assert.Equal(-536, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000001000"))); // double.Epsilon * 8 - Assert.Equal(-512, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000000"))); // ~1.1125369292536007E-308 + Assert.Equal(-513, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0100000000000000000000000000000000000000000000000000"))); // ~5.562684646268003E-309 (2 ^ -1024) + Assert.Equal(-512, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0100000000000000000000000000000000000000000000000001"))); // ~5.56268464626801E-309 + Assert.Equal(-512, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000000"))); // ~1.1125369292536007E-308 (2 ^ -1023) Assert.Equal(-512, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000001"))); // ~1.112536929253601E-308 Assert.Equal(-512, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1111111111111111111111111111111111111111111111111111"))); // ~2.2250738585072009E-308 (maximum subnormal positive) - Assert.Equal(-512, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000000"))); // ~2.2250738585072014E-308 (minimum normal positive) + Assert.Equal(-512, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000000"))); // ~2.2250738585072014E-308 (minimum normal positive, 2 ^ -1022) Assert.Equal(-511, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000001"))); // ~2.225073858507202E-308 Assert.Equal(-2, histogram.MapToIndex(IEEE754Double.FromString("0 01111111101 0000000000000000000000000000000000000000000000000000"))); // 0.25 Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111110 0000000000000000000000000000000000000000000000000000"))); // 0.5 @@ -115,21 +119,21 @@ public void IndexLookup() Assert.Equal(511, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111110"))); // ~1.7976931348623155E+308 Assert.Equal(511, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111111"))); // ~1.7976931348623157E+308 (maximum normal positive, double.MaxValue) - // An exponential bucket histogram with scale = -2. - // The base is 2 ^ (2 ^ 2) = 16. - // The buckets are: - // - // ... - // bucket[-3]: (1/4096, 1/256] - // bucket[-2]: (1/256, 1/16] - // bucket[-1]: (1/16, 1] - // bucket[0]: (1, 16] - // bucket[1]: (16, 256] - // bucket[2]: (256, 4096] - // bucket[3]: (4096, 65536] - // ... - - histogram = new ExponentialBucketHistogram(-2); + /* + An exponential bucket histogram with scale = -2. + The base is 2 ^ (2 ^ 2) = 16. + The buckets are: + ... + bucket[-3]: (1/4096, 1/256] + bucket[-2]: (1/256, 1/16] + bucket[-1]: (1/16, 1] + bucket[0]: (1, 16] + bucket[1]: (16, 256] + bucket[2]: (256, 4096] + bucket[3]: (4096, 65536] + ... + */ + histogram = new ExponentialBucketHistogram(maxBuckets: 2, scale: -2); Assert.Equal(-269, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000001"))); // ~4.9406564584124654E-324 (minimum subnormal positive, double.Epsilon) Assert.Equal(-269, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000010"))); // double.Epsilon * 2 @@ -139,10 +143,12 @@ public void IndexLookup() Assert.Equal(-268, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000110"))); // double.Epsilon * 6 Assert.Equal(-268, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000111"))); // double.Epsilon * 7 Assert.Equal(-268, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000001000"))); // double.Epsilon * 8 - Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000000"))); // ~1.1125369292536007E-308 + Assert.Equal(-257, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0100000000000000000000000000000000000000000000000000"))); // ~5.562684646268003E-309 (2 ^ -1024) + Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0100000000000000000000000000000000000000000000000001"))); // ~5.56268464626801E-309 + Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000000"))); // ~1.1125369292536007E-308 (2 ^ -1023) Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000001"))); // ~1.112536929253601E-308 Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1111111111111111111111111111111111111111111111111111"))); // ~2.2250738585072009E-308 (maximum subnormal positive) - Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000000"))); // ~2.2250738585072014E-308 (minimum normal positive) + Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000000"))); // ~2.2250738585072014E-308 (minimum normal positive, 2 ^ -1022) Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000001"))); // ~2.225073858507202E-308 Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111101 0000000000000000000000000000000000000000000000000000"))); // 0.25 Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111110 0000000000000000000000000000000000000000000000000000"))); // 0.5 @@ -159,21 +165,92 @@ public void IndexLookup() Assert.Equal(255, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111110"))); // ~1.7976931348623155E+308 Assert.Equal(255, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111111"))); // ~1.7976931348623157E+308 (maximum normal positive, double.MaxValue) - // An exponential bucket histogram with scale = 1. - // The base is 2 ^ (2 ^ -1) = sqrt(2) = 1.41421356237. - // The buckets are: - // - // ... - // bucket[-3]: (0.35355339059, 1/2] - // bucket[-2]: (1/2, 0.70710678118] - // bucket[-1]: (0.70710678118, 1] - // bucket[0]: (1, 1.41421356237] - // bucket[1]: (1.41421356237, 2] - // bucket[2]: (2, 2.82842712474] - // bucket[3]: (2.82842712474, 4] - // ... - - histogram = new ExponentialBucketHistogram(1); + /* + An exponential bucket histogram with scale = -10. + The base is 2 ^ (2 ^ 10) = 2 ^ 1024 = double.MaxValue + 2 ^ -52 (slightly bigger than double.MaxValue). + The buckets are: + bucket[-2]: [double.Epsilon, 2 ^ -1024] + bucket[-1]: (2 ^ -1024, 1] + bucket[0]: (1, double.MaxValue] + */ + histogram = new ExponentialBucketHistogram(maxBuckets: 2, scale: -10); + + Assert.Equal(-2, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000001"))); // ~4.9406564584124654E-324 (minimum subnormal positive, double.Epsilon) + Assert.Equal(-2, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0100000000000000000000000000000000000000000000000000"))); // ~5.562684646268003E-309 (2 ^ -1024) + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0100000000000000000000000000000000000000000000000001"))); // ~5.56268464626801E-309 + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000001"))); // ~1.1125369292536007E-308 (2 ^ -1023) + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1111111111111111111111111111111111111111111111111111"))); // ~2.2250738585072009E-308 (maximum subnormal positive) + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000000"))); // ~2.2250738585072014E-308 (minimum normal positive, 2 ^ -1022) + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111111 0000000000000000000000000000000000000000000000000000"))); // 1 + Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 01111111111 0000000000000000000000000000000000000000000000000001"))); // ~1.0000000000000002 + Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111111"))); // ~1.7976931348623157E+308 (maximum normal positive, double.MaxValue) + + /* + An exponential bucket histogram with scale = -11. + The base is 2 ^ (2 ^ 11) = 2 ^ 2048 (much bigger than double.MaxValue). + The buckets are: + bucket[-1]: [double.Epsilon, 1] + bucket[0]: (1, double.MaxValue] + */ + histogram = new ExponentialBucketHistogram(maxBuckets: 2, scale: -11); + + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000001"))); // ~4.9406564584124654E-324 (minimum subnormal positive, double.Epsilon) + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1111111111111111111111111111111111111111111111111111"))); // ~2.2250738585072009E-308 (maximum subnormal positive) + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000000"))); // ~2.2250738585072014E-308 (minimum normal positive, 2 ^ -1022) + Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111111 0000000000000000000000000000000000000000000000000000"))); // 1 + Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 01111111111 0000000000000000000000000000000000000000000000000001"))); // ~1.0000000000000002 + Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111111"))); // ~1.7976931348623157E+308 (maximum normal positive, double.MaxValue) + + /* + An exponential bucket histogram with scale = 1. + The base is 2 ^ (2 ^ -1) = sqrt(2) = 1.41421356237. + The buckets are: + ... + bucket[-3]: (0.35355339059, 1/2] + bucket[-2]: (1/2, 0.70710678118] + bucket[-1]: (0.70710678118, 1] + bucket[0]: (1, 1.41421356237] + bucket[1]: (1.41421356237, 2] + bucket[2]: (2, 2.82842712474] + bucket[3]: (2.82842712474, 4] + ... + */ + histogram = new ExponentialBucketHistogram(maxBuckets: 2, scale: 1); + } + + [Fact] + public void InfinityHandling() + { + var histogram = new ExponentialBucketHistogram(maxBuckets: 2, scale: 0); + + histogram.Record(double.PositiveInfinity); + histogram.Record(double.NegativeInfinity); + + Assert.Equal(0, histogram.ZeroCount + histogram.PositiveBuckets.Size + histogram.NegativeBuckets.Size); + } + + [Fact] + public void NaNHandling() + { + var histogram = new ExponentialBucketHistogram(maxBuckets: 2, scale: 0); + + histogram.Record(double.NaN); // NaN (language/runtime native) + histogram.Record(IEEE754Double.FromString("0 11111111111 0000000000000000000000000000000000000000000000000001").DoubleValue); // sNaN on x86/64 and ARM + histogram.Record(IEEE754Double.FromString("0 11111111111 1000000000000000000000000000000000000000000000000001").DoubleValue); // qNaN on x86/64 and ARM + histogram.Record(IEEE754Double.FromString("0 11111111111 1111111111111111111111111111111111111111111111111111").DoubleValue); // NaN (alternative encoding) + + Assert.Equal(0, histogram.ZeroCount + histogram.PositiveBuckets.Size + histogram.NegativeBuckets.Size); + } + + [Fact] + public void ZeroHandling() + { + var histogram = new ExponentialBucketHistogram(maxBuckets: 2, scale: 0); + + histogram.Record(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000000").DoubleValue); // +0 + histogram.Record(IEEE754Double.FromString("1 00000000000 0000000000000000000000000000000000000000000000000000").DoubleValue); // -0 + + Assert.Equal(2, histogram.ZeroCount); } } From 7e65f56ab2a2702581e6d47d9d42ff3ead47413a Mon Sep 17 00:00:00 2001 From: Alan West <3676547+alanwest@users.noreply.github.com> Date: Thu, 28 Jul 2022 07:48:21 -0700 Subject: [PATCH 34/58] OTLP exporter support for limiting activity tags, events, and links (#3376) --- .../Internal/ActivityHelperExtensions.cs | 102 +++++++++++--- .../CHANGELOG.md | 6 + .../EnvironmentVariableConfiguration.cs | 46 ++++++ .../Configuration/SdkConfiguration.cs | 69 +++++++++ .../Implementation/ActivityExtensions.cs | 14 +- src/OpenTelemetry/Internal/TagTransformer.cs | 38 +++-- .../Configuration/SdkConfigurationTests.cs | 133 ++++++++++++++++++ .../OtlpTraceExporterTests.cs | 76 ++++++++++ 8 files changed, 449 insertions(+), 35 deletions(-) create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/EnvironmentVariableConfiguration.cs create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/SdkConfiguration.cs create mode 100644 test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Configuration/SdkConfigurationTests.cs diff --git a/src/OpenTelemetry.Api/Internal/ActivityHelperExtensions.cs b/src/OpenTelemetry.Api/Internal/ActivityHelperExtensions.cs index 6cc238b8c23..e3a2c312072 100644 --- a/src/OpenTelemetry.Api/Internal/ActivityHelperExtensions.cs +++ b/src/OpenTelemetry.Api/Internal/ActivityHelperExtensions.cs @@ -44,7 +44,7 @@ public static bool TryGetStatus(this Activity activity, out StatusCode statusCod ActivityStatusTagEnumerator state = default; - ActivityTagsEnumeratorFactory.Enumerate(activity, ref state); + ActivityTagsEnumeratorFactory.Enumerate(activity, ref state, null); if (!state.StatusCode.HasValue) { @@ -72,7 +72,7 @@ public static object GetTagValue(this Activity activity, string tagName) ActivitySingleTagEnumerator state = new ActivitySingleTagEnumerator(tagName); - ActivityTagsEnumeratorFactory.Enumerate(activity, ref state); + ActivityTagsEnumeratorFactory.Enumerate(activity, ref state, null); return state.Value; } @@ -91,7 +91,7 @@ public static bool TryCheckFirstTag(this Activity activity, string tagName, out ActivityFirstTagEnumerator state = new ActivityFirstTagEnumerator(tagName); - ActivityTagsEnumeratorFactory.Enumerate(activity, ref state); + ActivityTagsEnumeratorFactory.Enumerate(activity, ref state, null); if (state.Value == null) { @@ -109,14 +109,15 @@ public static bool TryCheckFirstTag(this Activity activity, string tagName, out /// The struct implementation to use for the enumeration. /// Activity instance. /// Tag enumerator. + /// Maximum number of tags to enumerate. [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] - public static void EnumerateTags(this Activity activity, ref T tagEnumerator) + public static void EnumerateTags(this Activity activity, ref T tagEnumerator, int? maxTags = null) where T : struct, IActivityEnumerator> { Debug.Assert(activity != null, "Activity should not be null"); - ActivityTagsEnumeratorFactory.Enumerate(activity, ref tagEnumerator); + ActivityTagsEnumeratorFactory.Enumerate(activity, ref tagEnumerator, maxTags); } /// @@ -125,14 +126,15 @@ public static void EnumerateTags(this Activity activity, ref T tagEnumerator) /// The struct implementation to use for the enumeration. /// Activity instance. /// Link enumerator. + /// Maximum number of links to enumerate. [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] - public static void EnumerateLinks(this Activity activity, ref T linkEnumerator) + public static void EnumerateLinks(this Activity activity, ref T linkEnumerator, int? maxLinks = null) where T : struct, IActivityEnumerator { Debug.Assert(activity != null, "Activity should not be null"); - ActivityLinksEnumeratorFactory.Enumerate(activity, ref linkEnumerator); + ActivityLinksEnumeratorFactory.Enumerate(activity, ref linkEnumerator, maxLinks); } /// @@ -141,12 +143,13 @@ public static void EnumerateLinks(this Activity activity, ref T linkEnumerato /// The struct implementation to use for the enumeration. /// ActivityLink instance. /// Tag enumerator. + /// Maximum number of tags to enumerate. [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] - public static void EnumerateTags(this ActivityLink activityLink, ref T tagEnumerator) + public static void EnumerateTags(this ActivityLink activityLink, ref T tagEnumerator, int? maxTags = null) where T : struct, IActivityEnumerator> { - ActivityTagsEnumeratorFactory.Enumerate(activityLink, ref tagEnumerator); + ActivityTagsEnumeratorFactory.Enumerate(activityLink, ref tagEnumerator, maxTags); } /// @@ -155,14 +158,15 @@ public static void EnumerateTags(this ActivityLink activityLink, ref T tagEnu /// The struct implementation to use for the enumeration. /// Activity instance. /// Event enumerator. + /// Maximum number of events to enumerate. [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] - public static void EnumerateEvents(this Activity activity, ref T eventEnumerator) + public static void EnumerateEvents(this Activity activity, ref T eventEnumerator, int? maxEvents = null) where T : struct, IActivityEnumerator { Debug.Assert(activity != null, "Activity should not be null"); - ActivityEventsEnumeratorFactory.Enumerate(activity, ref eventEnumerator); + ActivityEventsEnumeratorFactory.Enumerate(activity, ref eventEnumerator, maxEvents); } /// @@ -171,12 +175,13 @@ public static void EnumerateEvents(this Activity activity, ref T eventEnumera /// The struct implementation to use for the enumeration. /// ActivityEvent instance. /// Tag enumerator. + /// Maximum number of tags to enumerate. [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] - public static void EnumerateTags(this ActivityEvent activityEvent, ref T tagEnumerator) + public static void EnumerateTags(this ActivityEvent activityEvent, ref T tagEnumerator, int? maxTags = null) where T : struct, IActivityEnumerator> { - ActivityTagsEnumeratorFactory.Enumerate(activityEvent, ref tagEnumerator); + ActivityTagsEnumeratorFactory.Enumerate(activityEvent, ref tagEnumerator, maxTags); } private struct ActivitySingleTagEnumerator : IActivityEnumerator> @@ -265,7 +270,7 @@ private static readonly DictionaryEnumerator.AllocationF private static readonly DictionaryEnumerator.ForEachDelegate ForEachTagValueCallbackRef = ForEachTagValueCallback; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Enumerate(Activity activity, ref TState state) + public static void Enumerate(Activity activity, ref TState state, int? maxTags) { var tagObjects = activity.TagObjects; @@ -274,6 +279,12 @@ public static void Enumerate(Activity activity, ref TState state) return; } + if (maxTags.HasValue) + { + SkipAllocationFreeEnumeration(tagObjects, ref state, maxTags.Value); + return; + } + ActivityTagObjectsEnumerator( tagObjects, ref state, @@ -281,7 +292,7 @@ public static void Enumerate(Activity activity, ref TState state) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Enumerate(ActivityLink activityLink, ref TState state) + public static void Enumerate(ActivityLink activityLink, ref TState state, int? maxTags) { var tags = activityLink.Tags; @@ -290,6 +301,12 @@ public static void Enumerate(ActivityLink activityLink, ref TState state) return; } + if (maxTags.HasValue) + { + SkipAllocationFreeEnumeration(tags, ref state, maxTags.Value); + return; + } + ActivityTagsCollectionEnumerator( tags, ref state, @@ -297,7 +314,7 @@ public static void Enumerate(ActivityLink activityLink, ref TState state) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Enumerate(ActivityEvent activityEvent, ref TState state) + public static void Enumerate(ActivityEvent activityEvent, ref TState state, int? maxTags) { var tags = activityEvent.Tags; @@ -306,12 +323,31 @@ public static void Enumerate(ActivityEvent activityEvent, ref TState state) return; } + if (maxTags.HasValue) + { + SkipAllocationFreeEnumeration(tags, ref state, maxTags.Value); + return; + } + ActivityTagsCollectionEnumerator( tags, ref state, ForEachTagValueCallbackRef); } + // TODO: When a limit has been configured an allocation-free enumerator is not used. + // Need to either: + // 1) modify the dynamically generated code to only enumerate up to the max number of items, or + // 2) wait until .NET 7 is released and do this more easily with the new enumerator functions + private static void SkipAllocationFreeEnumeration(IEnumerable> tags, ref TState state, int maxTags) + { + var enumerator = tags.GetEnumerator(); + for (var i = 0; enumerator.MoveNext() && i < maxTags; ++i) + { + state.ForEach(enumerator.Current); + } + } + private static bool ForEachTagValueCallback(ref TState state, KeyValuePair item) => state.ForEach(item); } @@ -328,7 +364,7 @@ private static readonly ListEnumerator.AllocationFreeForEa private static readonly ListEnumerator.ForEachDelegate ForEachLinkCallbackRef = ForEachLinkCallback; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Enumerate(Activity activity, ref TState state) + public static void Enumerate(Activity activity, ref TState state, int? maxLinks) { var activityLinks = activity.Links; @@ -337,6 +373,21 @@ public static void Enumerate(Activity activity, ref TState state) return; } + // TODO: When a limit has been configured an allocation-free enumerator is not used. + // Need to either: + // 1) modify the dynamically generated code to only enumerate up to the max number of items, or + // 2) wait until .NET 7 is released and do this more easily with the new enumerator functions + if (maxLinks.HasValue) + { + var enumerator = activityLinks.GetEnumerator(); + for (var i = 0; enumerator.MoveNext() && i < maxLinks; ++i) + { + state.ForEach(enumerator.Current); + } + + return; + } + ActivityLinksEnumerator( activityLinks, ref state, @@ -359,7 +410,7 @@ private static readonly ListEnumerator.AllocationFreeForE private static readonly ListEnumerator.ForEachDelegate ForEachEventCallbackRef = ForEachEventCallback; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Enumerate(Activity activity, ref TState state) + public static void Enumerate(Activity activity, ref TState state, int? maxEvents) { var activityEvents = activity.Events; @@ -368,6 +419,21 @@ public static void Enumerate(Activity activity, ref TState state) return; } + // TODO: When a limit has been configured an allocation-free enumerator is not used. + // Need to either: + // 1) modify the dynamically generated code to only enumerate up to the max number of items, or + // 2) wait until .NET 7 is released and do this more easily with the new enumerator functions + if (maxEvents.HasValue) + { + var enumerator = activityEvents.GetEnumerator(); + for (var i = 0; enumerator.MoveNext() && i < maxEvents; ++i) + { + state.ForEach(enumerator.Current); + } + + return; + } + ActivityEventsEnumerator( activityEvents, ref state, diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md index 6199a199208..1a4ae40ade9 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +* Adds support for limiting the length and count of attributes exported from + the OTLP exporter. These + [Attribute Limits](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#attribute-limits) + are configured via the environment variables defined in the specification. + ([#3376](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3376)) + * The `MetricReaderOptions` defaults can be overridden using `OTEL_METRIC_EXPORT_INTERVAL` and `OTEL_METRIC_EXPORT_TIMEOUT` environmental variables as defined in the diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/EnvironmentVariableConfiguration.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/EnvironmentVariableConfiguration.cs new file mode 100644 index 00000000000..dd6cbc7ee9e --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/EnvironmentVariableConfiguration.cs @@ -0,0 +1,46 @@ +// +// 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. +// + +using System; +using OpenTelemetry.Internal; + +namespace OpenTelemetry.Configuration; + +internal class EnvironmentVariableConfiguration +{ + public static void InitializeDefaultConfigurationFromEnvironment(SdkConfiguration sdkConfiguration) + { + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#attribute-limits + SetIntConfigValue("OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT", value => sdkConfiguration.AttributeValueLengthLimit = value); + SetIntConfigValue("OTEL_ATTRIBUTE_COUNT_LIMIT", value => sdkConfiguration.AttributeCountLimit = value); + + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#span-limits + SetIntConfigValue("OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT", value => sdkConfiguration.SpanAttributeValueLengthLimit = value); + SetIntConfigValue("OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT", value => sdkConfiguration.SpanAttributeCountLimit = value); + SetIntConfigValue("OTEL_SPAN_EVENT_COUNT_LIMIT", value => sdkConfiguration.SpanEventCountLimit = value); + SetIntConfigValue("OTEL_SPAN_LINK_COUNT_LIMIT", value => sdkConfiguration.SpanLinkCountLimit = value); + SetIntConfigValue("OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT", value => sdkConfiguration.EventAttributeCountLimit = value); + SetIntConfigValue("OTEL_LINK_ATTRIBUTE_COUNT_LIMIT", value => sdkConfiguration.LinkAttributeCountLimit = value); + } + + private static void SetIntConfigValue(string key, Action setter) + { + if (EnvironmentVariableHelper.LoadNumeric(key, out var result)) + { + setter(result); + } + } +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/SdkConfiguration.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/SdkConfiguration.cs new file mode 100644 index 00000000000..a7772bcd532 --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Configuration/SdkConfiguration.cs @@ -0,0 +1,69 @@ +// +// 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. +// + +namespace OpenTelemetry.Configuration; + +internal class SdkConfiguration +{ + private int? spanAttributeValueLengthLimit; + private int? spanAttributeCountLimit; + private int? eventAttributeCountLimit; + private int? linkAttributeCountLimit; + + private SdkConfiguration() + { + EnvironmentVariableConfiguration.InitializeDefaultConfigurationFromEnvironment(this); + } + + public static SdkConfiguration Instance { get; private set; } = new SdkConfiguration(); + + public int? AttributeValueLengthLimit { get; set; } + + public int? AttributeCountLimit { get; set; } + + public int? SpanAttributeValueLengthLimit + { + get => this.spanAttributeValueLengthLimit ?? this.AttributeValueLengthLimit; + set => this.spanAttributeValueLengthLimit = value; + } + + public int? SpanAttributeCountLimit + { + get => this.spanAttributeCountLimit ?? this.AttributeCountLimit; + set => this.spanAttributeCountLimit = value; + } + + public int? SpanEventCountLimit { get; set; } + + public int? SpanLinkCountLimit { get; set; } + + public int? EventAttributeCountLimit + { + get => this.eventAttributeCountLimit ?? this.SpanAttributeCountLimit; + set => this.eventAttributeCountLimit = value; + } + + public int? LinkAttributeCountLimit + { + get => this.linkAttributeCountLimit ?? this.SpanAttributeCountLimit; + set => this.linkAttributeCountLimit = value; + } + + internal static void Reset() + { + Instance = new SdkConfiguration(); + } +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs index 6e62e19e805..f1e36021513 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs @@ -24,6 +24,7 @@ using System.Runtime.CompilerServices; using Google.Protobuf; using Google.Protobuf.Collections; +using OpenTelemetry.Configuration; using OpenTelemetry.Internal; using OpenTelemetry.Trace; using OtlpCollector = Opentelemetry.Proto.Collector.Trace.V1; @@ -154,7 +155,7 @@ internal static OtlpTrace.Span ToOtlpSpan(this Activity activity) }; TagEnumerationState otlpTags = default; - activity.EnumerateTags(ref otlpTags); + activity.EnumerateTags(ref otlpTags, SdkConfiguration.Instance.SpanAttributeCountLimit); if (activity.Kind == ActivityKind.Client || activity.Kind == ActivityKind.Producer) { @@ -181,7 +182,7 @@ internal static OtlpTrace.Span ToOtlpSpan(this Activity activity) otlpSpan.Status = activity.ToOtlpStatus(ref otlpTags); EventEnumerationState otlpEvents = default; - activity.EnumerateEvents(ref otlpEvents); + activity.EnumerateEvents(ref otlpEvents, SdkConfiguration.Instance.SpanEventCountLimit); if (otlpEvents.Created) { otlpSpan.Events.AddRange(otlpEvents.Events); @@ -189,13 +190,14 @@ internal static OtlpTrace.Span ToOtlpSpan(this Activity activity) } LinkEnumerationState otlpLinks = default; - activity.EnumerateLinks(ref otlpLinks); + activity.EnumerateLinks(ref otlpLinks, SdkConfiguration.Instance.SpanLinkCountLimit); if (otlpLinks.Created) { otlpSpan.Links.AddRange(otlpLinks.Links); otlpLinks.Links.Return(); } + // TODO: The drop counts should be set when necessary. // Activity does not limit number of attributes, events, links, etc so drop counts are always zero. return otlpSpan; @@ -259,7 +261,7 @@ private static OtlpTrace.Span.Types.Link ToOtlpLink(ActivityLink activityLink) }; TagEnumerationState otlpTags = default; - activityLink.EnumerateTags(ref otlpTags); + activityLink.EnumerateTags(ref otlpTags, SdkConfiguration.Instance.LinkAttributeCountLimit); if (otlpTags.Created) { otlpLink.Attributes.AddRange(otlpTags.Tags); @@ -279,7 +281,7 @@ private static OtlpTrace.Span.Types.Event ToOtlpEvent(ActivityEvent activityEven }; TagEnumerationState otlpTags = default; - activityEvent.EnumerateTags(ref otlpTags); + activityEvent.EnumerateTags(ref otlpTags, SdkConfiguration.Instance.EventAttributeCountLimit); if (otlpTags.Created) { otlpEvent.Attributes.AddRange(otlpTags.Tags); @@ -355,7 +357,7 @@ public bool ForEach(KeyValuePair activityTag) this.Created = true; } - if (OtlpKeyValueTransformer.Instance.TryTransformTag(activityTag, out var attribute)) + if (OtlpKeyValueTransformer.Instance.TryTransformTag(activityTag, out var attribute, SdkConfiguration.Instance.AttributeValueLengthLimit)) { PooledList.Add(ref this.Tags, attribute); diff --git a/src/OpenTelemetry/Internal/TagTransformer.cs b/src/OpenTelemetry/Internal/TagTransformer.cs index e63ccd623f9..f2a0cee9cc6 100644 --- a/src/OpenTelemetry/Internal/TagTransformer.cs +++ b/src/OpenTelemetry/Internal/TagTransformer.cs @@ -16,12 +16,13 @@ using System; using System.Collections.Generic; +using System.Linq; namespace OpenTelemetry.Internal; internal abstract class TagTransformer { - public bool TryTransformTag(KeyValuePair tag, out T result) + public bool TryTransformTag(KeyValuePair tag, out T result, int? maxLength = null) { if (tag.Value == null) { @@ -33,7 +34,7 @@ public bool TryTransformTag(KeyValuePair tag, out T result) { case char: case string: - result = this.TransformStringTag(tag.Key, Convert.ToString(tag.Value)); + result = this.TransformStringTag(tag.Key, TruncateString(Convert.ToString(tag.Value), maxLength)); break; case bool b: result = this.TransformBooleanTag(tag.Key, b); @@ -54,7 +55,7 @@ public bool TryTransformTag(KeyValuePair tag, out T result) case Array array: try { - result = this.TransformArrayTagInternal(tag.Key, array); + result = this.TransformArrayTagInternal(tag.Key, array, maxLength); } catch { @@ -77,7 +78,7 @@ public bool TryTransformTag(KeyValuePair tag, out T result) default: try { - result = this.TransformStringTag(tag.Key, tag.Value.ToString()); + result = this.TransformStringTag(tag.Key, TruncateString(tag.Value.ToString(), maxLength)); } catch { @@ -103,13 +104,20 @@ public bool TryTransformTag(KeyValuePair tag, out T result) protected abstract T TransformArrayTag(string key, Array array); - private T TransformArrayTagInternal(string key, Array array) + private static string TruncateString(string value, int? maxLength) + { + return maxLength.HasValue && value?.Length > maxLength + ? value.Substring(0, maxLength.Value) + : value; + } + + private T TransformArrayTagInternal(string key, Array array, int? maxStringValueLength) { // This switch ensures the values of the resultant array-valued tag are of the same type. return array switch { char[] => this.TransformArrayTag(key, array), - string[] => this.TransformArrayTag(key, array), + string[] => this.ConvertToStringArrayThenTransformArrayTag(key, array, maxStringValueLength), bool[] => this.TransformArrayTag(key, array), byte[] => this.TransformArrayTag(key, array), sbyte[] => this.TransformArrayTag(key, array), @@ -120,17 +128,25 @@ private T TransformArrayTagInternal(string key, Array array) long[] => this.TransformArrayTag(key, array), float[] => this.TransformArrayTag(key, array), double[] => this.TransformArrayTag(key, array), - _ => this.ConvertToStringArrayThenTransformArrayTag(key, array), + _ => this.ConvertToStringArrayThenTransformArrayTag(key, array, maxStringValueLength), }; } - private T ConvertToStringArrayThenTransformArrayTag(string key, Array array) + private T ConvertToStringArrayThenTransformArrayTag(string key, Array array, int? maxStringValueLength) { - var stringArray = new string[array.Length]; + string[] stringArray; - for (var i = 0; i < array.Length; ++i) + if (array is string[] arrayAsStringArray && (!maxStringValueLength.HasValue || !arrayAsStringArray.Any(s => s?.Length > maxStringValueLength))) + { + stringArray = arrayAsStringArray; + } + else { - stringArray[i] = array.GetValue(i)?.ToString(); + stringArray = new string[array.Length]; + for (var i = 0; i < array.Length; ++i) + { + stringArray[i] = TruncateString(array.GetValue(i)?.ToString(), maxStringValueLength); + } } return this.TransformArrayTag(key, stringArray); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Configuration/SdkConfigurationTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Configuration/SdkConfigurationTests.cs new file mode 100644 index 00000000000..dbbc9e43688 --- /dev/null +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Configuration/SdkConfigurationTests.cs @@ -0,0 +1,133 @@ +// +// 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. +// + +using System; +using Xunit; + +namespace OpenTelemetry.Configuration.Tests +{ + [Collection("xUnitCollectionPreventingTestsThatDependOnSdkConfigurationFromRunningInParallel")] + public class SdkConfigurationTests : IDisposable + { + public SdkConfigurationTests() + { + ClearEnvVars(); + SdkConfiguration.Reset(); + } + + public void Dispose() + { + ClearEnvVars(); + SdkConfiguration.Reset(); + } + + [Fact] + public void SdkConfigurationDefaults() + { + var config = SdkConfiguration.Instance; + + Assert.Null(config.AttributeValueLengthLimit); + Assert.Null(config.AttributeCountLimit); + Assert.Null(config.SpanAttributeValueLengthLimit); + Assert.Null(config.SpanAttributeCountLimit); + Assert.Null(config.SpanEventCountLimit); + Assert.Null(config.SpanLinkCountLimit); + Assert.Null(config.EventAttributeCountLimit); + Assert.Null(config.LinkAttributeCountLimit); + } + + [Fact] + public void SdkConfigurationIsInitializedFromEnvironment() + { + Environment.SetEnvironmentVariable("OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT", "10"); + Environment.SetEnvironmentVariable("OTEL_ATTRIBUTE_COUNT_LIMIT", "10"); + Environment.SetEnvironmentVariable("OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT", "20"); + Environment.SetEnvironmentVariable("OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT", "20"); + Environment.SetEnvironmentVariable("OTEL_SPAN_EVENT_COUNT_LIMIT", "10"); + Environment.SetEnvironmentVariable("OTEL_SPAN_LINK_COUNT_LIMIT", "10"); + Environment.SetEnvironmentVariable("OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT", "30"); + Environment.SetEnvironmentVariable("OTEL_LINK_ATTRIBUTE_COUNT_LIMIT", "30"); + + SdkConfiguration.Reset(); + var config = SdkConfiguration.Instance; + + Assert.Equal(10, config.AttributeValueLengthLimit); + Assert.Equal(10, config.AttributeCountLimit); + Assert.Equal(20, config.SpanAttributeValueLengthLimit); + Assert.Equal(20, config.SpanAttributeCountLimit); + Assert.Equal(10, config.SpanEventCountLimit); + Assert.Equal(10, config.SpanLinkCountLimit); + Assert.Equal(30, config.EventAttributeCountLimit); + Assert.Equal(30, config.LinkAttributeCountLimit); + } + + [Fact] + public void SpanAttributeValueLengthLimitFallback() + { + var config = SdkConfiguration.Instance; + + config.AttributeValueLengthLimit = 10; + Assert.Equal(10, config.AttributeValueLengthLimit); + Assert.Equal(10, config.SpanAttributeValueLengthLimit); + + config.SpanAttributeValueLengthLimit = 20; + Assert.Equal(10, config.AttributeValueLengthLimit); + Assert.Equal(20, config.SpanAttributeValueLengthLimit); + } + + [Fact] + public void SpanAttributeCountLimitFallback() + { + var config = SdkConfiguration.Instance; + + config.AttributeCountLimit = 10; + Assert.Equal(10, config.AttributeCountLimit); + Assert.Equal(10, config.SpanAttributeCountLimit); + Assert.Equal(10, config.EventAttributeCountLimit); + Assert.Equal(10, config.LinkAttributeCountLimit); + + config.SpanAttributeCountLimit = 20; + Assert.Equal(10, config.AttributeCountLimit); + Assert.Equal(20, config.SpanAttributeCountLimit); + Assert.Equal(20, config.EventAttributeCountLimit); + Assert.Equal(20, config.LinkAttributeCountLimit); + + config.EventAttributeCountLimit = 30; + Assert.Equal(10, config.AttributeCountLimit); + Assert.Equal(20, config.SpanAttributeCountLimit); + Assert.Equal(30, config.EventAttributeCountLimit); + Assert.Equal(20, config.LinkAttributeCountLimit); + + config.LinkAttributeCountLimit = 40; + Assert.Equal(10, config.AttributeCountLimit); + Assert.Equal(20, config.SpanAttributeCountLimit); + Assert.Equal(30, config.EventAttributeCountLimit); + Assert.Equal(40, config.LinkAttributeCountLimit); + } + + private static void ClearEnvVars() + { + Environment.SetEnvironmentVariable("OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT", null); + Environment.SetEnvironmentVariable("OTEL_ATTRIBUTE_COUNT_LIMIT", null); + Environment.SetEnvironmentVariable("OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT", null); + Environment.SetEnvironmentVariable("OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT", null); + Environment.SetEnvironmentVariable("OTEL_SPAN_EVENT_COUNT_LIMIT", null); + Environment.SetEnvironmentVariable("OTEL_SPAN_LINK_COUNT_LIMIT", null); + Environment.SetEnvironmentVariable("OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT", null); + Environment.SetEnvironmentVariable("OTEL_LINK_ATTRIBUTE_COUNT_LIMIT", null); + } + } +} diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index 56a8df672cd..a3a21e45c3f 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -18,8 +18,10 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using Google.Protobuf.Collections; using Microsoft.Extensions.DependencyInjection; using Moq; +using OpenTelemetry.Configuration; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; using OpenTelemetry.Resources; @@ -33,6 +35,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests { + [Collection("xUnitCollectionPreventingTestsThatDependOnSdkConfigurationFromRunningInParallel")] public class OtlpTraceExporterTests : Http2UnencryptedSupportTests { static OtlpTraceExporterTests() @@ -216,6 +219,79 @@ void RunTest(Batch batch) } } + [Fact] + public void SpanLimitsTest() + { + SdkConfiguration.Instance.AttributeValueLengthLimit = 4; + SdkConfiguration.Instance.AttributeCountLimit = 3; + SdkConfiguration.Instance.SpanEventCountLimit = 1; + SdkConfiguration.Instance.SpanLinkCountLimit = 1; + + var tags = new ActivityTagsCollection() + { + new KeyValuePair("TruncatedTag", "12345"), + new KeyValuePair("TruncatedStringArray", new string[] { "12345", "1234", string.Empty, null }), + new KeyValuePair("TruncatedObjectTag", new object()), + new KeyValuePair("OneTagTooMany", 1), + }; + + var links = new[] + { + new ActivityLink(default, tags), + new ActivityLink(default, tags), + }; + + using var activitySource = new ActivitySource(nameof(this.SpanLimitsTest)); + using var activity = activitySource.StartActivity("root", ActivityKind.Server, default(ActivityContext), tags, links); + + var event1 = new ActivityEvent("Event", DateTime.UtcNow, tags); + var event2 = new ActivityEvent("OneEventTooMany", DateTime.Now, tags); + + activity.AddEvent(event1); + activity.AddEvent(event2); + + var otlpSpan = activity.ToOtlpSpan(); + + Assert.NotNull(otlpSpan); + Assert.Equal(3, otlpSpan.Attributes.Count); + Assert.Equal("1234", otlpSpan.Attributes[0].Value.StringValue); + ArrayValueAsserts(otlpSpan.Attributes[1].Value.ArrayValue.Values); + Assert.Equal(new object().ToString().Substring(0, 4), otlpSpan.Attributes[2].Value.StringValue); + + Assert.Single(otlpSpan.Events); + Assert.Equal(3, otlpSpan.Events[0].Attributes.Count); + Assert.Equal("1234", otlpSpan.Events[0].Attributes[0].Value.StringValue); + ArrayValueAsserts(otlpSpan.Events[0].Attributes[1].Value.ArrayValue.Values); + Assert.Equal(new object().ToString().Substring(0, 4), otlpSpan.Events[0].Attributes[2].Value.StringValue); + + Assert.Single(otlpSpan.Links); + Assert.Equal(3, otlpSpan.Links[0].Attributes.Count); + Assert.Equal("1234", otlpSpan.Links[0].Attributes[0].Value.StringValue); + ArrayValueAsserts(otlpSpan.Links[0].Attributes[1].Value.ArrayValue.Values); + Assert.Equal(new object().ToString().Substring(0, 4), otlpSpan.Links[0].Attributes[2].Value.StringValue); + + void ArrayValueAsserts(RepeatedField values) + { + var expectedStringArray = new string[] { "1234", "1234", string.Empty, null }; + for (var i = 0; i < expectedStringArray.Length; ++i) + { + var expectedValue = expectedStringArray[i]; + var expectedValueCase = expectedValue != null + ? OtlpCommon.AnyValue.ValueOneofCase.StringValue + : OtlpCommon.AnyValue.ValueOneofCase.None; + + var actual = values[i]; + Assert.Equal(expectedValueCase, actual.ValueCase); + if (expectedValueCase != OtlpCommon.AnyValue.ValueOneofCase.None) + { + Assert.Equal(expectedValue, actual.StringValue); + } + } + } + + SdkConfiguration.Reset(); + } + [Fact] public void ToOtlpSpanTest() { From df97482f6e1ea9276f26471dd51662bc433e7c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Thu, 28 Jul 2022 18:30:33 +0200 Subject: [PATCH 35/58] EnvironmentVariableHelper support for boolean (#3457) --- .../Internal/EnvironmentVariableHelper.cs | 28 ++++++++++++++ .../EnvironmentVariableHelperTests.cs | 38 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/src/OpenTelemetry/Internal/EnvironmentVariableHelper.cs b/src/OpenTelemetry/Internal/EnvironmentVariableHelper.cs index df3f89df0aa..f6ea9783590 100644 --- a/src/OpenTelemetry/Internal/EnvironmentVariableHelper.cs +++ b/src/OpenTelemetry/Internal/EnvironmentVariableHelper.cs @@ -111,5 +111,33 @@ public static bool LoadUri(string envVarKey, out Uri result) return true; } + + /// + /// Reads an environment variable and parses it as a . + /// + /// The name of the environment variable. + /// The parsed value of the environment variable. + /// + /// Returns true when a non-empty value was read; otherwise, false. + /// + /// + /// Thrown when failed to parse the non-empty value. + /// + public static bool LoadBoolean(string envVarKey, out bool result) + { + result = default; + + if (!LoadString(envVarKey, out string value)) + { + return false; + } + + if (!bool.TryParse(value, out result)) + { + throw new FormatException($"{envVarKey} environment variable has an invalid value: '${value}'"); + } + + return true; + } } } diff --git a/test/OpenTelemetry.Tests/Internal/EnvironmentVariableHelperTests.cs b/test/OpenTelemetry.Tests/Internal/EnvironmentVariableHelperTests.cs index 290e5ceb77b..738fa27b9b3 100644 --- a/test/OpenTelemetry.Tests/Internal/EnvironmentVariableHelperTests.cs +++ b/test/OpenTelemetry.Tests/Internal/EnvironmentVariableHelperTests.cs @@ -55,6 +55,44 @@ public void LoadString_NoValue() Assert.Null(actualValue); } + [Theory] + [InlineData("true", true)] + [InlineData("TRUE", true)] + [InlineData("false", false)] + [InlineData("FALSE", false)] + [InlineData(" true ", true)] + [InlineData(" false ", false)] + public void LoadBoolean(string value, bool expectedValue) + { + Environment.SetEnvironmentVariable(EnvVar, value); + + bool actualBool = EnvironmentVariableHelper.LoadBoolean(EnvVar, out bool actualValue); + + Assert.True(actualBool); + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public void LoadBoolean_NoValue() + { + bool actualBool = EnvironmentVariableHelper.LoadBoolean(EnvVar, out bool actualValue); + + Assert.False(actualBool); + Assert.False(actualValue); + } + + [Theory] + [InlineData("something")] // non true/false + [InlineData(" ")] // whitespaces + [InlineData("0")] // 0 + [InlineData("1")] // 1 + public void LoadBoolean_Invalid(string value) + { + Environment.SetEnvironmentVariable(EnvVar, value); + + Assert.Throws(() => EnvironmentVariableHelper.LoadBoolean(EnvVar, out bool _)); + } + [Theory] [InlineData("123", 123)] [InlineData("0", 0)] From 4e0dccd527f1354915e94250f0ba90daf3b2e33b Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 28 Jul 2022 13:11:02 -0700 Subject: [PATCH 36/58] [Logs] Add extension for registering OpenTelemetryLoggerProvider with ILoggingBuilder (#3489) --- .../.publicApi/net462/PublicAPI.Unshipped.txt | 5 + .../.publicApi/net6.0/PublicAPI.Unshipped.txt | 7 +- .../netstandard2.0/PublicAPI.Unshipped.txt | 5 + .../netstandard2.1/PublicAPI.Unshipped.txt | 7 +- src/OpenTelemetry/CHANGELOG.md | 4 + .../Logs/OpenTelemetryLoggingExtensions.cs | 59 ++++- .../OpenTelemetryLoggingExtensionsTests.cs | 211 ++++++++++++++++++ 7 files changed, 294 insertions(+), 4 deletions(-) create mode 100644 test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggingExtensionsTests.cs diff --git a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt index ecaf006aa47..791e1c4f84a 100644 --- a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt @@ -11,5 +11,10 @@ OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMillisecond OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! +*REMOVED*static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action? configure = null) -> Microsoft.Extensions.Logging.ILoggingBuilder! +static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder) -> Microsoft.Extensions.Logging.ILoggingBuilder! +static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder! +static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, bool disposeProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder! +static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action? configure) -> Microsoft.Extensions.Logging.ILoggingBuilder! ~static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action configure) -> OpenTelemetry.Metrics.MeterProviderBuilder ~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.Action configure) -> OpenTelemetry.Trace.TracerProviderBuilder diff --git a/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt index 43156ee3c3a..a045a0ffba5 100644 --- a/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt @@ -11,5 +11,10 @@ OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action bool OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void +*REMOVED*static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action? configure = null) -> Microsoft.Extensions.Logging.ILoggingBuilder! +static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder) -> Microsoft.Extensions.Logging.ILoggingBuilder! +static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder! +static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, bool disposeProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder! +static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action? configure) -> Microsoft.Extensions.Logging.ILoggingBuilder! ~static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action configure) -> OpenTelemetry.Metrics.MeterProviderBuilder -~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.Action configure) -> OpenTelemetry.Trace.TracerProviderBuilder \ No newline at end of file +~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.Action configure) -> OpenTelemetry.Trace.TracerProviderBuilder diff --git a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index ecaf006aa47..791e1c4f84a 100644 --- a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -11,5 +11,10 @@ OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMillisecond OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! +*REMOVED*static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action? configure = null) -> Microsoft.Extensions.Logging.ILoggingBuilder! +static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder) -> Microsoft.Extensions.Logging.ILoggingBuilder! +static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder! +static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, bool disposeProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder! +static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action? configure) -> Microsoft.Extensions.Logging.ILoggingBuilder! ~static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action configure) -> OpenTelemetry.Metrics.MeterProviderBuilder ~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.Action configure) -> OpenTelemetry.Trace.TracerProviderBuilder diff --git a/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt index 43156ee3c3a..a045a0ffba5 100644 --- a/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt @@ -11,5 +11,10 @@ OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action bool OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void +*REMOVED*static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action? configure = null) -> Microsoft.Extensions.Logging.ILoggingBuilder! +static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder) -> Microsoft.Extensions.Logging.ILoggingBuilder! +static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder! +static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, bool disposeProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder! +static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action? configure) -> Microsoft.Extensions.Logging.ILoggingBuilder! ~static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action configure) -> OpenTelemetry.Metrics.MeterProviderBuilder -~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.Action configure) -> OpenTelemetry.Trace.TracerProviderBuilder \ No newline at end of file +~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.Action configure) -> OpenTelemetry.Trace.TracerProviderBuilder diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index e9943f06bd6..c50635a1d78 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -34,6 +34,10 @@ * Fix exact match of activity source name when `wildcard` is used. ([#3446](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3446)) +* Added AddOpenTelemetry `ILoggingBuilder` extensions which accept + `OpenTelemetryLoggerProvider` directly + ([#3489](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3489)) + ## 1.3.0 Released 2022-Jun-03 diff --git a/src/OpenTelemetry/Logs/OpenTelemetryLoggingExtensions.cs b/src/OpenTelemetry/Logs/OpenTelemetryLoggingExtensions.cs index 000542b2fbd..9687d32eb1b 100644 --- a/src/OpenTelemetry/Logs/OpenTelemetryLoggingExtensions.cs +++ b/src/OpenTelemetry/Logs/OpenTelemetryLoggingExtensions.cs @@ -32,12 +32,20 @@ namespace Microsoft.Extensions.Logging public static class OpenTelemetryLoggingExtensions { /// - /// Adds a OpenTelemetry logger named 'OpenTelemetry' to the factory. + /// Adds an OpenTelemetry logger named 'OpenTelemetry' to the . + /// + /// The to use. + /// The supplied for call chaining. + public static ILoggingBuilder AddOpenTelemetry(this ILoggingBuilder builder) + => AddOpenTelemetry(builder, configure: null); + + /// + /// Adds an OpenTelemetry logger named 'OpenTelemetry' to the . /// /// The to use. /// Optional configuration action. /// The supplied for call chaining. - public static ILoggingBuilder AddOpenTelemetry(this ILoggingBuilder builder, Action? configure = null) + public static ILoggingBuilder AddOpenTelemetry(this ILoggingBuilder builder, Action? configure) { Guard.ThrowIfNull(builder); @@ -51,5 +59,52 @@ public static ILoggingBuilder AddOpenTelemetry(this ILoggingBuilder builder, Act return builder; } + + /// + /// Adds an OpenTelemetry logger named 'OpenTelemetry' to the . + /// + /// + /// Note: The supplied will + /// automatically be disposed when the + /// built from is disposed. + /// + /// The to use. + /// . + /// The supplied for call chaining. + public static ILoggingBuilder AddOpenTelemetry(this ILoggingBuilder builder, OpenTelemetryLoggerProvider openTelemetryLoggerProvider) + => AddOpenTelemetry(builder, openTelemetryLoggerProvider, disposeProvider: true); + + /// + /// Adds an OpenTelemetry logger named 'OpenTelemetry' to the . + /// + /// The to use. + /// . + /// Controls whether or not the supplied + /// will be disposed when + /// the is disposed. + /// The supplied for call chaining. + public static ILoggingBuilder AddOpenTelemetry( + this ILoggingBuilder builder, + OpenTelemetryLoggerProvider openTelemetryLoggerProvider, + bool disposeProvider) + { + Guard.ThrowIfNull(builder); + Guard.ThrowIfNull(openTelemetryLoggerProvider); + + // Note: Currently if multiple OpenTelemetryLoggerProvider instances + // are added to the same ILoggingBuilder everything after the first + // is silently ignored. + + if (disposeProvider) + { + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton(sp => openTelemetryLoggerProvider)); + } + else + { + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton(openTelemetryLoggerProvider)); + } + + return builder; + } } } diff --git a/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggingExtensionsTests.cs b/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggingExtensionsTests.cs new file mode 100644 index 00000000000..5f07ee7b06f --- /dev/null +++ b/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggingExtensionsTests.cs @@ -0,0 +1,211 @@ +// +// 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. +// + +#nullable enable + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace OpenTelemetry.Logs.Tests; + +public sealed class OpenTelemetryLoggingExtensionsTests +{ + [Fact] + public void ServiceCollectionAddOpenTelemetryNoParametersTest() + { + bool optionsCallbackInvoked = false; + + var serviceCollection = new ServiceCollection(); + + serviceCollection.AddLogging(configure => + { + configure.AddOpenTelemetry(); + }); + + serviceCollection.Configure(options => + { + optionsCallbackInvoked = true; + }); + + using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); + + ILoggerFactory? loggerFactory = serviceProvider.GetService(); + + Assert.NotNull(loggerFactory); + + Assert.True(optionsCallbackInvoked); + } + + [Theory] + [InlineData(1, 0)] + [InlineData(1, 1)] + [InlineData(5, 5)] + public void ServiceCollectionAddOpenTelemetryConfigureActionTests(int numberOfBuilderRegistrations, int numberOfOptionsRegistrations) + { + int configureCallbackInvocations = 0; + int optionsCallbackInvocations = 0; + OpenTelemetryLoggerOptions? optionsInstance = null; + + var serviceCollection = new ServiceCollection(); + + serviceCollection.AddLogging(configure => + { + for (int i = 0; i < numberOfBuilderRegistrations; i++) + { + configure.AddOpenTelemetry(ConfigureCallback); + } + }); + + for (int i = 0; i < numberOfOptionsRegistrations; i++) + { + serviceCollection.Configure(OptionsCallback); + } + + using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); + + ILoggerFactory? loggerFactory = serviceProvider.GetService(); + + Assert.NotNull(loggerFactory); + + Assert.NotNull(optionsInstance); + + Assert.Equal(numberOfBuilderRegistrations, configureCallbackInvocations); + Assert.Equal(numberOfOptionsRegistrations, optionsCallbackInvocations); + + void ConfigureCallback(OpenTelemetryLoggerOptions options) + { + if (optionsInstance == null) + { + optionsInstance = options; + } + else + { + Assert.Equal(optionsInstance, options); + } + + configureCallbackInvocations++; + } + + void OptionsCallback(OpenTelemetryLoggerOptions options) + { + if (optionsInstance == null) + { + optionsInstance = options; + } + else + { + Assert.Equal(optionsInstance, options); + } + + optionsCallbackInvocations++; + } + } + + [Fact] + public void ServiceCollectionAddOpenTelemetryWithProviderTest() + { + var provider = new WrappedOpenTelemetryLoggerProvider(); + + var serviceCollection = new ServiceCollection(); + + serviceCollection.AddLogging(configure => + { + configure.AddOpenTelemetry(provider); + }); + + using (ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider()) + { + ILoggerFactory? loggerFactory = serviceProvider.GetService(); + + Assert.NotNull(loggerFactory); + + loggerFactory!.Dispose(); + + // Note: Provider disposal does not actually happen until serviceProvider is disposed + Assert.False(provider.Disposed); + } + + Assert.True(provider.Disposed); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ServiceCollectionAddOpenTelemetryWithProviderAndDisposeSpecifiedTests(bool dispose) + { + var provider = new WrappedOpenTelemetryLoggerProvider(); + + var serviceCollection = new ServiceCollection(); + + serviceCollection.AddLogging(configure => + { + configure.AddOpenTelemetry(provider, disposeProvider: dispose); + }); + + using (ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider()) + { + ILoggerFactory? loggerFactory = serviceProvider.GetService(); + + Assert.NotNull(loggerFactory); + + loggerFactory!.Dispose(); + + // Note: Provider disposal does not actually happen until serviceProvider is disposed + Assert.False(provider.Disposed); + } + + Assert.Equal(dispose, provider.Disposed); + + provider.Dispose(); + + Assert.True(provider.Disposed); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void LoggerFactoryCreateAddOpenTelemetryWithProviderAndDisposeSpecifiedTests(bool dispose) + { + var provider = new WrappedOpenTelemetryLoggerProvider(); + + using (var factory = LoggerFactory.Create(configure => + { + configure.AddOpenTelemetry(provider, disposeProvider: dispose); + })) + { + Assert.False(provider.Disposed); + } + + Assert.Equal(dispose, provider.Disposed); + + provider.Dispose(); + + Assert.True(provider.Disposed); + } + + private sealed class WrappedOpenTelemetryLoggerProvider : OpenTelemetryLoggerProvider + { + public bool Disposed { get; private set; } + + protected override void Dispose(bool disposing) + { + this.Disposed = true; + + base.Dispose(disposing); + } + } +} From e3450b2e591e5d590e34d85f69ed3bcc7143d7ab Mon Sep 17 00:00:00 2001 From: Yun-Ting Lin Date: Thu, 28 Jul 2022 16:02:53 -0700 Subject: [PATCH 37/58] [Prometheus] Split up projects based on hosting mechanism. (#3430) --- OpenTelemetry.sln | 48 ++++++-- README.md | 3 +- docs/metrics/customizing-the-sdk/README.md | 3 +- docs/metrics/extending-the-sdk/README.md | 3 +- .../Program.cs | 2 +- .../README.md | 21 ++-- .../getting-started-prometheus-grafana.csproj | 5 +- .../AspNetCore/Examples.AspNetCore.csproj | 2 +- examples/Console/Examples.Console.csproj | 2 +- examples/Console/TestPrometheusExporter.cs | 9 +- .../netcoreapp3.1}/PublicAPI.Shipped.txt | 0 .../netcoreapp3.1/PublicAPI.Unshipped.txt | 26 ++--- .../AssemblyInfo.cs | 23 ++++ .../CHANGELOG.md | 6 + ...etry.Exporter.Prometheus.AspNetCore.csproj | 41 +++++++ ...eusExporterApplicationBuilderExtensions.cs | 7 +- ...sExporterEndpointRouteBuilderExtensions.cs | 7 +- ...sExporterMeterProviderBuilderExtensions.cs | 12 +- .../PrometheusExporterMiddleware.cs | 3 +- .../README.md | 58 +++------- .../.publicApi/net462}/PublicAPI.Shipped.txt | 0 .../.publicApi/net462/PublicAPI.Unshipped.txt | 12 ++ .../netstandard2.0/PublicAPI.Shipped.txt | 0 .../netstandard2.0/PublicAPI.Unshipped.txt | 12 ++ .../CHANGELOG.md | 94 ++++++++++++++++ ...ry.Exporter.Prometheus.HttpListener.csproj | 36 ++++++ ...pListenerMeterProviderBuilderExtensions.cs | 104 ++++++++++++++++++ .../PrometheusHttpListener.cs} | 38 +++---- .../PrometheusHttpListenerOptions.cs | 56 ++++++++++ .../README.md | 72 ++++++++++++ .../.publicApi/net462/PublicAPI.Shipped.txt | 0 .../.publicApi/net462/PublicAPI.Unshipped.txt | 6 + .../netcoreapp3.1/PublicAPI.Shipped.txt | 0 .../netcoreapp3.1/PublicAPI.Unshipped.txt | 6 + .../AssemblyInfo.cs | 4 +- ...lemetry.Exporter.Prometheus.Shared.csproj} | 7 +- .../PrometheusCollectionManager.cs | 2 +- .../PrometheusExporter.cs | 36 +++--- .../PrometheusExporterEventSource.cs | 2 +- .../PrometheusExporterOptions.cs | 52 ++------- .../PrometheusSerializer.cs | 2 +- .../PrometheusSerializerExt.cs | 2 +- .../.publicApi/net462/PublicAPI.Unshipped.txt | 18 --- src/OpenTelemetry/AssemblyInfo.cs | 5 +- test/Benchmarks/Benchmarks.csproj | 2 +- .../PrometheusSerializerBenchmarks.cs | 4 +- ...porter.Prometheus.AspNetCore.Tests.csproj} | 13 +-- .../PrometheusCollectionManagerTests.cs | 3 +- .../PrometheusExporterMiddlewareTests.cs | 6 +- ...orter.Prometheus.HttpListener.Tests.csproj | 29 +++++ .../PrometheusExporterHttpListenerTests.cs} | 96 +++++----------- ...ry.Exporter.Prometheus.Shared.Tests.csproj | 32 ++++++ .../PrometheusSerializerTests.cs | 2 +- .../OpenTelemetry.Tests.Stress.Logs.csproj | 2 +- .../OpenTelemetry.Tests.Stress.Metrics.csproj | 2 +- .../Program.cs | 9 +- .../OpenTelemetry.Tests.Stress.csproj | 2 +- test/OpenTelemetry.Tests.Stress/README.md | 2 +- test/OpenTelemetry.Tests.Stress/Skeleton.cs | 9 +- 59 files changed, 737 insertions(+), 323 deletions(-) rename src/{OpenTelemetry.Exporter.Prometheus/.publicApi/net462 => OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/netcoreapp3.1}/PublicAPI.Shipped.txt (100%) rename src/{OpenTelemetry.Exporter.Prometheus => OpenTelemetry.Exporter.Prometheus.AspNetCore}/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt (63%) create mode 100644 src/OpenTelemetry.Exporter.Prometheus.AspNetCore/AssemblyInfo.cs rename src/{OpenTelemetry.Exporter.Prometheus => OpenTelemetry.Exporter.Prometheus.AspNetCore}/CHANGELOG.md (84%) create mode 100644 src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj rename src/{OpenTelemetry.Exporter.Prometheus => OpenTelemetry.Exporter.Prometheus.AspNetCore}/PrometheusExporterApplicationBuilderExtensions.cs (96%) rename src/{OpenTelemetry.Exporter.Prometheus => OpenTelemetry.Exporter.Prometheus.AspNetCore}/PrometheusExporterEndpointRouteBuilderExtensions.cs (95%) rename src/{OpenTelemetry.Exporter.Prometheus => OpenTelemetry.Exporter.Prometheus.AspNetCore}/PrometheusExporterMeterProviderBuilderExtensions.cs (87%) rename src/{OpenTelemetry.Exporter.Prometheus/Implementation => OpenTelemetry.Exporter.Prometheus.AspNetCore}/PrometheusExporterMiddleware.cs (97%) rename src/{OpenTelemetry.Exporter.Prometheus => OpenTelemetry.Exporter.Prometheus.AspNetCore}/README.md (58%) rename src/{OpenTelemetry.Exporter.Prometheus/.publicApi/netcoreapp3.1 => OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462}/PublicAPI.Shipped.txt (100%) create mode 100644 src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt create mode 100644 src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Shipped.txt create mode 100644 src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt create mode 100644 src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md create mode 100644 src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj create mode 100644 src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusExporterHttpListenerMeterProviderBuilderExtensions.cs rename src/{OpenTelemetry.Exporter.Prometheus/Implementation/PrometheusExporterHttpServer.cs => OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs} (82%) create mode 100644 src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs create mode 100644 src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md create mode 100644 src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/net462/PublicAPI.Shipped.txt create mode 100644 src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/net462/PublicAPI.Unshipped.txt create mode 100644 src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/netcoreapp3.1/PublicAPI.Shipped.txt create mode 100644 src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt rename src/{OpenTelemetry.Exporter.Prometheus => OpenTelemetry.Exporter.Prometheus.Shared}/AssemblyInfo.cs (78%) rename src/{OpenTelemetry.Exporter.Prometheus/OpenTelemetry.Exporter.Prometheus.csproj => OpenTelemetry.Exporter.Prometheus.Shared/OpenTelemetry.Exporter.Prometheus.Shared.csproj} (84%) rename src/{OpenTelemetry.Exporter.Prometheus/Implementation => OpenTelemetry.Exporter.Prometheus.Shared}/PrometheusCollectionManager.cs (99%) rename src/{OpenTelemetry.Exporter.Prometheus => OpenTelemetry.Exporter.Prometheus.Shared}/PrometheusExporter.cs (75%) rename src/{OpenTelemetry.Exporter.Prometheus/Implementation => OpenTelemetry.Exporter.Prometheus.Shared}/PrometheusExporterEventSource.cs (98%) rename src/{OpenTelemetry.Exporter.Prometheus => OpenTelemetry.Exporter.Prometheus.Shared}/PrometheusExporterOptions.cs (52%) rename src/{OpenTelemetry.Exporter.Prometheus/Implementation => OpenTelemetry.Exporter.Prometheus.Shared}/PrometheusSerializer.cs (99%) rename src/{OpenTelemetry.Exporter.Prometheus/Implementation => OpenTelemetry.Exporter.Prometheus.Shared}/PrometheusSerializerExt.cs (99%) delete mode 100644 src/OpenTelemetry.Exporter.Prometheus/.publicApi/net462/PublicAPI.Unshipped.txt rename test/{OpenTelemetry.Exporter.Prometheus.Tests/OpenTelemetry.Exporter.Prometheus.Tests.csproj => OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests.csproj} (75%) rename test/{OpenTelemetry.Exporter.Prometheus.Tests => OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests}/PrometheusCollectionManagerTests.cs (98%) rename test/{OpenTelemetry.Exporter.Prometheus.Tests => OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests}/PrometheusExporterMiddlewareTests.cs (97%) create mode 100644 test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests.csproj rename test/{OpenTelemetry.Exporter.Prometheus.Tests/PrometheusExporterHttpServerTests.cs => OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusExporterHttpListenerTests.cs} (63%) create mode 100644 test/OpenTelemetry.Exporter.Prometheus.Shared.Tests/OpenTelemetry.Exporter.Prometheus.Shared.Tests.csproj rename test/{OpenTelemetry.Exporter.Prometheus.Tests => OpenTelemetry.Exporter.Prometheus.Shared.Tests}/PrometheusSerializerTests.cs (99%) diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index 375d2633837..d345ee94283 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -195,8 +195,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "reporting-exceptions", "doc EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "customizing-the-sdk", "docs\trace\customizing-the-sdk\customizing-the-sdk.csproj", "{64E3D8BB-93AB-4571-93F7-ED8D64DFFD06}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Prometheus", "src\OpenTelemetry.Exporter.Prometheus\OpenTelemetry.Exporter.Prometheus.csproj", "{52158A12-E7EF-45A1-859F-06F9B17410CB}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "learning-more-instruments", "docs\metrics\learning-more-instruments\learning-more-instruments.csproj", "{E7F491CC-C37E-4A56-9CA7-8F77F59E0614}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "getting-started", "docs\metrics\getting-started\getting-started.csproj", "{EA60B549-F712-4ABE-8E44-FCA83B78C06E}" @@ -209,8 +207,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "customizing-the-sdk", "docs EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Tests.Stress", "test\OpenTelemetry.Tests.Stress\OpenTelemetry.Tests.Stress.csproj", "{2770158A-D220-414B-ABC6-179371323579}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Prometheus.Tests", "test\OpenTelemetry.Exporter.Prometheus.Tests\OpenTelemetry.Exporter.Prometheus.Tests.csproj", "{380EE686-91F1-45B3-AEEB-755F0E5B068F}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.OpenTelemetryProtocol.Logs", "src\OpenTelemetry.Exporter.OpenTelemetryProtocol.Logs\OpenTelemetry.Exporter.OpenTelemetryProtocol.Logs.csproj", "{6E1A5FA3-E024-4972-9EDC-11E36C5A0D6F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApp.AspNetCore.6.0", "test\TestApp.AspNetCore.6.0\TestApp.AspNetCore.6.0.csproj", "{0076C657-564F-4787-9FFF-52D9D55166E8}" @@ -239,6 +235,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Extensions.Ev EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Extensions.EventSource.Tests", "test\OpenTelemetry.Extensions.EventSource.Tests\OpenTelemetry.Extensions.EventSource.Tests.csproj", "{304FCFFF-97DE-484B-8D8C-612C644426E5}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Prometheus.AspNetCore", "src\OpenTelemetry.Exporter.Prometheus.AspNetCore\OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj", "{921CF401-4C2F-4C6D-A750-0B5DC457C1F1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Prometheus.HttpListener", "src\OpenTelemetry.Exporter.Prometheus.HttpListener\OpenTelemetry.Exporter.Prometheus.HttpListener.csproj", "{6B0232B7-5F29-4FB5-B383-1AA02DFE1089}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Prometheus.Shared", "src\OpenTelemetry.Exporter.Prometheus.Shared\OpenTelemetry.Exporter.Prometheus.Shared.csproj", "{4AD27517-BAFC-413B-A8F0-988C3CEDC662}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests", "test\OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests\OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests.csproj", "{FBD12B0B-6731-4DD4-9C13-86F34593E974}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Prometheus.HttpListener.Tests", "test\OpenTelemetry.Exporter.Prometheus.HttpListener.Tests\OpenTelemetry.Exporter.Prometheus.HttpListener.Tests.csproj", "{4EF4364F-6E64-43CE-BED1-E6FE01024899}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Prometheus.Shared.Tests", "test\OpenTelemetry.Exporter.Prometheus.Shared.Tests\OpenTelemetry.Exporter.Prometheus.Shared.Tests.csproj", "{8E75AEE2-017B-474F-A96D-035DF76A1C9E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -405,10 +413,6 @@ Global {64E3D8BB-93AB-4571-93F7-ED8D64DFFD06}.Debug|Any CPU.Build.0 = Debug|Any CPU {64E3D8BB-93AB-4571-93F7-ED8D64DFFD06}.Release|Any CPU.ActiveCfg = Release|Any CPU {64E3D8BB-93AB-4571-93F7-ED8D64DFFD06}.Release|Any CPU.Build.0 = Release|Any CPU - {52158A12-E7EF-45A1-859F-06F9B17410CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {52158A12-E7EF-45A1-859F-06F9B17410CB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {52158A12-E7EF-45A1-859F-06F9B17410CB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {52158A12-E7EF-45A1-859F-06F9B17410CB}.Release|Any CPU.Build.0 = Release|Any CPU {E7F491CC-C37E-4A56-9CA7-8F77F59E0614}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E7F491CC-C37E-4A56-9CA7-8F77F59E0614}.Debug|Any CPU.Build.0 = Debug|Any CPU {E7F491CC-C37E-4A56-9CA7-8F77F59E0614}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -433,10 +437,6 @@ Global {2770158A-D220-414B-ABC6-179371323579}.Debug|Any CPU.Build.0 = Debug|Any CPU {2770158A-D220-414B-ABC6-179371323579}.Release|Any CPU.ActiveCfg = Release|Any CPU {2770158A-D220-414B-ABC6-179371323579}.Release|Any CPU.Build.0 = Release|Any CPU - {380EE686-91F1-45B3-AEEB-755F0E5B068F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {380EE686-91F1-45B3-AEEB-755F0E5B068F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {380EE686-91F1-45B3-AEEB-755F0E5B068F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {380EE686-91F1-45B3-AEEB-755F0E5B068F}.Release|Any CPU.Build.0 = Release|Any CPU {6E1A5FA3-E024-4972-9EDC-11E36C5A0D6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6E1A5FA3-E024-4972-9EDC-11E36C5A0D6F}.Debug|Any CPU.Build.0 = Debug|Any CPU {6E1A5FA3-E024-4972-9EDC-11E36C5A0D6F}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -493,6 +493,30 @@ Global {304FCFFF-97DE-484B-8D8C-612C644426E5}.Debug|Any CPU.Build.0 = Debug|Any CPU {304FCFFF-97DE-484B-8D8C-612C644426E5}.Release|Any CPU.ActiveCfg = Release|Any CPU {304FCFFF-97DE-484B-8D8C-612C644426E5}.Release|Any CPU.Build.0 = Release|Any CPU + {921CF401-4C2F-4C6D-A750-0B5DC457C1F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {921CF401-4C2F-4C6D-A750-0B5DC457C1F1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {921CF401-4C2F-4C6D-A750-0B5DC457C1F1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {921CF401-4C2F-4C6D-A750-0B5DC457C1F1}.Release|Any CPU.Build.0 = Release|Any CPU + {6B0232B7-5F29-4FB5-B383-1AA02DFE1089}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B0232B7-5F29-4FB5-B383-1AA02DFE1089}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B0232B7-5F29-4FB5-B383-1AA02DFE1089}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B0232B7-5F29-4FB5-B383-1AA02DFE1089}.Release|Any CPU.Build.0 = Release|Any CPU + {4AD27517-BAFC-413B-A8F0-988C3CEDC662}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4AD27517-BAFC-413B-A8F0-988C3CEDC662}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4AD27517-BAFC-413B-A8F0-988C3CEDC662}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4AD27517-BAFC-413B-A8F0-988C3CEDC662}.Release|Any CPU.Build.0 = Release|Any CPU + {FBD12B0B-6731-4DD4-9C13-86F34593E974}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBD12B0B-6731-4DD4-9C13-86F34593E974}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBD12B0B-6731-4DD4-9C13-86F34593E974}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBD12B0B-6731-4DD4-9C13-86F34593E974}.Release|Any CPU.Build.0 = Release|Any CPU + {4EF4364F-6E64-43CE-BED1-E6FE01024899}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4EF4364F-6E64-43CE-BED1-E6FE01024899}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4EF4364F-6E64-43CE-BED1-E6FE01024899}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4EF4364F-6E64-43CE-BED1-E6FE01024899}.Release|Any CPU.Build.0 = Release|Any CPU + {8E75AEE2-017B-474F-A96D-035DF76A1C9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E75AEE2-017B-474F-A96D-035DF76A1C9E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E75AEE2-017B-474F-A96D-035DF76A1C9E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E75AEE2-017B-474F-A96D-035DF76A1C9E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/README.md b/README.md index f0f027b7478..442df7a4c5f 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,8 @@ libraries](https://github.com/open-telemetry/opentelemetry-specification/blob/ma * [Jaeger](./src/OpenTelemetry.Exporter.Jaeger/README.md) * [OTLP](./src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md) (OpenTelemetry Protocol) -* [Prometheus](./src/OpenTelemetry.Exporter.Prometheus/README.md) +* [Prometheus HttpListener](./src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md) +* [Prometheus AspNetCore](./src/OpenTelemetry.Exporter.Prometheus.AspNetCore/README.md) * [Zipkin](./src/OpenTelemetry.Exporter.Zipkin/README.md) See the [OpenTelemetry registry](https://opentelemetry.io/registry/?s=net) for diff --git a/docs/metrics/customizing-the-sdk/README.md b/docs/metrics/customizing-the-sdk/README.md index 5491faf1914..eeeb64ef819 100644 --- a/docs/metrics/customizing-the-sdk/README.md +++ b/docs/metrics/customizing-the-sdk/README.md @@ -423,7 +423,8 @@ Refer to the individual exporter docs to learn how to use them: * [In-memory](../../../src/OpenTelemetry.Exporter.InMemory/README.md) * [OTLP](../../../src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md) (OpenTelemetry Protocol) -* [Prometheus](../../../src/OpenTelemetry.Exporter.Prometheus/README.md) +* [Prometheus HttpListener](../../../src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md) +* [Prometheus AspNetCore](../../../src/OpenTelemetry.Exporter.Prometheus.AspNetCore/README.md) ### Resource diff --git a/docs/metrics/extending-the-sdk/README.md b/docs/metrics/extending-the-sdk/README.md index 7671bb420ca..0777c8f8f1b 100644 --- a/docs/metrics/extending-the-sdk/README.md +++ b/docs/metrics/extending-the-sdk/README.md @@ -12,7 +12,8 @@ OpenTelemetry .NET SDK has provided the following built-in metric exporters: * [InMemory](../../../src/OpenTelemetry.Exporter.InMemory/README.md) * [Console](../../../src/OpenTelemetry.Exporter.Console/README.md) * [OpenTelemetryProtocol](../../../src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md) -* [Prometheus](../../../src/OpenTelemetry.Exporter.Prometheus/README.md) +* [Prometheus HttpListener](../../../src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md) +* [Prometheus AspNetCore](../../../src/OpenTelemetry.Exporter.Prometheus.AspNetCore/README.md) Custom exporters can be implemented to send telemetry data to places which are not covered by the built-in exporters: diff --git a/docs/metrics/getting-started-prometheus-grafana/Program.cs b/docs/metrics/getting-started-prometheus-grafana/Program.cs index 2e176c39f79..32bfa1c65e1 100644 --- a/docs/metrics/getting-started-prometheus-grafana/Program.cs +++ b/docs/metrics/getting-started-prometheus-grafana/Program.cs @@ -31,7 +31,7 @@ public static void Main() { using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter("MyCompany.MyProduct.MyLibrary") - .AddPrometheusExporter(options => { options.StartHttpListener = true; }) + .AddPrometheusHttpListener() .Build(); Console.WriteLine("Press any key to exit"); diff --git a/docs/metrics/getting-started-prometheus-grafana/README.md b/docs/metrics/getting-started-prometheus-grafana/README.md index e58245a6b04..62953f4f130 100644 --- a/docs/metrics/getting-started-prometheus-grafana/README.md +++ b/docs/metrics/getting-started-prometheus-grafana/README.md @@ -23,10 +23,10 @@ dotnet run ``` Add a reference to [Prometheus -Exporter](../../../src/OpenTelemetry.Exporter.Prometheus/README.md): +Exporter Http Listener](../../../src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md): ```sh -dotnet add package --prerelease OpenTelemetry.Exporter.Prometheus +dotnet add package --prerelease OpenTelemetry.Exporter.Prometheus.HttpListener ``` Now, we are going to make some small tweaks to the example in the @@ -46,12 +46,13 @@ And replace the below line: with ```csharp -.AddPrometheusExporter(options => { options.StartHttpListener = true; }) +.AddPrometheusHttpListener() ``` -With `AddPrometheusExporter()`, OpenTelemetry `PrometheusExporter` will export +`PrometheusHttpListener` is a wrapper that contains `PrometheusExporter`. +With `AddPrometheusHttpListener()`, OpenTelemetry `PrometheusExporter` will export data via the endpoint defined by -[PrometheusExporterOptions.HttpListenerPrefixes](../../../src/OpenTelemetry.Exporter.Prometheus/README.md#httplistenerprefixes), +[PrometheusHttpListenerOptions.Prefixes](../../../src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md#prefixes), which is `http://localhost:9464/` by default. ```mermaid @@ -60,7 +61,7 @@ graph LR subgraph SDK MeterProvider MetricReader[BaseExportingMetricReader] - PrometheusExporter["PrometheusExporter
(http://localhost:9464/)"] + PrometheusHttpListener["PrometheusHttpListener
(http://localhost:9464/)"] end subgraph API @@ -69,7 +70,7 @@ end Instrument --> | Measurements | MeterProvider -MeterProvider --> | Metrics | MetricReader --> | Pull | PrometheusExporter +MeterProvider --> | Metrics | MetricReader --> | Pull | PrometheusHttpListener ``` Also, for our learning purpose, use a while-loop to keep increasing the counter @@ -99,7 +100,7 @@ web browser: ![Browser UI](https://user-images.githubusercontent.com/17327289/151633547-736c6d91-62d2-4e66-a53f-2e16c44bfabc.png) -Now, we understand how we can configure `PrometheusExporter` to export metrics. +Now, we understand how we can configure `PrometheusHttpListener` to export metrics. Next, we are going to learn about how to use Prometheus to collect the metrics. ## Collect metrics using Prometheus @@ -156,7 +157,7 @@ values we have set in `otel.yml`. Congratulations! Now we know how to configure Prometheus server and deploy OpenTelemetry -`PrometheusExporter` to export our metrics. Next, we are going to explore a tool +`PrometheusHttpListener` to export our metrics. Next, we are going to explore a tool called Grafana, which has powerful visualizations for the metrics. ## Explore metrics using Grafana @@ -201,7 +202,7 @@ subgraph Prometheus PrometheusDatabase end -PrometheusExporter["PrometheusExporter
(listening at #quot;http://localhost:9464/#quot;)"] -->|HTTP GET| PrometheusScraper{{"Prometheus scraper
(polling #quot;http://localhost:9464/metrics#quot; every 10 seconds)"}} +PrometheusHttpListener["PrometheusHttpListener
(listening at #quot;http://localhost:9464/#quot;)"] -->|HTTP GET| PrometheusScraper{{"Prometheus scraper
(polling #quot;http://localhost:9464/metrics#quot; every 10 seconds)"}} PrometheusScraper --> PrometheusDatabase[("Prometheus TSDB (time series database)")] PrometheusDatabase -->|http://localhost:9090/graph| PrometheusUI["Browser
(Prometheus Dashboard)"] PrometheusDatabase -->|http://localhost:9090/api/| Grafana[Grafana Server] diff --git a/docs/metrics/getting-started-prometheus-grafana/getting-started-prometheus-grafana.csproj b/docs/metrics/getting-started-prometheus-grafana/getting-started-prometheus-grafana.csproj index 4913a024a94..455ed30ceb4 100644 --- a/docs/metrics/getting-started-prometheus-grafana/getting-started-prometheus-grafana.csproj +++ b/docs/metrics/getting-started-prometheus-grafana/getting-started-prometheus-grafana.csproj @@ -1,5 +1,8 @@ + + netstandard2.0 + - + diff --git a/examples/AspNetCore/Examples.AspNetCore.csproj b/examples/AspNetCore/Examples.AspNetCore.csproj index d92150ac7f8..a1f897d4dcf 100644 --- a/examples/AspNetCore/Examples.AspNetCore.csproj +++ b/examples/AspNetCore/Examples.AspNetCore.csproj @@ -19,7 +19,7 @@ - + diff --git a/examples/Console/Examples.Console.csproj b/examples/Console/Examples.Console.csproj index 830e6dfe32c..8239cd4f5f1 100644 --- a/examples/Console/Examples.Console.csproj +++ b/examples/Console/Examples.Console.csproj @@ -33,7 +33,7 @@ - + diff --git a/examples/Console/TestPrometheusExporter.cs b/examples/Console/TestPrometheusExporter.cs index 935e2530000..29fc3879f39 100644 --- a/examples/Console/TestPrometheusExporter.cs +++ b/examples/Console/TestPrometheusExporter.cs @@ -51,12 +51,9 @@ internal static object Run(int port) using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(MyMeter.Name) .AddMeter(MyMeter2.Name) - .AddPrometheusExporter(options => - { - options.StartHttpListener = true; - options.HttpListenerPrefixes = new string[] { $"http://localhost:{port}/" }; - options.ScrapeResponseCacheDurationMilliseconds = 0; - }) + .AddPrometheusHttpListener( + exporterOptions => exporterOptions.ScrapeResponseCacheDurationMilliseconds = 0, + listenerOptions => listenerOptions.Prefixes = new string[] { $"http://localhost:{port}/" }) .Build(); var process = Process.GetCurrentProcess(); diff --git a/src/OpenTelemetry.Exporter.Prometheus/.publicApi/net462/PublicAPI.Shipped.txt b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/netcoreapp3.1/PublicAPI.Shipped.txt similarity index 100% rename from src/OpenTelemetry.Exporter.Prometheus/.publicApi/net462/PublicAPI.Shipped.txt rename to src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/netcoreapp3.1/PublicAPI.Shipped.txt diff --git a/src/OpenTelemetry.Exporter.Prometheus/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt similarity index 63% rename from src/OpenTelemetry.Exporter.Prometheus/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt rename to src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt index 0c45b3f187c..f9271183c1c 100644 --- a/src/OpenTelemetry.Exporter.Prometheus/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt @@ -1,27 +1,17 @@ Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions Microsoft.AspNetCore.Builder.PrometheusExporterEndpointRouteBuilderExtensions -OpenTelemetry.Exporter.PrometheusExporter -OpenTelemetry.Exporter.PrometheusExporter.Collect.get -> System.Func -OpenTelemetry.Exporter.PrometheusExporter.Collect.set -> void -OpenTelemetry.Exporter.PrometheusExporter.PrometheusExporter(OpenTelemetry.Exporter.PrometheusExporterOptions options) -> void -OpenTelemetry.Exporter.PrometheusExporterOptions -OpenTelemetry.Exporter.PrometheusExporterOptions.HttpListenerPrefixes.get -> System.Collections.Generic.IReadOnlyCollection -OpenTelemetry.Exporter.PrometheusExporterOptions.HttpListenerPrefixes.set -> void -OpenTelemetry.Exporter.PrometheusExporterOptions.PrometheusExporterOptions() -> void -OpenTelemetry.Exporter.PrometheusExporterOptions.ScrapeEndpointPath.get -> string -OpenTelemetry.Exporter.PrometheusExporterOptions.ScrapeEndpointPath.set -> void -OpenTelemetry.Exporter.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.get -> int -OpenTelemetry.Exporter.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.set -> void -OpenTelemetry.Exporter.PrometheusExporterOptions.StartHttpListener.get -> bool -OpenTelemetry.Exporter.PrometheusExporterOptions.StartHttpListener.set -> void +OpenTelemetry.Exporter.Prometheus.AspNetCore.PrometheusExporterOptions +OpenTelemetry.Exporter.Prometheus.AspNetCore.PrometheusExporterOptions.PrometheusExporterOptions() -> void +OpenTelemetry.Exporter.Prometheus.AspNetCore.PrometheusExporterOptions.ScrapeEndpointPath.get -> string +OpenTelemetry.Exporter.Prometheus.AspNetCore.PrometheusExporterOptions.ScrapeEndpointPath.set -> void +OpenTelemetry.Exporter.Prometheus.AspNetCore.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.get -> int +OpenTelemetry.Exporter.Prometheus.AspNetCore.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.set -> void OpenTelemetry.Metrics.PrometheusExporterMeterProviderBuilderExtensions -override OpenTelemetry.Exporter.PrometheusExporter.Dispose(bool disposing) -> void -override OpenTelemetry.Exporter.PrometheusExporter.Export(in OpenTelemetry.Batch metrics) -> OpenTelemetry.ExportResult static Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions.UseOpenTelemetryPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Builder.IApplicationBuilder app) -> Microsoft.AspNetCore.Builder.IApplicationBuilder static Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions.UseOpenTelemetryPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, OpenTelemetry.Metrics.MeterProvider meterProvider, System.Func predicate, string path, System.Action configureBranchedPipeline) -> Microsoft.AspNetCore.Builder.IApplicationBuilder static Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions.UseOpenTelemetryPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, string path) -> Microsoft.AspNetCore.Builder.IApplicationBuilder static Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions.UseOpenTelemetryPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, System.Func predicate) -> Microsoft.AspNetCore.Builder.IApplicationBuilder -static OpenTelemetry.Metrics.PrometheusExporterMeterProviderBuilderExtensions.AddPrometheusExporter(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configure = null) -> OpenTelemetry.Metrics.MeterProviderBuilder static Microsoft.AspNetCore.Builder.PrometheusExporterEndpointRouteBuilderExtensions.MapPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder -static Microsoft.AspNetCore.Builder.PrometheusExporterEndpointRouteBuilderExtensions.MapPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string path) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder static Microsoft.AspNetCore.Builder.PrometheusExporterEndpointRouteBuilderExtensions.MapPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string path = null, OpenTelemetry.Metrics.MeterProvider meterProvider = null, System.Action configureBranchedPipeline = null) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder +static Microsoft.AspNetCore.Builder.PrometheusExporterEndpointRouteBuilderExtensions.MapPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string path) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder +static OpenTelemetry.Metrics.PrometheusExporterMeterProviderBuilderExtensions.AddPrometheusExporter(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configure = null) -> OpenTelemetry.Metrics.MeterProviderBuilder diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/AssemblyInfo.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/AssemblyInfo.cs new file mode 100644 index 00000000000..574fd52fb83 --- /dev/null +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/AssemblyInfo.cs @@ -0,0 +1,23 @@ +// +// 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. +// + +using System.Runtime.CompilerServices; + +#if SIGNED +[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] +#else +[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests")] +#endif diff --git a/src/OpenTelemetry.Exporter.Prometheus/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md similarity index 84% rename from src/OpenTelemetry.Exporter.Prometheus/CHANGELOG.md rename to src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md index 5787a17c1db..f2df4b1a83b 100644 --- a/src/OpenTelemetry.Exporter.Prometheus/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +* Split up Prometheus projects based on its hosting mechanism, HttpListener and AspNetCore, + into their own projects and assemblies. The shared code for both hosting mechanism + now lives in the `OpenTelemetry.Exporter.Prometheus.Shared` project and will not + be released. + ([#3430](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3430)) + * Added `IEndpointRouteBuilder` extension methods to help with Prometheus middleware configuration on ASP.NET Core ([#3295](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3295)) diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj new file mode 100644 index 00000000000..e7453e0bf52 --- /dev/null +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj @@ -0,0 +1,41 @@ + + + + + netcoreapp3.1 + AspNetCore middleware for hosting OpenTelemetry .NET Prometheus exporter + $(PackageTags);prometheus;metrics + core- + + + + + false + + + + $(DefineConstants);PROMETHEUS_ASPNETCORE + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/OpenTelemetry.Exporter.Prometheus/PrometheusExporterApplicationBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs similarity index 96% rename from src/OpenTelemetry.Exporter.Prometheus/PrometheusExporterApplicationBuilderExtensions.cs rename to src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs index ef96f4722f8..00fe170e6fb 100644 --- a/src/OpenTelemetry.Exporter.Prometheus/PrometheusExporterApplicationBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs @@ -19,8 +19,7 @@ using System; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; -using OpenTelemetry.Exporter; -using OpenTelemetry.Exporter.Prometheus; +using OpenTelemetry.Exporter.Prometheus.AspNetCore; using OpenTelemetry.Internal; using OpenTelemetry.Metrics; @@ -93,8 +92,8 @@ public static IApplicationBuilder UseOpenTelemetryPrometheusScrapingEndpoint(thi /// The to add /// middleware to. /// Optional - /// containing a otherwise the primary - /// SDK provider will be resolved using application services. + /// containing a Prometheus exporter otherwise the primary SDK provider + /// will be resolved using application services. /// Optional predicate for deciding if a given /// should be branched. If supplied is ignored. diff --git a/src/OpenTelemetry.Exporter.Prometheus/PrometheusExporterEndpointRouteBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs similarity index 95% rename from src/OpenTelemetry.Exporter.Prometheus/PrometheusExporterEndpointRouteBuilderExtensions.cs rename to src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs index 02b58a34201..372a96e8a81 100644 --- a/src/OpenTelemetry.Exporter.Prometheus/PrometheusExporterEndpointRouteBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs @@ -20,8 +20,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; -using OpenTelemetry.Exporter; -using OpenTelemetry.Exporter.Prometheus; +using OpenTelemetry.Exporter.Prometheus.AspNetCore; using OpenTelemetry.Internal; using OpenTelemetry.Metrics; @@ -70,8 +69,8 @@ public static IEndpointConventionBuilder MapPrometheusScrapingEndpoint(this IEnd /// If not provided then /// is used. /// Optional - /// containing a otherwise the primary - /// SDK provider will be resolved using application services. + /// containing a Prometheus exporter otherwise the primary SDK provider + /// will be resolved using application services. /// Optional callback to /// configure the branched pipeline. Called before registration of the /// Prometheus middleware. diff --git a/src/OpenTelemetry.Exporter.Prometheus/PrometheusExporterMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs similarity index 87% rename from src/OpenTelemetry.Exporter.Prometheus/PrometheusExporterMeterProviderBuilderExtensions.cs rename to src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs index ada7d1c90d1..fd6809d68ee 100644 --- a/src/OpenTelemetry.Exporter.Prometheus/PrometheusExporterMeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs @@ -15,11 +15,15 @@ // using System; -using OpenTelemetry.Exporter; +using OpenTelemetry.Exporter.Prometheus.AspNetCore; +using OpenTelemetry.Exporter.Prometheus.Shared; using OpenTelemetry.Internal; namespace OpenTelemetry.Metrics { + /// + /// Extension methods to simplify registering a PrometheusExporter. + /// public static class PrometheusExporterMeterProviderBuilderExtensions { /// @@ -48,8 +52,10 @@ private static MeterProviderBuilder AddPrometheusExporter(MeterProviderBuilder b configure?.Invoke(options); var exporter = new PrometheusExporter(options); - var reader = new BaseExportingMetricReader(exporter); - reader.TemporalityPreference = MetricReaderTemporalityPreference.Cumulative; + var reader = new BaseExportingMetricReader(exporter) + { + TemporalityPreference = MetricReaderTemporalityPreference.Cumulative, + }; return builder.AddReader(reader); } diff --git a/src/OpenTelemetry.Exporter.Prometheus/Implementation/PrometheusExporterMiddleware.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs similarity index 97% rename from src/OpenTelemetry.Exporter.Prometheus/Implementation/PrometheusExporterMiddleware.cs rename to src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs index 826ca135763..bebf084ce33 100644 --- a/src/OpenTelemetry.Exporter.Prometheus/Implementation/PrometheusExporterMiddleware.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs @@ -19,10 +19,11 @@ using System.Diagnostics; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using OpenTelemetry.Exporter.Prometheus.Shared; using OpenTelemetry.Internal; using OpenTelemetry.Metrics; -namespace OpenTelemetry.Exporter.Prometheus +namespace OpenTelemetry.Exporter.Prometheus.AspNetCore { /// /// ASP.NET Core middleware for exposing a Prometheus metrics scraping endpoint. diff --git a/src/OpenTelemetry.Exporter.Prometheus/README.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/README.md similarity index 58% rename from src/OpenTelemetry.Exporter.Prometheus/README.md rename to src/OpenTelemetry.Exporter.Prometheus.AspNetCore/README.md index ecac7ad4d41..855c20637a6 100644 --- a/src/OpenTelemetry.Exporter.Prometheus/README.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/README.md @@ -1,18 +1,24 @@ -# Prometheus Exporter for OpenTelemetry .NET +# Prometheus Exporter AspNetCore for OpenTelemetry .NET -[![NuGet](https://img.shields.io/nuget/v/OpenTelemetry.Exporter.Prometheus.svg)](https://www.nuget.org/packages/OpenTelemetry.Exporter.Prometheus) -[![NuGet](https://img.shields.io/nuget/dt/OpenTelemetry.Exporter.Prometheus.svg)](https://www.nuget.org/packages/OpenTelemetry.Exporter.Prometheus) +[![NuGet](https://img.shields.io/nuget/v/OpenTelemetry.Exporter.Prometheus.AspNetCore.svg)](https://www.nuget.org/packages/OpenTelemetry.Exporter.Prometheus.AspNetCore) +[![NuGet](https://img.shields.io/nuget/dt/OpenTelemetry.Exporter.Prometheus.AspNetCore.svg)](https://www.nuget.org/packages/OpenTelemetry.Exporter.Prometheus.AspNetCore) + +An [OpenTelemetry Prometheus exporter](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk_exporters/prometheus.md) +for configuring an ASP.NET Core application with an endpoint for Prometheus +to scrape. ## Prerequisite * [Get Prometheus](https://prometheus.io/docs/introduction/first_steps/) -## Steps to enable OpenTelemetry.Exporter.Prometheus +## Steps to enable OpenTelemetry.Exporter.Prometheus.AspNetCore ### Step 1: Install Package +Install + ```shell -dotnet add package OpenTelemetry.Exporter.Prometheus +dotnet add package OpenTelemetry.Exporter.Prometheus.AspNetCore ``` ### Step 2: Configure OpenTelemetry MeterProvider @@ -32,14 +38,15 @@ dotnet add package OpenTelemetry.Exporter.Prometheus register the Prometheus exporter. ```csharp - using var meterProvider = Sdk.CreateMeterProviderBuilder() + var meterProvider = Sdk.CreateMeterProviderBuilder() .AddPrometheusExporter() .Build(); + builder.Services.AddSingleton(meterProvider); ``` ### Step 3: Configure Prometheus Scraping Endpoint -* On .NET Core 3.1+ register Prometheus scraping middleware using the +* Register Prometheus scraping middleware using the `UseOpenTelemetryPrometheusScrapingEndpoint` extension: ```csharp @@ -72,46 +79,15 @@ dotnet add package OpenTelemetry.Exporter.Prometheus } ``` -* On .NET Framework an HTTP listener is automatically started which will respond - to scraping requests. See the [Configuration](#configuration) section for - details on the settings available. This may also be turned on in .NET Core (it - is OFF by default) when the ASP.NET Core pipeline is not available for - middleware registration. - ## Configuration The `PrometheusExporter` can be configured using the `PrometheusExporterOptions` -properties. Refer to -[`TestPrometheusExporter.cs`](../../examples/Console/TestPrometheusExporter.cs) -for example use. - -### StartHttpListener - -Set to `true` to start an HTTP listener which will respond to Prometheus scrape -requests using the [HttpListenerPrefixes](#httplistenerprefixes) and -[ScrapeEndpointPath](#scrapeendpointpath) options. - -Defaults: - -* On .NET Framework this is `true` by default. - -* On .NET Core 3.1+ this is `false` by default. Users running ASP.NET Core - should use the `UseOpenTelemetryPrometheusScrapingEndpoint` extension to - register the scraping middleware instead of using the listener. - -### HttpListenerPrefixes - -Defines the prefixes which will be used by the listener when `StartHttpListener` -is `true`. The default value is `["http://localhost:9464/"]`. You may specify -multiple endpoints. - -For details see: -[HttpListenerPrefixCollection.Add(String)](https://docs.microsoft.com/dotnet/api/system.net.httplistenerprefixcollection.add) +properties. ### ScrapeEndpointPath -Defines the path for the Prometheus scrape endpoint for -either the HTTP listener or the middleware registered by +Defines the path for the Prometheus scrape endpoint for the middleware +registered by `UseOpenTelemetryPrometheusScrapingEndpoint`. Default value: `"/metrics"`. ### ScrapeResponseCacheDurationMilliseconds diff --git a/src/OpenTelemetry.Exporter.Prometheus/.publicApi/netcoreapp3.1/PublicAPI.Shipped.txt b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Shipped.txt similarity index 100% rename from src/OpenTelemetry.Exporter.Prometheus/.publicApi/netcoreapp3.1/PublicAPI.Shipped.txt rename to src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Shipped.txt diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt new file mode 100644 index 00000000000..a8f01b6d787 --- /dev/null +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt @@ -0,0 +1,12 @@ +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.PrometheusExporterOptions() -> void +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.ScrapeEndpointPath.get -> string +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.ScrapeEndpointPath.set -> void +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.get -> int +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.set -> void +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.Prefixes.get -> System.Collections.Generic.IReadOnlyCollection +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.Prefixes.set -> void +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.PrometheusHttpListenerOptions() -> void +OpenTelemetry.Metrics.PrometheusExporterHttpListenerMeterProviderBuilderExtensions +static OpenTelemetry.Metrics.PrometheusExporterHttpListenerMeterProviderBuilderExtensions.AddPrometheusHttpListener(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configureExporterOptions = null, System.Action configureListenerOptions = null) -> OpenTelemetry.Metrics.MeterProviderBuilder diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Shipped.txt b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Shipped.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt new file mode 100644 index 00000000000..a8f01b6d787 --- /dev/null +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -0,0 +1,12 @@ +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.PrometheusExporterOptions() -> void +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.ScrapeEndpointPath.get -> string +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.ScrapeEndpointPath.set -> void +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.get -> int +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.set -> void +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.Prefixes.get -> System.Collections.Generic.IReadOnlyCollection +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.Prefixes.set -> void +OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.PrometheusHttpListenerOptions() -> void +OpenTelemetry.Metrics.PrometheusExporterHttpListenerMeterProviderBuilderExtensions +static OpenTelemetry.Metrics.PrometheusExporterHttpListenerMeterProviderBuilderExtensions.AddPrometheusHttpListener(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configureExporterOptions = null, System.Action configureListenerOptions = null) -> OpenTelemetry.Metrics.MeterProviderBuilder diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md new file mode 100644 index 00000000000..9f4ac19cfa2 --- /dev/null +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -0,0 +1,94 @@ +# Changelog + +## Unreleased + +* Split up Prometheus projects based on its hosting mechanism, HttpListener and AspNetCore, + into their own projects and assemblies. The shared code for both hosting mechanism + now lives in the `OpenTelemetry.Exporter.Prometheus.Shared` project and will not + be released. + ([#3430](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3430)) + +## 1.3.0-rc.2 + +Released 2022-June-1 + +## 1.3.0-beta.2 + +Released 2022-May-16 + +## 1.3.0-beta.1 + +Released 2022-Apr-15 + +* Added `IApplicationBuilder` extension methods to help with Prometheus + middleware configuration on ASP.NET Core + ([#3029](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3029)) + +* Changed Prometheus exporter to return 204 No Content and log a warning event + if there are no metrics to collect. + +* Removes .NET Framework 4.6.1. The minimum .NET Framework + version supported is .NET 4.6.2. ([#3190](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3190)) + +## 1.2.0-rc5 + +Released 2022-Apr-12 + +## 1.2.0-rc4 + +Released 2022-Mar-30 + +## 1.2.0-rc3 + +Released 2022-Mar-04 + +## 1.2.0-rc2 + +Released 2022-Feb-02 + +* Update default `httpListenerPrefixes` for PrometheusExporter to be `http://localhost:9464/`. +([#2783](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2783)) + +## 1.2.0-rc1 + +Released 2021-Nov-29 + +* Bug fix for handling Histogram with empty buckets. + ([#2651](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2651)) + +## 1.2.0-beta2 + +Released 2021-Nov-19 + +* Added scrape endpoint response caching feature & + `ScrapeResponseCacheDurationMilliseconds` option + ([#2610](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2610)) + +## 1.2.0-beta1 + +Released 2021-Oct-08 + +## 1.2.0-alpha4 + +Released 2021-Sep-23 + +## 1.2.0-alpha3 + +Released 2021-Sep-13 + +* Bug fixes + ([#2289](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2289)) + ([#2309](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2309)) + +## 1.2.0-alpha2 + +Released 2021-Aug-24 + +* Revamped to support the new Metrics API/SDK. + Supports Counter, Gauge and Histogram. + +## 1.0.0-rc1.1 + +Released 2020-Nov-17 + +* Initial release diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj new file mode 100644 index 00000000000..7769147c0f6 --- /dev/null +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj @@ -0,0 +1,36 @@ + + + + + netstandard2.0;net462 + Stand-alone HttpListener for hosting OpenTelemetry .NET exporter + $(PackageTags);prometheus;metrics + core- + + + + + false + + + + $(DefineConstants);PROMETHEUS_HTTPLISTENER + + + + + + + + + + + + + + + + + + diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusExporterHttpListenerMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusExporterHttpListenerMeterProviderBuilderExtensions.cs new file mode 100644 index 00000000000..9ab648b6be0 --- /dev/null +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusExporterHttpListenerMeterProviderBuilderExtensions.cs @@ -0,0 +1,104 @@ +// +// 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. +// + +using System; +using OpenTelemetry.Exporter.Prometheus.HttpListener; +using OpenTelemetry.Exporter.Prometheus.Shared; +using OpenTelemetry.Internal; + +namespace OpenTelemetry.Metrics +{ + /// + /// Extension methods to simplify registering a PrometheusHttpListener. + /// + public static class PrometheusExporterHttpListenerMeterProviderBuilderExtensions + { + /// + /// Adds Prometheus exporter to MeterProviderBuilder. + /// + /// builder to use. + /// Exporter configuration options. + /// HttpListener options. + /// The instance of to chain calls. + public static MeterProviderBuilder AddPrometheusHttpListener( + this MeterProviderBuilder builder, + Action configureExporterOptions = null, + Action configureListenerOptions = null) + { + Guard.ThrowIfNull(builder); + + if (builder is IDeferredMeterProviderBuilder deferredMeterProviderBuilder) + { + return deferredMeterProviderBuilder.Configure((sp, builder) => + { + AddPrometheusHttpListener( + builder, + sp.GetOptions(), + sp.GetOptions(), + configureExporterOptions, + configureListenerOptions); + }); + } + + return AddPrometheusHttpListener( + builder, + new PrometheusExporterOptions(), + new PrometheusHttpListenerOptions(), + configureExporterOptions, + configureListenerOptions); + } + + private static MeterProviderBuilder AddPrometheusHttpListener( + MeterProviderBuilder builder, + PrometheusExporterOptions exporterOptions, + PrometheusHttpListenerOptions listenerOptions, + Action configureExporterOptions = null, + Action configureListenerOptions = null) + { + configureExporterOptions?.Invoke(exporterOptions); + configureListenerOptions?.Invoke(listenerOptions); + + var exporter = new PrometheusExporter(exporterOptions); + + var reader = new BaseExportingMetricReader(exporter) + { + TemporalityPreference = MetricReaderTemporalityPreference.Cumulative, + }; + + const string HttpListenerStartFailureExceptionMessage = "PrometheusExporter HttpListener could not be started."; + try + { + var listener = new PrometheusHttpListener(exporter, listenerOptions); + exporter.OnDispose = () => listener.Dispose(); + listener.Start(); + } + catch (Exception ex) + { + try + { + reader.Dispose(); + } + catch + { + } + + throw new InvalidOperationException(HttpListenerStartFailureExceptionMessage, ex); + } + + return builder.AddReader(reader); + } + } +} diff --git a/src/OpenTelemetry.Exporter.Prometheus/Implementation/PrometheusExporterHttpServer.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs similarity index 82% rename from src/OpenTelemetry.Exporter.Prometheus/Implementation/PrometheusExporterHttpServer.cs rename to src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs index 554f5cf3470..28700603b1b 100644 --- a/src/OpenTelemetry.Exporter.Prometheus/Implementation/PrometheusExporterHttpServer.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,37 +18,36 @@ using System.Net; using System.Threading; using System.Threading.Tasks; +using OpenTelemetry.Exporter.Prometheus.Shared; using OpenTelemetry.Internal; -namespace OpenTelemetry.Exporter.Prometheus +namespace OpenTelemetry.Exporter.Prometheus.HttpListener { - /// - /// An HTTP listener used to expose Prometheus metrics. - /// - internal sealed class PrometheusExporterHttpServer : IDisposable + internal sealed class PrometheusHttpListener : IDisposable { private readonly PrometheusExporter exporter; - private readonly HttpListener httpListener = new(); + private readonly System.Net.HttpListener httpListener = new(); private readonly object syncObject = new(); private CancellationTokenSource tokenSource; private Task workerThread; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The instance. - public PrometheusExporterHttpServer(PrometheusExporter exporter) + /// The exporter instance. + /// The configured HttpListener options. + public PrometheusHttpListener(PrometheusExporter exporter, PrometheusHttpListenerOptions options) { Guard.ThrowIfNull(exporter); - this.exporter = exporter; - if ((exporter.Options.HttpListenerPrefixes?.Count ?? 0) <= 0) + if ((options.Prefixes?.Count ?? 0) <= 0) { - throw new ArgumentException("No HttpListenerPrefixes were specified on PrometheusExporterOptions."); + throw new ArgumentException("No Prefixes were specified on PrometheusHttpListenerOptions."); } - string path = exporter.Options.ScrapeEndpointPath ?? PrometheusExporterOptions.DefaultScrapeEndpointPath; + this.exporter = exporter; + string path = this.exporter.Options.ScrapeEndpointPath ?? PrometheusExporterOptions.DefaultScrapeEndpointPath; if (!path.StartsWith("/")) { path = $"/{path}"; @@ -59,16 +58,16 @@ public PrometheusExporterHttpServer(PrometheusExporter exporter) path = $"{path}/"; } - foreach (string prefix in exporter.Options.HttpListenerPrefixes) + foreach (string prefix in options.Prefixes) { this.httpListener.Prefixes.Add($"{prefix.TrimEnd('/')}{path}"); } } /// - /// Start Http Server. + /// Start the HttpListener. /// - /// An optional that can be used to stop the HTTP server. + /// An optional that can be used to stop the HTTP listener. public void Start(CancellationToken token = default) { lock (this.syncObject) @@ -88,7 +87,7 @@ public void Start(CancellationToken token = default) } /// - /// Stop exporter. + /// Gracefully stop the PrometheusHttpListener. /// public void Stop() { @@ -108,9 +107,10 @@ public void Stop() /// public void Dispose() { + this.Stop(); + if (this.httpListener != null && this.httpListener.IsListening) { - this.Stop(); this.httpListener.Close(); } } diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs new file mode 100644 index 00000000000..264a5f229fd --- /dev/null +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs @@ -0,0 +1,56 @@ +// +// 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. +// + +using System; +using System.Collections.Generic; +using OpenTelemetry.Internal; + +namespace OpenTelemetry.Exporter.Prometheus.HttpListener +{ + /// + /// options. + /// + public class PrometheusHttpListenerOptions + { + private IReadOnlyCollection prefixes = new string[] { "http://localhost:9464/" }; + + /// + /// Gets or sets the prefixes to use for the http listener. + /// Default value: http://localhost:9464/. + /// + public IReadOnlyCollection Prefixes + { + get => this.prefixes; + set + { + Guard.ThrowIfNull(value); + + foreach (string inputUri in value) + { + if (!(Uri.TryCreate(inputUri, UriKind.Absolute, out var uri) && + (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps))) + { + throw new ArgumentException( + "Prometheus HttpListener prefix path should be a valid URI with http/https scheme.", + nameof(this.prefixes)); + } + } + + this.prefixes = value; + } + } + } +} diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md new file mode 100644 index 00000000000..8e772a4754e --- /dev/null +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md @@ -0,0 +1,72 @@ +# Prometheus Exporter HttpListener for OpenTelemetry .NET + +[![NuGet](https://img.shields.io/nuget/v/OpenTelemetry.Exporter.Prometheus.HttpListener.svg)](https://www.nuget.org/packages/OpenTelemetry.Exporter.Prometheus.HttpListener) +[![NuGet](https://img.shields.io/nuget/dt/OpenTelemetry.Exporter.Prometheus.HttpListener.svg)](https://www.nuget.org/packages/OpenTelemetry.Exporter.Prometheus.HttpListener) + +An [OpenTelemetry Prometheus exporter](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk_exporters/prometheus.md) +that configures an [HttpListener](https://docs.microsoft.com/dotnet/api/system.net.httplistener) +instance for Prometheus to scrape. + +## Prerequisite + +* [Get Prometheus](https://prometheus.io/docs/introduction/first_steps/) + +## Steps to enable OpenTelemetry.Exporter.Prometheus.HttpListener + +### Step 1: Install Package + +Install + +```shell +dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener +``` + +### Step 2: Add PrometheusHttpListener + +Add and configure `PrometheusHttpListener` with `PrometheusExporterOptions` as +the first argument and `PrometheusHttpListenerOptions` as the second argument. + +For example: + +```csharp +using var meterProvider = Sdk.CreateMeterProviderBuilder() + .AddMeter(MyMeter.Name) + .AddPrometheusHttpListener( + exporterOptions => exporterOptions.ScrapeResponseCacheDurationMilliseconds = 0, + listenerOptions => listenerOptions.Prefixes = new string[] { "http://localhost:9464/" }) + .Build(); +``` + +### Prefixes + +Defines the prefixes which will be used by the listener. The default value is `["http://localhost:9464/"]`. +You may specify multiple endpoints. + +For details see: +[HttpListenerPrefixCollection.Add(String)](https://docs.microsoft.com/dotnet/api/system.net.httplistenerprefixcollection.add) + +### ScrapeEndpointPath + +Defines the path for the Prometheus scrape endpoint for by +`UseOpenTelemetryPrometheusScrapingEndpoint`. Default value: `"/metrics"`. + +### ScrapeResponseCacheDurationMilliseconds + +Configures scrape endpoint response caching. Multiple scrape requests within the +cache duration time period will receive the same previously generated response. +The default value is `10000` (10 seconds). Set to `0` to disable response +caching. + +## Troubleshooting + +This component uses an +[EventSource](https://docs.microsoft.com/dotnet/api/system.diagnostics.tracing.eventsource) +with the name "OpenTelemetry-Exporter-Prometheus" for its internal logging. +Please refer to [SDK +troubleshooting](../OpenTelemetry/README.md#troubleshooting) for instructions on +seeing these internal logs. + +## References + +* [OpenTelemetry Project](https://opentelemetry.io/) +* [Prometheus](https://prometheus.io) diff --git a/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/net462/PublicAPI.Shipped.txt b/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/net462/PublicAPI.Shipped.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/net462/PublicAPI.Unshipped.txt new file mode 100644 index 00000000000..d61e4fb9ed9 --- /dev/null +++ b/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/net462/PublicAPI.Unshipped.txt @@ -0,0 +1,6 @@ +OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions +OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.PrometheusExporterOptions() -> void +OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.ScrapeEndpointPath.get -> string +OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.ScrapeEndpointPath.set -> void +OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.get -> int +OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.set -> void diff --git a/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/netcoreapp3.1/PublicAPI.Shipped.txt b/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/netcoreapp3.1/PublicAPI.Shipped.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt new file mode 100644 index 00000000000..539467ab62d --- /dev/null +++ b/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt @@ -0,0 +1,6 @@ +OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions +OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.PrometheusExporterOptions() -> void +OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.ScrapeEndpointPath.get -> string +OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.ScrapeEndpointPath.set -> void +OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.get -> int +OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.set -> void diff --git a/src/OpenTelemetry.Exporter.Prometheus/AssemblyInfo.cs b/src/OpenTelemetry.Exporter.Prometheus.Shared/AssemblyInfo.cs similarity index 78% rename from src/OpenTelemetry.Exporter.Prometheus/AssemblyInfo.cs rename to src/OpenTelemetry.Exporter.Prometheus.Shared/AssemblyInfo.cs index 619668af3b4..d4361cf0913 100644 --- a/src/OpenTelemetry.Exporter.Prometheus/AssemblyInfo.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.Shared/AssemblyInfo.cs @@ -18,8 +18,8 @@ #if SIGNED [assembly: InternalsVisibleTo("Benchmarks, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] -[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] +[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.Shared.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] #else [assembly: InternalsVisibleTo("Benchmarks")] -[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.Tests")] +[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.Shared.Tests")] #endif diff --git a/src/OpenTelemetry.Exporter.Prometheus/OpenTelemetry.Exporter.Prometheus.csproj b/src/OpenTelemetry.Exporter.Prometheus.Shared/OpenTelemetry.Exporter.Prometheus.Shared.csproj similarity index 84% rename from src/OpenTelemetry.Exporter.Prometheus/OpenTelemetry.Exporter.Prometheus.csproj rename to src/OpenTelemetry.Exporter.Prometheus.Shared/OpenTelemetry.Exporter.Prometheus.Shared.csproj index c53448ea4d7..169e97169c4 100644 --- a/src/OpenTelemetry.Exporter.Prometheus/OpenTelemetry.Exporter.Prometheus.csproj +++ b/src/OpenTelemetry.Exporter.Prometheus.Shared/OpenTelemetry.Exporter.Prometheus.Shared.csproj @@ -3,13 +3,8 @@ netcoreapp3.1;net462 - Prometheus exporter for OpenTelemetry .NET + Prometheus exporter shared code for both Prometheus exporter HttpListener and Prometheus exporter AspNetCore $(PackageTags);prometheus;metrics - core- - - - - $(NoWarn),1591 - net6.0;netcoreapp3.1 - $(TargetFrameworks);net462 + netcoreapp3.1 + + false @@ -19,12 +20,8 @@ - - - - - + diff --git a/test/OpenTelemetry.Exporter.Prometheus.Tests/PrometheusCollectionManagerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusCollectionManagerTests.cs similarity index 98% rename from test/OpenTelemetry.Exporter.Prometheus.Tests/PrometheusCollectionManagerTests.cs rename to test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusCollectionManagerTests.cs index 60945c6f997..adeb3ac29c0 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.Tests/PrometheusCollectionManagerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusCollectionManagerTests.cs @@ -21,11 +21,12 @@ #endif using System.Threading; using System.Threading.Tasks; +using OpenTelemetry.Exporter.Prometheus.Shared; using OpenTelemetry.Metrics; using OpenTelemetry.Tests; using Xunit; -namespace OpenTelemetry.Exporter.Prometheus.Tests +namespace OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests { public sealed class PrometheusCollectionManagerTests { diff --git a/test/OpenTelemetry.Exporter.Prometheus.Tests/PrometheusExporterMiddlewareTests.cs b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs similarity index 97% rename from test/OpenTelemetry.Exporter.Prometheus.Tests/PrometheusExporterMiddlewareTests.cs rename to test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs index a11ea430423..ff8b0bdc484 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.Tests/PrometheusExporterMiddlewareTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs @@ -31,7 +31,7 @@ using OpenTelemetry.Tests; using Xunit; -namespace OpenTelemetry.Exporter.Prometheus.Tests +namespace OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests { public sealed class PrometheusExporterMiddlewareTests { @@ -224,10 +224,6 @@ private static async Task RunPrometheusExporterMiddlewareIntegrationTest( .AddPrometheusExporter(o => { configureOptions?.Invoke(o); - if (o.StartHttpListener) - { - throw new InvalidOperationException("StartHttpListener should be false on .NET Core 3.1+."); - } })); } diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests.csproj b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests.csproj new file mode 100644 index 00000000000..22b68c4e9ea --- /dev/null +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests.csproj @@ -0,0 +1,29 @@ + + + Unit test project for Prometheus Exporter HttpListener for OpenTelemetry + + net462 + + false + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + + + + diff --git a/test/OpenTelemetry.Exporter.Prometheus.Tests/PrometheusExporterHttpServerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusExporterHttpListenerTests.cs similarity index 63% rename from test/OpenTelemetry.Exporter.Prometheus.Tests/PrometheusExporterHttpServerTests.cs rename to test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusExporterHttpListenerTests.cs index 6ba169505d8..d197ffea85a 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.Tests/PrometheusExporterHttpServerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusExporterHttpListenerTests.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,24 +24,12 @@ using OpenTelemetry.Tests; using Xunit; -namespace OpenTelemetry.Exporter.Prometheus.Tests +namespace OpenTelemetry.Exporter.Prometheus.HttpListener.Tests { - public class PrometheusExporterHttpServerTests + public class PrometheusExporterHttpListenerTests { private readonly string meterName = Utils.GetCurrentMethodName(); - [Fact] - public async Task PrometheusExporterHttpServerIntegration() - { - await this.RunPrometheusExporterHttpServerIntegrationTest(); - } - - [Fact] - public async Task PrometheusExporterHttpServerIntegration_NoMetrics() - { - await this.RunPrometheusExporterHttpServerIntegrationTest(skipMetrics: true); - } - [Theory] [InlineData("http://example.com")] [InlineData("https://example.com")] @@ -50,10 +38,7 @@ public async Task PrometheusExporterHttpServerIntegration_NoMetrics() public void ServerEndpointSanityCheckPositiveTest(params string[] uris) { using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() - .AddPrometheusExporter(opt => - { - opt.HttpListenerPrefixes = uris; - }) + .AddPrometheusHttpListener(null, listenerOptions => listenerOptions.Prefixes = uris) .Build(); } @@ -67,10 +52,7 @@ public void ServerEndpointSanityCheckNegativeTest(params string[] uris) try { using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() - .AddPrometheusExporter(opt => - { - opt.HttpListenerPrefixes = uris; - }) + .AddPrometheusHttpListener(null, listenerOptions => listenerOptions.Prefixes = uris) .Build(); } catch (Exception ex) @@ -79,66 +61,45 @@ public void ServerEndpointSanityCheckNegativeTest(params string[] uris) { Assert.Equal("System.ArgumentException", ex.GetType().ToString()); #if NETFRAMEWORK - Assert.Equal("Prometheus server path should be a valid URI with http/https scheme.\r\nParameter name: httpListenerPrefixes", ex.Message); + Assert.Equal("Prometheus HttpListener prefix path should be a valid URI with http/https scheme.\r\nParameter name: prefixes", ex.Message); #else - Assert.Equal("Prometheus server path should be a valid URI with http/https scheme. (Parameter 'httpListenerPrefixes')", ex.Message); + Assert.Equal("Prometheus HttpListener prefix path should be a valid URI with http/https scheme. (Parameter 'prefixes')", ex.Message); #endif } } } + [Fact] + public async Task PrometheusExporterHttpServerIntegration() + { + await this.RunPrometheusExporterHttpServerIntegrationTest(); + } + + [Fact] + public async Task PrometheusExporterHttpServerIntegration_NoMetrics() + { + await this.RunPrometheusExporterHttpServerIntegrationTest(skipMetrics: true); + } + private async Task RunPrometheusExporterHttpServerIntegrationTest(bool skipMetrics = false) { Random random = new Random(); + int retryAttempts = 5; int port = 0; - int retryCount = 5; - MeterProvider provider; string address = null; + MeterProvider provider; using var meter = new Meter(this.meterName); - while (true) + while (retryAttempts-- != 0) { - try - { - port = random.Next(2000, 5000); - provider = Sdk.CreateMeterProviderBuilder() - .AddMeter(meter.Name) - .AddPrometheusExporter(o => - { -#if NETFRAMEWORK - bool expectedDefaultState = true; -#else - bool expectedDefaultState = false; -#endif - if (o.StartHttpListener != expectedDefaultState) - { - throw new InvalidOperationException("StartHttpListener value is unexpected."); - } - - if (!o.StartHttpListener) - { - o.StartHttpListener = true; - } + port = random.Next(2000, 5000); + address = $"http://localhost:{port}/"; - address = $"http://localhost:{port}/"; - o.HttpListenerPrefixes = new string[] { address }; - }) - .Build(); - break; - } - catch (Exception ex) - { - if (ex.Message != PrometheusExporter.HttpListenerStartFailureExceptionMessage) - { - throw; - } - - if (retryCount-- <= 0) - { - throw new InvalidOperationException("HttpListener could not be started."); - } - } + provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddPrometheusHttpListener(null, listenerOptions => listenerOptions.Prefixes = new string[] { address }) + .Build(); } var tags = new KeyValuePair[] @@ -155,7 +116,6 @@ private async Task RunPrometheusExporterHttpServerIntegrationTest(bool skipMetri } using HttpClient client = new HttpClient(); - using var response = await client.GetAsync($"{address}metrics").ConfigureAwait(false); if (!skipMetrics) diff --git a/test/OpenTelemetry.Exporter.Prometheus.Shared.Tests/OpenTelemetry.Exporter.Prometheus.Shared.Tests.csproj b/test/OpenTelemetry.Exporter.Prometheus.Shared.Tests/OpenTelemetry.Exporter.Prometheus.Shared.Tests.csproj new file mode 100644 index 00000000000..640b3541f2e --- /dev/null +++ b/test/OpenTelemetry.Exporter.Prometheus.Shared.Tests/OpenTelemetry.Exporter.Prometheus.Shared.Tests.csproj @@ -0,0 +1,32 @@ + + + Unit test project of Prometheus exporter shared code for both Prometheus exporter HttpListener and Prometheus Exporter AspNetCore + + netcoreapp3.1 + $(TargetFrameworks);net462 + + false + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + + + + + + diff --git a/test/OpenTelemetry.Exporter.Prometheus.Tests/PrometheusSerializerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.Shared.Tests/PrometheusSerializerTests.cs similarity index 99% rename from test/OpenTelemetry.Exporter.Prometheus.Tests/PrometheusSerializerTests.cs rename to test/OpenTelemetry.Exporter.Prometheus.Shared.Tests/PrometheusSerializerTests.cs index cb2ea1d5641..1def2012b8e 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.Tests/PrometheusSerializerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.Shared.Tests/PrometheusSerializerTests.cs @@ -21,7 +21,7 @@ using OpenTelemetry.Tests; using Xunit; -namespace OpenTelemetry.Exporter.Prometheus.Tests +namespace OpenTelemetry.Exporter.Prometheus.Shared.Tests { public sealed class PrometheusSerializerTests { diff --git a/test/OpenTelemetry.Tests.Stress.Logs/OpenTelemetry.Tests.Stress.Logs.csproj b/test/OpenTelemetry.Tests.Stress.Logs/OpenTelemetry.Tests.Stress.Logs.csproj index 8093f8d6d6f..ff88e203fb4 100644 --- a/test/OpenTelemetry.Tests.Stress.Logs/OpenTelemetry.Tests.Stress.Logs.csproj +++ b/test/OpenTelemetry.Tests.Stress.Logs/OpenTelemetry.Tests.Stress.Logs.csproj @@ -8,7 +8,7 @@ - + diff --git a/test/OpenTelemetry.Tests.Stress.Metrics/OpenTelemetry.Tests.Stress.Metrics.csproj b/test/OpenTelemetry.Tests.Stress.Metrics/OpenTelemetry.Tests.Stress.Metrics.csproj index 46fcfb47bd2..49134943927 100644 --- a/test/OpenTelemetry.Tests.Stress.Metrics/OpenTelemetry.Tests.Stress.Metrics.csproj +++ b/test/OpenTelemetry.Tests.Stress.Metrics/OpenTelemetry.Tests.Stress.Metrics.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs b/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs index 8838a1854c9..6a56c5a4bcb 100644 --- a/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs +++ b/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs @@ -46,12 +46,9 @@ public static void Main() using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(TestMeter.Name) - .AddPrometheusExporter(options => - { - options.StartHttpListener = true; - options.HttpListenerPrefixes = new string[] { $"http://localhost:9185/" }; - options.ScrapeResponseCacheDurationMilliseconds = 0; - }) + .AddPrometheusHttpListener( + exporterOptions => exporterOptions.ScrapeResponseCacheDurationMilliseconds = 0, + listenerOptions => listenerOptions.Prefixes = new string[] { $"http://localhost:9185/" }) .Build(); Stress(prometheusPort: 9184); diff --git a/test/OpenTelemetry.Tests.Stress/OpenTelemetry.Tests.Stress.csproj b/test/OpenTelemetry.Tests.Stress/OpenTelemetry.Tests.Stress.csproj index 32481d41662..73b65c17227 100644 --- a/test/OpenTelemetry.Tests.Stress/OpenTelemetry.Tests.Stress.csproj +++ b/test/OpenTelemetry.Tests.Stress/OpenTelemetry.Tests.Stress.csproj @@ -10,6 +10,6 @@ - + diff --git a/test/OpenTelemetry.Tests.Stress/README.md b/test/OpenTelemetry.Tests.Stress/README.md index a22d7b6788f..0013c573ad6 100644 --- a/test/OpenTelemetry.Tests.Stress/README.md +++ b/test/OpenTelemetry.Tests.Stress/README.md @@ -41,7 +41,7 @@ Running (concurrency = 1), press to stop... ``` The stress test metrics are exposed via -[PrometheusExporter](../../src/OpenTelemetry.Exporter.Prometheus/README.md), +[Prometheus HttpListener](../../src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md), which can be accessed via [http://localhost:9184/metrics/](http://localhost:9184/metrics/). diff --git a/test/OpenTelemetry.Tests.Stress/Skeleton.cs b/test/OpenTelemetry.Tests.Stress/Skeleton.cs index 2ff889d21a3..e372b108710 100644 --- a/test/OpenTelemetry.Tests.Stress/Skeleton.cs +++ b/test/OpenTelemetry.Tests.Stress/Skeleton.cs @@ -75,12 +75,9 @@ public static void Stress(int concurrency = 0, int prometheusPort = 0) using var meterProvider = prometheusPort != 0 ? Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddRuntimeInstrumentation() - .AddPrometheusExporter(options => - { - options.StartHttpListener = true; - options.HttpListenerPrefixes = new string[] { $"http://localhost:{prometheusPort}/" }; - options.ScrapeResponseCacheDurationMilliseconds = 0; - }) + .AddPrometheusHttpListener( + exporterOptions => exporterOptions.ScrapeResponseCacheDurationMilliseconds = 0, + listenerOptions => listenerOptions.Prefixes = new string[] { $"http://localhost:{prometheusPort}/" }) .Build() : null; var statistics = new long[concurrency]; From 8a4d395452717dba4c3f3636c841c192707f7cbf Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Thu, 28 Jul 2022 18:08:47 -0700 Subject: [PATCH 38/58] Fix nits (#3502) --- .../CHANGELOG.md | 8 +-- src/OpenTelemetry/CHANGELOG.md | 71 ++----------------- 2 files changed, 9 insertions(+), 70 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index 3766ca848aa..241df1ee7f8 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -4,18 +4,18 @@ * Metrics instrumentation to correctly populate `http.flavor` tag. (1.1 instead of HTTP/1.1 etc.) - ([3379](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3379)) + ([#3379](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3379)) * Tracing instrumentation to populate `http.flavor` tag. - ([3372](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3372)) + ([#3372](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3372)) * Tracing instrumentation to populate `http.scheme` tag. -([3392](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3392)) + ([#3392](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3392)) ## 1.0.0-rc9.4 Released 2022-Jun-03 * Added additional metric dimensions. - ([3247](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3247)) + ([#3247](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3247)) * Removes net5.0 target as .NET 5.0 is going out of support. The package keeps netstandard2.1 target, so it can still be used with .NET5.0 apps. diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index c50635a1d78..99114364c52 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -9,31 +9,23 @@ activity will be created irrespective of SamplingResult, to maintain context propagation. ([#3329](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3329)) - * Fix issue where a measurement would be dropped when recording it with a null-valued tag. ([#3325](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3325)) - * `CompositeProcessor` will now ensure `ParentProvider` is set on its children ([#3368](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3368)) - * Added `ForceFlush` and helper ctors on `OpenTelemetryLoggerProvider` ([#3364](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3364)) - * `Timestamp`, `TraceId`, `SpanId`, `TraceFlags`, `TraceState`, `CategoryName`, `LogLevel`, `EventId`, & `Exception` properties on `LogRecord` now expose `set` methods ([#3378](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3378)) - * Handle possible exception when initializing the default service name. ([#3405](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3405)) - * `LogRecord` instances are now reused to reduce memory pressure ([#3385](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3385)) - * Fix exact match of activity source name when `wildcard` is used. ([#3446](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3446)) - * Added AddOpenTelemetry `ILoggingBuilder` extensions which accept `OpenTelemetryLoggerProvider` directly ([#3489](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3489)) @@ -70,7 +62,6 @@ Released 2022-Apr-15 * Removes .NET Framework 4.6.1. The minimum .NET Framework version supported is .NET 4.6.2. ([#3190](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3190)) - * Bumped minimum required version of `Microsoft.Extensions.Logging` and `Microsoft.Extensions.Logging.Configuration` to 3.1.0 ([#2582](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3196)) @@ -99,13 +90,11 @@ Released 2022-Apr-12 [DiagnosticSource version 7.0 onwards](https://www.nuget.org/packages/System.Diagnostics.DiagnosticSource/7.0.0-preview.2.22152.2), they will be aggregated using `AggregationTemporality.Cumulative`. ([#3153](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3153)) - * Fix issue where `ExplicitBucketHistogramConfiguration` could be used to configure metric streams for instruments that are not histograms. Currently, it is not possible to change the aggregation of an instrument with views. This may be possible in the future. ([#3126](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3126)) - * Conformed to the specification to ensure that each view that an instrument matches results in a new metric stream. With this change it is possible for views to introduce conflicting metric streams. Any conflicts encountered will @@ -120,15 +109,12 @@ Released 2022-Mar-30 `ExportIntervalMilliseconds` of `-1` indicating an infinite export interval period. ([#2982](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2982)) - * Fix bug where multiple views selecting a single instrument can result in duplicate updates to a single metric point. ([#3006](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3006)) - * Added the `PeriodicExportingMetricReaderOptions.ExportTimeoutMilliseconds` option. ([#3038](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3038)) - * Removed `MetricReaderType`. This enumeration was previously used when configuring a metric reader with an exporter to configure whether the export cycle would be periodic or manual (i.e., requiring a explicit call to flush @@ -137,11 +123,9 @@ Released 2022-Mar-30 by setting `PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds` to `-1`. ([#3038](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3038)) - * Marked members of the `MetricPoint` `struct` which do not mutate state as `readonly` ([#3065](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3065)) - * [Bug fix] OpenTelemetryLoggerProvider is now unaffected by changes to OpenTelemetryLoggerOptions after the LoggerFactory is built. ([#3055](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3055)) @@ -151,24 +135,18 @@ Released 2022-Mar-30 Released 2022-Mar-04 * 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. - + 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) @@ -180,14 +158,11 @@ Released 2022-Feb-02 * Make `MetricPoint` of `MetricPointAccessor` readonly. ([#2736](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2736)) - * Fail-fast when using AddView with guaranteed conflict. ([#2751](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2751)) - * Swallow `ObjectDisposedException` from the `BatchExportProcessor` worker thread. ([#2844](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2844)) - * 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)) @@ -199,34 +174,27 @@ Released 2021-Nov-29 * Prevent accessing activity Id before sampler runs in case of legacy activities. ([#2659](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2659)) - * Added `ReadOnlyTagCollection` and expose `Tags` on `MetricPoint` instead of `Keys`+`Values` ([#2642](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2642)) - * Refactored `MetricPoint` and added public methods: `GetBucketCounts`, `GetExplicitBounds`, `GetHistogramCount`, and `GetHistogramSum` ([#2657](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2657)) - * Remove MetricStreamConfiguration.Aggregation, as the feature to customize aggregation is not implemented yet. ([#2660](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2660)) - * Removed the public property `HistogramMeasurements` and added a public method `GetHistogramBuckets` instead. Renamed the class `HistogramMeasurements` to `HistogramBuckets` and added an enumerator of type `HistogramBucket` for enumerating `BucketCounts` and `ExplicitBounds`. Removed `GetBucketCounts` and `GetExplicitBounds` methods from `MetricPoint`. ([#2664](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2664)) - * Refactored temporality setting to align with the latest spec. ([#2666](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2666)) - * Removed the public properties `LongValue`, `DoubleValue`, in favor of their counterpart public methods `GetSumLong`, `GetSumDouble`, `GetGaugeLastValueLong`, `GetGaugeLastValueDouble`. ([#2667](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2667)) - * MetricType modified to reserve bits for future types. ([#2693](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2693)) @@ -237,32 +205,23 @@ Released 2021-Nov-19 * Renamed `HistogramConfiguration` to `ExplicitBucketHistogramConfiguration` and changed its member `BucketBounds` to `Boundaries`. ([#2638](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2638)) - * Metrics with the same name but from different meters are allowed. ([#2634](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2634)) - * Metrics SDK will not provide inactive Metrics to delta exporter. ([#2629](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2629)) - * Histogram bounds are validated when added to a View. ([#2573](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2573)) - * Changed `BatchExportActivityProcessorOptions` constructor to throw `FormatException` if it fails to parse any of the supported environment variables. - * Added `BaseExporter.ForceFlush`. ([#2525](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2525)) - * Exposed public `Batch(T[] items, int count)` constructor on `Batch` struct ([#2542](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2542)) - * Added wildcard support for AddMeter. ([#2459](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2459)) - * Add support for multiple Metric readers ([#2596](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2596)) - * Add ability to configure MaxMetricStreams, MaxMetricPointsPerMetricStream ([#2635](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2635)) @@ -272,12 +231,9 @@ Released 2021-Oct-08 * Exception from Observable instrument callbacks does not result in entire metrics being lost. - * SDK is allocation-free on recording of measurements with up to 8 tags. - * TracerProviderBuilder.AddLegacySource now supports wildcard activity names. ([#2183](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2183)) - * Instrument and View names are validated [according with the spec](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument). ([#2470](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2470)) @@ -301,7 +257,6 @@ Released 2021-Sep-13 * Metrics perf improvements, bug fixes. Replace MetricProcessor with MetricReader. ([#2306](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2306)) - * Add `BatchExportActivityProcessorOptions` which supports field value overriding using `OTEL_BSP_SCHEDULE_DELAY`, `OTEL_BSP_EXPORT_TIMEOUT`, `OTEL_BSP_MAX_QUEUE_SIZE`, `OTEL_BSP_MAX_EXPORT_BATCH_SIZE` environmental @@ -315,19 +270,15 @@ Released 2021-Aug-24 * More Metrics features. All instrument types, push/pull exporters, Delta/Cumulative temporality supported. - * `ResourceBuilder.CreateDefault` has detectors for `OTEL_RESOURCE_ATTRIBUTES`, `OTEL_SERVICE_NAME` environment variables so that explicit `AddEnvironmentVariableDetector` call is not needed. ([#2247](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2247)) - * `ResourceBuilder.AddEnvironmentVariableDetector` handles `OTEL_SERVICE_NAME` environmental variable. ([#2209](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2209)) - * Removes upper constraint for Microsoft.Extensions.Logging dependencies. ([#2179](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2179)) - * OpenTelemetryLogger modified to not throw, when the formatter supplied in ILogger.Log call is null. ([#2200](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2200)) @@ -339,7 +290,6 @@ Released 2021-Jul-23 * Add basic Metrics support with a single pipeline, and supporting Counter (sync) instrument. Push and Pull exporters are supported. ([#2174](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2174)) - * Removes .NET Framework 4.5.2, .NET 4.6 support. The minimum .NET Framework version supported is .NET 4.6.1. ([#2138](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2138)) @@ -366,7 +316,6 @@ Released 2021-May-11 * `AddLegacySource()` moved out of `TracerProviderBuilderExtensions` and into public API ([#2019](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2019)) - * Fixed an issue causing inconsistent log scopes when using `BatchLogRecordExportProcessor`. To make parsing scopes easier the `LogRecord.ForEachScope` signature has been changed to receive instances of @@ -389,47 +338,37 @@ Released 2021-Mar-19 * Removed SuppressScope Increment/Decrement from DiagnosticSourceListeners. ([1893](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1893)) - * Added `TracerProviderBuilder.SetErrorStatusOnException` which automatically sets the activity status to `Error` when exception happened. ([#1858](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1858) [#1875](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1875)) - * Added `ForceFlush` to `TracerProvider`. ([#1837](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1837)) - * Added a TracerProviderBuilder extension method called `AddLegacySource` which is used by instrumentation libraries that use DiagnosticSource to get activities processed without ActivitySourceAdapter. [#1836](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1836) [#1860](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1860) - * Added new constructor with optional parameters to allow customization of `ParentBasedSampler` behavior. ([#1727](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1727)) - * The application base directory is now tested after the current directory when searching for the [self diagnostic configuration file](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry/README.md#troubleshooting). ([#1865](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1865)) - * Resource Attributes now accept primitive arrays as values. ([#1852](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1852)) - * Fixed [#1846](https://github.com/open-telemetry/opentelemetry-dotnet/issues/1846): `ParentBasedSampler` will no longer explicitly consider Activity links. ([#1851](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1851)) - * Added `IncludeScopes`, `IncludeFormattedMessage`, & `ParseStateValues` on `OpenTelemetryLoggerOptions`. Added `FormattedMessage`, `StateValues`, & `ForEachScope` on `LogRecord`. ([#1869](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1869) & [#1883](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1883)) - * Added `SetResourceBuilder` support to `OpenTelemetryLoggerOptions`. ([#1913](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1913)) - * Added `IDeferredTracerProviderBuilder` and `TracerProviderBuilderBase` to support dependency injection through OpenTelemetry.Extensions.Hosting. ([#1889](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1889)) From c475666aeb91ca55aba462aa9172b45d9253bd9b Mon Sep 17 00:00:00 2001 From: liangli Date: Fri, 29 Jul 2022 10:12:28 +0800 Subject: [PATCH 39/58] fix: replace all newtonsoft.json usages (#3478) --- build/Common.nonprod.props | 2 +- .../BasicTests.cs | 10 ++-- .../HttpClientTests.netcore31.cs | 49 ++++++++++--------- .../HttpTestData.cs | 8 ++- .../HttpWebRequestTests.netfx.cs | 43 ++++++++-------- ...elemetry.Instrumentation.Http.Tests.csproj | 2 +- .../http-out-test-cases.json | 16 +++--- .../Controllers/ForwardController.cs | 9 ++-- .../Controllers/ForwardController.cs | 9 ++-- 9 files changed, 75 insertions(+), 73 deletions(-) diff --git a/build/Common.nonprod.props b/build/Common.nonprod.props index 704d4f890fb..4810547d72e 100644 --- a/build/Common.nonprod.props +++ b/build/Common.nonprod.props @@ -41,11 +41,11 @@ [6.0.0,) [6.0.0,) [16.10.0] - [13.0.1,14.0) [4.14.5,5.0) [6.1.0,7.0) [1.0.0-rc.2,2.0) [6.2.3] + 6.0.5 [2.4.3,3.0) [2.4.1,3.0) diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs index 208b12da777..f7eaf9bd213 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs @@ -19,6 +19,7 @@ using System.Diagnostics; using System.Linq; using System.Net.Http; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -26,7 +27,6 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Moq; -using Newtonsoft.Json; using OpenTelemetry.Context.Propagation; using OpenTelemetry.Instrumentation.AspNetCore.Implementation; using OpenTelemetry.Tests; @@ -364,7 +364,7 @@ public async Task ExtractContextIrrespectiveOfSamplingDecision(SamplingDecision // Test TraceContext Propagation var request = new HttpRequestMessage(HttpMethod.Get, "/api/GetChildActivityTraceContext"); var response = await client.SendAsync(request); - var childActivityTraceContext = JsonConvert.DeserializeObject>(response.Content.ReadAsStringAsync().Result); + var childActivityTraceContext = JsonSerializer.Deserialize>(response.Content.ReadAsStringAsync().Result); response.EnsureSuccessStatusCode(); @@ -376,7 +376,7 @@ public async Task ExtractContextIrrespectiveOfSamplingDecision(SamplingDecision request = new HttpRequestMessage(HttpMethod.Get, "/api/GetChildActivityBaggageContext"); response = await client.SendAsync(request); - var childActivityBaggageContext = JsonConvert.DeserializeObject>(response.Content.ReadAsStringAsync().Result); + var childActivityBaggageContext = JsonSerializer.Deserialize>(response.Content.ReadAsStringAsync().Result); response.EnsureSuccessStatusCode(); @@ -431,7 +431,7 @@ public async Task ExtractContextIrrespectiveOfTheFilterApplied() // Ensure that filter was called Assert.True(isFilterCalled); - var childActivityTraceContext = JsonConvert.DeserializeObject>(response.Content.ReadAsStringAsync().Result); + var childActivityTraceContext = JsonSerializer.Deserialize>(response.Content.ReadAsStringAsync().Result); response.EnsureSuccessStatusCode(); @@ -443,7 +443,7 @@ public async Task ExtractContextIrrespectiveOfTheFilterApplied() request = new HttpRequestMessage(HttpMethod.Get, "/api/GetChildActivityBaggageContext"); response = await client.SendAsync(request); - var childActivityBaggageContext = JsonConvert.DeserializeObject>(response.Content.ReadAsStringAsync().Result); + var childActivityBaggageContext = JsonSerializer.Deserialize>(response.Content.ReadAsStringAsync().Result); response.EnsureSuccessStatusCode(); diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.netcore31.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.netcore31.cs index 27db9e89fba..05c39935426 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.netcore31.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.netcore31.cs @@ -22,9 +22,9 @@ using System.Linq; using System.Net.Http; using System.Reflection; +using System.Text.Json; using System.Threading.Tasks; using Moq; -using Newtonsoft.Json; using OpenTelemetry.Metrics; using OpenTelemetry.Tests; using OpenTelemetry.Trace; @@ -185,29 +185,30 @@ public async Task HttpOutCallsAreCollectedSuccessfullyAsync(HttpTestData.HttpOut [Fact] public async Task DebugIndividualTestAsync() { - var serializer = new JsonSerializer(); - var input = serializer.Deserialize(new JsonTextReader(new StringReader(@" -[ - { - ""name"": ""Response code: 399"", - ""method"": ""GET"", - ""url"": ""http://{host}:{port}/"", - ""responseCode"": 399, - ""responseExpected"": true, - ""spanName"": ""HTTP GET"", - ""spanStatus"": ""UNSET"", - ""spanKind"": ""Client"", - ""spanAttributes"": { - ""http.scheme"": ""http"", - ""http.method"": ""GET"", - ""http.host"": ""{host}:{port}"", - ""http.status_code"": ""399"", - ""http.flavor"": ""2.0"", - ""http.url"": ""http://{host}:{port}/"" - } - } -] -"))); + var input = JsonSerializer.Deserialize( + @" + [ + { + ""name"": ""Response code: 399"", + ""method"": ""GET"", + ""url"": ""http://{host}:{port}/"", + ""responseCode"": 399, + ""responseExpected"": true, + ""spanName"": ""HTTP GET"", + ""spanStatus"": ""UNSET"", + ""spanKind"": ""Client"", + ""spanAttributes"": { + ""http.scheme"": ""http"", + ""http.method"": ""GET"", + ""http.host"": ""{host}:{port}"", + ""http.status_code"": ""399"", + ""http.flavor"": ""2.0"", + ""http.url"": ""http://{host}:{port}/"" + } + } + ] + ", + new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); var t = (Task)this.GetType().InvokeMember(nameof(this.HttpOutCallsAreCollectedSuccessfullyAsync), BindingFlags.InvokeMethod, null, this, HttpTestData.GetArgumentsFromTestCaseObject(input).First()); await t; diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpTestData.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpTestData.cs index 38e1dd83391..aa8efe91fbe 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpTestData.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpTestData.cs @@ -14,9 +14,8 @@ // limitations under the License. // using System.Collections.Generic; -using System.IO; using System.Reflection; -using Newtonsoft.Json; +using System.Text.Json; namespace OpenTelemetry.Instrumentation.Http.Tests { @@ -25,9 +24,8 @@ public static class HttpTestData public static IEnumerable ReadTestCases() { var assembly = Assembly.GetExecutingAssembly(); - var serializer = new JsonSerializer(); - var input = serializer.Deserialize(new JsonTextReader(new StreamReader(assembly.GetManifestResourceStream("OpenTelemetry.Instrumentation.Http.Tests.http-out-test-cases.json")))); - + var input = JsonSerializer.Deserialize( + assembly.GetManifestResourceStream("OpenTelemetry.Instrumentation.Http.Tests.http-out-test-cases.json"), new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); return GetArgumentsFromTestCaseObject(input); } diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestTests.netfx.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestTests.netfx.cs index 3d3e10a0400..a31ddbaaf43 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestTests.netfx.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestTests.netfx.cs @@ -20,8 +20,8 @@ using System.IO; using System.Linq; using System.Net; +using System.Text.Json; using Moq; -using Newtonsoft.Json; using OpenTelemetry.Tests; using OpenTelemetry.Trace; using Xunit; @@ -137,26 +137,27 @@ public void HttpOutCallsAreCollectedSuccessfully(HttpTestData.HttpOutTestCase tc [Fact] public void DebugIndividualTest() { - var serializer = new JsonSerializer(); - var input = serializer.Deserialize(new JsonTextReader(new StringReader(@" - { - ""name"": ""Http version attribute populated"", - ""method"": ""GET"", - ""url"": ""http://{host}:{port}/"", - ""responseCode"": 200, - ""spanName"": ""HTTP GET"", - ""spanStatus"": ""UNSET"", - ""spanKind"": ""Client"", - ""setHttpFlavor"": true, - ""spanAttributes"": { - ""http.method"": ""GET"", - ""http.host"": ""{host}:{port}"", - ""http.flavor"": ""2.0"", - ""http.status_code"": 200, - ""http.url"": ""http://{host}:{port}/"" - } - } -"))); + var input = JsonSerializer.Deserialize( + @" + { + ""name"": ""Http version attribute populated"", + ""method"": ""GET"", + ""url"": ""http://{host}:{port}/"", + ""responseCode"": 200, + ""spanName"": ""HTTP GET"", + ""spanStatus"": ""UNSET"", + ""spanKind"": ""Client"", + ""setHttpFlavor"": true, + ""spanAttributes"": { + ""http.method"": ""GET"", + ""http.host"": ""{host}:{port}"", + ""http.flavor"": ""2.0"", + ""http.status_code"": ""200"", + ""http.url"": ""http://{host}:{port}/"" + } + } + ", + new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); this.HttpOutCallsAreCollectedSuccessfully(input); } diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/OpenTelemetry.Instrumentation.Http.Tests.csproj b/test/OpenTelemetry.Instrumentation.Http.Tests/OpenTelemetry.Instrumentation.Http.Tests.csproj index a38f1a99cba..35f23f1ba4b 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/OpenTelemetry.Instrumentation.Http.Tests.csproj +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/OpenTelemetry.Instrumentation.Http.Tests.csproj @@ -15,9 +15,9 @@ - + all diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/http-out-test-cases.json b/test/OpenTelemetry.Instrumentation.Http.Tests/http-out-test-cases.json index aeae7bf8ade..3441e152d92 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/http-out-test-cases.json +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/http-out-test-cases.json @@ -11,7 +11,7 @@ "http.method": "GET", "http.host": "{host}:{port}", "http.flavor": "2.0", - "http.status_code": 200, + "http.status_code": "200", "http.url": "http://{host}:{port}/" } }, @@ -27,7 +27,7 @@ "http.method": "POST", "http.host": "{host}:{port}", "http.flavor": "2.0", - "http.status_code": 200, + "http.status_code": "200", "http.url": "http://{host}:{port}/" } }, @@ -44,7 +44,7 @@ "http.method": "GET", "http.host": "{host}:{port}", "http.flavor": "2.0", - "http.status_code": 200, + "http.status_code": "200", "http.url": "http://{host}:{port}/path/to/resource/" } }, @@ -61,7 +61,7 @@ "http.method": "GET", "http.host": "{host}:{port}", "http.flavor": "2.0", - "http.status_code": 200, + "http.status_code": "200", "http.url": "http://{host}:{port}/path/to/resource#fragment" } }, @@ -78,7 +78,7 @@ "http.method": "GET", "http.host": "{host}:{port}", "http.flavor": "2.0", - "http.status_code": 200, + "http.status_code": "200", "http.url": "http://{host}:{port}/path/to/resource#fragment" } }, @@ -129,7 +129,7 @@ "http.method": "GET", "http.host": "{host}:{port}", "http.flavor": "2.0", - "http.status_code": 200, + "http.status_code": "200", "http.url": "http://{host}:{port}/" } }, @@ -146,7 +146,7 @@ "http.method": "GET", "http.host": "{host}:{port}", "http.flavor": "2.0", - "http.status_code": 200, + "http.status_code": "200", "http.url": "http://{host}:{port}/" } }, @@ -333,7 +333,7 @@ "http.method": "GET", "http.host": "{host}:{port}", "http.flavor": "2.0", - "http.status_code": 200, + "http.status_code": "200", "http.url": "http://{host}:{port}/" } } diff --git a/test/TestApp.AspNetCore.3.1/Controllers/ForwardController.cs b/test/TestApp.AspNetCore.3.1/Controllers/ForwardController.cs index 31b98f6955c..5b532d3af0e 100644 --- a/test/TestApp.AspNetCore.3.1/Controllers/ForwardController.cs +++ b/test/TestApp.AspNetCore.3.1/Controllers/ForwardController.cs @@ -15,9 +15,10 @@ // using System.Net.Http; using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Newtonsoft.Json; namespace TestApp.AspNetCore._3._1.Controllers { @@ -44,7 +45,7 @@ public async Task Post([FromBody] Data[] data) var request = new HttpRequestMessage(HttpMethod.Post, argument.Url) { Content = new StringContent( - JsonConvert.SerializeObject(argument.Arguments), + JsonSerializer.Serialize(argument.Arguments), Encoding.UTF8, "application/json"), }; @@ -61,10 +62,10 @@ public async Task Post([FromBody] Data[] data) public class Data { - [JsonProperty("url")] + [JsonPropertyName("url")] public string Url { get; set; } - [JsonProperty("arguments")] + [JsonPropertyName("arguments")] public Data[] Arguments { get; set; } } } diff --git a/test/TestApp.AspNetCore.6.0/Controllers/ForwardController.cs b/test/TestApp.AspNetCore.6.0/Controllers/ForwardController.cs index 36290e144c9..3f1a867186e 100644 --- a/test/TestApp.AspNetCore.6.0/Controllers/ForwardController.cs +++ b/test/TestApp.AspNetCore.6.0/Controllers/ForwardController.cs @@ -16,9 +16,10 @@ using System.Net.Http; using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Newtonsoft.Json; namespace TestApp.AspNetCore._6._0.Controllers { @@ -45,7 +46,7 @@ public async Task Post([FromBody] Data[] data) var request = new HttpRequestMessage(HttpMethod.Post, argument.Url) { Content = new StringContent( - JsonConvert.SerializeObject(argument.Arguments), + JsonSerializer.Serialize(argument.Arguments), Encoding.UTF8, "application/json"), }; @@ -62,10 +63,10 @@ public async Task Post([FromBody] Data[] data) public class Data { - [JsonProperty("url")] + [JsonPropertyName("url")] public string Url { get; set; } - [JsonProperty("arguments")] + [JsonPropertyName("arguments")] public Data[] Arguments { get; set; } } } From eb49e4d194c66ff6b5bddc1311bcaae2a1fd7ac8 Mon Sep 17 00:00:00 2001 From: nayang7 <107023695+nayang7@users.noreply.github.com> Date: Fri, 29 Jul 2022 13:36:19 +0800 Subject: [PATCH 40/58] Fix Remote IP Address - NULL reference exception (#3481) --- src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md | 2 ++ .../Implementation/HttpInListener.cs | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index 241df1ee7f8..d456038b9b8 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +* Fix Remote IP Address - NULL reference exception. + ([#3481](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3481)) * Metrics instrumentation to correctly populate `http.flavor` tag. (1.1 instead of HTTP/1.1 etc.) ([#3379](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3379)) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs index 1ff80acc8b2..a37863ad8b9 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs @@ -360,7 +360,11 @@ private static void AddGrpcAttributes(Activity activity, string grpcMethod, Http activity.DisplayName = grpcMethod.TrimStart('/'); activity.SetTag(SemanticConventions.AttributeRpcSystem, GrpcTagHelper.RpcSystemGrpc); - activity.SetTag(SemanticConventions.AttributeNetPeerIp, context.Connection.RemoteIpAddress.ToString()); + if (context.Connection.RemoteIpAddress != null) + { + activity.SetTag(SemanticConventions.AttributeNetPeerIp, context.Connection.RemoteIpAddress.ToString()); + } + activity.SetTag(SemanticConventions.AttributeNetPeerPort, context.Connection.RemotePort); bool validConversion = GrpcTagHelper.TryGetGrpcStatusCodeFromActivity(activity, out int status); From a3397aadcd280122804955b206a836f7190062f1 Mon Sep 17 00:00:00 2001 From: Timothy Mothra Date: Fri, 29 Jul 2022 14:10:12 -0700 Subject: [PATCH 41/58] improve test coverage: OpenTelemetry.Api `CallerArgumentExpressionAttribute` (#3362) --- src/OpenTelemetry.Api/Internal/Guard.cs | 3 ++ .../OpenTelemetry.Tests/Internal/GuardTest.cs | 43 +++++++++++++++---- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/OpenTelemetry.Api/Internal/Guard.cs b/src/OpenTelemetry.Api/Internal/Guard.cs index 1b6348cd83a..f5b4235f1b8 100644 --- a/src/OpenTelemetry.Api/Internal/Guard.cs +++ b/src/OpenTelemetry.Api/Internal/Guard.cs @@ -25,6 +25,9 @@ namespace System.Runtime.CompilerServices /// /// Allows capturing of the expressions passed to a method. /// + /// + /// Borrowed from: . + /// [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] #pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1649 // File name should match first type name diff --git a/test/OpenTelemetry.Tests/Internal/GuardTest.cs b/test/OpenTelemetry.Tests/Internal/GuardTest.cs index 01e23d3d788..30b0ab7b7dd 100644 --- a/test/OpenTelemetry.Tests/Internal/GuardTest.cs +++ b/test/OpenTelemetry.Tests/Internal/GuardTest.cs @@ -15,20 +15,12 @@ // using System; +using System.Runtime.CompilerServices; using System.Threading; using Xunit; namespace OpenTelemetry.Internal.Tests { -#pragma warning disable SA1402 // File may only contain a single type -#pragma warning disable SA1649 // File name should match first type name - public class Thing -#pragma warning restore SA1649 // File name should match first type name -#pragma warning restore SA1402 // File may only contain a single type - { - public string Bar { get; set; } - } - public class GuardTest { [Fact] @@ -171,5 +163,38 @@ public void ZeroTest() Assert.Contains("Must not be zero", ex1.Message); Assert.Equal("0", ex1.ParamName); } + + public class Thing + { + public string Bar { get; set; } + } + +#if !NETCOREAPP3_0_OR_GREATER + /// + /// Borrowed from: . + /// + public class CallerArgumentExpressionAttributeTests + { + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("paramName")] + public static void Ctor_ParameterName_Roundtrip(string value) + { + var caea = new CallerArgumentExpressionAttribute(value); + Assert.Equal(value, caea.ParameterName); + } + + [Fact] + public static void BasicTest() + { + Assert.Equal("\"hello\"", GetValue("hello")); + Assert.Equal("3 + 2", GetValue(3 + 2)); + Assert.Equal("new object()", GetValue(new object())); + } + + private static string GetValue(object argument, [CallerArgumentExpression("argument")] string expr = null) => expr; + } +#endif } } From ba2986aec0ce21c3cf45d42e33db7fe0da422441 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Fri, 29 Jul 2022 16:26:28 -0700 Subject: [PATCH 42/58] Exponential Bucket Histogram - part 8 (#3505) --- .../Metrics/CircularBufferBuckets.cs | 71 +++++++++---------- .../Metrics/ExponentialBucketHistogram.cs | 49 ++++--------- .../Metrics/CircularBufferBucketsTest.cs | 12 ++-- 3 files changed, 55 insertions(+), 77 deletions(-) diff --git a/src/OpenTelemetry/Metrics/CircularBufferBuckets.cs b/src/OpenTelemetry/Metrics/CircularBufferBuckets.cs index 06d318a25a7..14d5eaddcaf 100644 --- a/src/OpenTelemetry/Metrics/CircularBufferBuckets.cs +++ b/src/OpenTelemetry/Metrics/CircularBufferBuckets.cs @@ -89,50 +89,59 @@ public int TryIncrement(int index, long value = 1) this.begin = index; this.end = index; + this.trait[this.ModuloIndex(index)] += value; + + return 0; } - else if (index > this.end) - { - var diff = index - this.begin; - if (diff >= capacity || diff < 0) - { - return CalculateScaleReduction(diff + 1, capacity); - } + var begin = this.begin; + var end = this.end; - this.end = index; + if (index > end) + { + end = index; } - else if (index < this.begin) + else if (index < begin) { - var diff = this.end - index; + begin = index; + } + else + { + this.trait[this.ModuloIndex(index)] += value; - if (diff >= this.Capacity || diff < 0) - { - return CalculateScaleReduction(diff + 1, capacity); - } + return 0; + } - this.begin = index; + var diff = end - begin; + + if (diff >= capacity || diff < 0) + { + return CalculateScaleReduction(begin, end, capacity); } + this.begin = begin; + this.end = end; + this.trait[this.ModuloIndex(index)] += value; return 0; [MethodImpl(MethodImplOptions.AggressiveInlining)] - static int CalculateScaleReduction(int size, int capacity) + static int CalculateScaleReduction(int begin, int end, int capacity) { - var shift = MathHelper.LeadingZero32(capacity); - - if (size > 0) - { - shift -= MathHelper.LeadingZero32(size); - } + Debug.Assert(capacity >= 2, "The capacity must be at least 2."); - if (size > (capacity << shift)) + var retval = 0; + var diff = end - begin; + while (diff >= capacity || diff < 0) { - shift++; + begin >>= 1; + end >>= 1; + diff = end - begin; + retval++; } - return shift; + return retval; } } @@ -256,18 +265,6 @@ static void Move(long[] array, uint src, uint dst) } } - public override string ToString() - { - return nameof(CircularBufferBuckets) - + "{" - + nameof(this.Capacity) + "=" + this.Capacity + ", " - + nameof(this.Size) + "=" + this.Size + ", " - + nameof(this.begin) + "=" + this.begin + ", " - + nameof(this.end) + "=" + this.end + ", " - + (this.trait == null ? "null" : "{" + string.Join(", ", this.trait) + "}") - + "}"; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private int ModuloIndex(int value) { diff --git a/src/OpenTelemetry/Metrics/ExponentialBucketHistogram.cs b/src/OpenTelemetry/Metrics/ExponentialBucketHistogram.cs index 87edd9908b9..b05a4c4438b 100644 --- a/src/OpenTelemetry/Metrics/ExponentialBucketHistogram.cs +++ b/src/OpenTelemetry/Metrics/ExponentialBucketHistogram.cs @@ -120,15 +120,6 @@ private set internal CircularBufferBuckets NegativeBuckets { get; } - /// - public override string ToString() - { - return nameof(ExponentialBucketHistogram) - + "{" - + nameof(this.Scale) + "=" + this.Scale - + "}"; - } - /// /// Maps a finite positive IEEE 754 double-precision floating-point /// number to Bucket[index] = ( base ^ index, base ^ (index + 1) ], @@ -189,36 +180,26 @@ public void Record(double value) var c = value.CompareTo(0); - if (c > 0) + if (c == 0) { - var index = this.MapToIndex(value); - var n = this.PositiveBuckets.TryIncrement(index); - - if (n != 0) - { - this.PositiveBuckets.ScaleDown(n); - this.NegativeBuckets.ScaleDown(n); - n = this.PositiveBuckets.TryIncrement(index); - Debug.Assert(n == 0, "Increment should always succeed after scale down."); - } + this.ZeroCount++; + return; } - else if (c < 0) - { - var index = this.MapToIndex(-value); - var n = this.NegativeBuckets.TryIncrement(index); - if (n != 0) - { - this.PositiveBuckets.ScaleDown(n); - this.NegativeBuckets.ScaleDown(n); - n = this.NegativeBuckets.TryIncrement(index); - Debug.Assert(n == 0, "Increment should always succeed after scale down."); - } - } - else + var index = this.MapToIndex(c > 0 ? value : -value); + var buckets = c > 0 ? this.PositiveBuckets : this.NegativeBuckets; + var n = buckets.TryIncrement(index); + + if (n == 0) { - this.ZeroCount++; + return; } + + this.PositiveBuckets.ScaleDown(n); + this.NegativeBuckets.ScaleDown(n); + this.Scale -= n; + n = buckets.TryIncrement(index >> n); + Debug.Assert(n == 0, "Increment should always succeed after scale down."); } } diff --git a/test/OpenTelemetry.Tests/Metrics/CircularBufferBucketsTest.cs b/test/OpenTelemetry.Tests/Metrics/CircularBufferBucketsTest.cs index 68122562250..e83b4bda46c 100644 --- a/test/OpenTelemetry.Tests/Metrics/CircularBufferBucketsTest.cs +++ b/test/OpenTelemetry.Tests/Metrics/CircularBufferBucketsTest.cs @@ -101,18 +101,18 @@ public void NegativeInsertions() [Fact] public void IntegerOverflow() { - var buckets = new CircularBufferBuckets(1); + var buckets = new CircularBufferBuckets(2); Assert.Equal(0, buckets.TryIncrement(int.MaxValue)); Assert.Equal(int.MaxValue, buckets.Offset); Assert.Equal(1, buckets.Size); - Assert.Equal(31, buckets.TryIncrement(1)); - Assert.Equal(31, buckets.TryIncrement(0)); - Assert.Equal(32, buckets.TryIncrement(-1)); - Assert.Equal(32, buckets.TryIncrement(int.MinValue + 1)); - Assert.Equal(32, buckets.TryIncrement(int.MinValue)); + Assert.Equal(30, buckets.TryIncrement(1)); + Assert.Equal(30, buckets.TryIncrement(0)); + Assert.Equal(31, buckets.TryIncrement(-1)); + Assert.Equal(31, buckets.TryIncrement(int.MinValue + 1)); + Assert.Equal(31, buckets.TryIncrement(int.MinValue)); } [Fact] From 7517702f62c6ab2c93450698ce0c06ce4d287339 Mon Sep 17 00:00:00 2001 From: Yun-Ting Lin Date: Fri, 29 Jul 2022 17:16:20 -0700 Subject: [PATCH 43/58] [Prometheus] Remove shared project and move the shared code under listener project (#3503) --- OpenTelemetry.sln | 12 - ...etry.Exporter.Prometheus.AspNetCore.csproj | 12 +- ...sExporterMeterProviderBuilderExtensions.cs | 2 +- .../PrometheusExporterMiddleware.cs | 2 +- .../.publicApi/net462/PublicAPI.Unshipped.txt | 14 +- .../netstandard2.0/PublicAPI.Unshipped.txt | 14 +- .../AssemblyInfo.cs | 4 +- ...ry.Exporter.Prometheus.HttpListener.csproj | 10 - ...pListenerMeterProviderBuilderExtensions.cs | 2 +- .../PrometheusHttpListener.cs | 2 +- .../Shared}/PrometheusCollectionManager.cs | 10 +- .../Shared}/PrometheusExporter.cs | 4 +- .../Shared}/PrometheusExporterEventSource.cs | 2 +- .../Shared}/PrometheusExporterOptions.cs | 4 +- .../Shared}/PrometheusSerializer.cs | 2 +- .../Shared}/PrometheusSerializerExt.cs | 2 +- .../.publicApi/net462/PublicAPI.Shipped.txt | 0 .../.publicApi/net462/PublicAPI.Unshipped.txt | 6 - .../netcoreapp3.1/PublicAPI.Shipped.txt | 0 .../netcoreapp3.1/PublicAPI.Unshipped.txt | 6 - ...elemetry.Exporter.Prometheus.Shared.csproj | 29 -- test/Benchmarks/Benchmarks.csproj | 2 +- .../PrometheusSerializerBenchmarks.cs | 2 +- .../PrometheusCollectionManagerTests.cs | 2 +- ...orter.Prometheus.HttpListener.Tests.csproj | 1 + .../PrometheusSerializerTests.cs | 387 ++++++++++++++++++ 26 files changed, 427 insertions(+), 106 deletions(-) rename src/{OpenTelemetry.Exporter.Prometheus.Shared => OpenTelemetry.Exporter.Prometheus.HttpListener}/AssemblyInfo.cs (77%) rename src/{OpenTelemetry.Exporter.Prometheus.Shared => OpenTelemetry.Exporter.Prometheus.HttpListener/Shared}/PrometheusCollectionManager.cs (96%) rename src/{OpenTelemetry.Exporter.Prometheus.Shared => OpenTelemetry.Exporter.Prometheus.HttpListener/Shared}/PrometheusExporter.cs (95%) rename src/{OpenTelemetry.Exporter.Prometheus.Shared => OpenTelemetry.Exporter.Prometheus.HttpListener/Shared}/PrometheusExporterEventSource.cs (97%) rename src/{OpenTelemetry.Exporter.Prometheus.Shared => OpenTelemetry.Exporter.Prometheus.HttpListener/Shared}/PrometheusExporterOptions.cs (93%) rename src/{OpenTelemetry.Exporter.Prometheus.Shared => OpenTelemetry.Exporter.Prometheus.HttpListener/Shared}/PrometheusSerializer.cs (99%) rename src/{OpenTelemetry.Exporter.Prometheus.Shared => OpenTelemetry.Exporter.Prometheus.HttpListener/Shared}/PrometheusSerializerExt.cs (99%) delete mode 100644 src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/net462/PublicAPI.Shipped.txt delete mode 100644 src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/net462/PublicAPI.Unshipped.txt delete mode 100644 src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/netcoreapp3.1/PublicAPI.Shipped.txt delete mode 100644 src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt delete mode 100644 src/OpenTelemetry.Exporter.Prometheus.Shared/OpenTelemetry.Exporter.Prometheus.Shared.csproj create mode 100644 test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index d345ee94283..a16555d39ce 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -239,14 +239,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Prom EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Prometheus.HttpListener", "src\OpenTelemetry.Exporter.Prometheus.HttpListener\OpenTelemetry.Exporter.Prometheus.HttpListener.csproj", "{6B0232B7-5F29-4FB5-B383-1AA02DFE1089}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Prometheus.Shared", "src\OpenTelemetry.Exporter.Prometheus.Shared\OpenTelemetry.Exporter.Prometheus.Shared.csproj", "{4AD27517-BAFC-413B-A8F0-988C3CEDC662}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests", "test\OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests\OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests.csproj", "{FBD12B0B-6731-4DD4-9C13-86F34593E974}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Prometheus.HttpListener.Tests", "test\OpenTelemetry.Exporter.Prometheus.HttpListener.Tests\OpenTelemetry.Exporter.Prometheus.HttpListener.Tests.csproj", "{4EF4364F-6E64-43CE-BED1-E6FE01024899}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Prometheus.Shared.Tests", "test\OpenTelemetry.Exporter.Prometheus.Shared.Tests\OpenTelemetry.Exporter.Prometheus.Shared.Tests.csproj", "{8E75AEE2-017B-474F-A96D-035DF76A1C9E}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -501,10 +497,6 @@ Global {6B0232B7-5F29-4FB5-B383-1AA02DFE1089}.Debug|Any CPU.Build.0 = Debug|Any CPU {6B0232B7-5F29-4FB5-B383-1AA02DFE1089}.Release|Any CPU.ActiveCfg = Release|Any CPU {6B0232B7-5F29-4FB5-B383-1AA02DFE1089}.Release|Any CPU.Build.0 = Release|Any CPU - {4AD27517-BAFC-413B-A8F0-988C3CEDC662}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4AD27517-BAFC-413B-A8F0-988C3CEDC662}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4AD27517-BAFC-413B-A8F0-988C3CEDC662}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4AD27517-BAFC-413B-A8F0-988C3CEDC662}.Release|Any CPU.Build.0 = Release|Any CPU {FBD12B0B-6731-4DD4-9C13-86F34593E974}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FBD12B0B-6731-4DD4-9C13-86F34593E974}.Debug|Any CPU.Build.0 = Debug|Any CPU {FBD12B0B-6731-4DD4-9C13-86F34593E974}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -513,10 +505,6 @@ Global {4EF4364F-6E64-43CE-BED1-E6FE01024899}.Debug|Any CPU.Build.0 = Debug|Any CPU {4EF4364F-6E64-43CE-BED1-E6FE01024899}.Release|Any CPU.ActiveCfg = Release|Any CPU {4EF4364F-6E64-43CE-BED1-E6FE01024899}.Release|Any CPU.Build.0 = Release|Any CPU - {8E75AEE2-017B-474F-A96D-035DF76A1C9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8E75AEE2-017B-474F-A96D-035DF76A1C9E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8E75AEE2-017B-474F-A96D-035DF76A1C9E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8E75AEE2-017B-474F-A96D-035DF76A1C9E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj index e7453e0bf52..33a00e05f1a 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj @@ -19,12 +19,12 @@ - - - - - - + + + + + + diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs index fd6809d68ee..d230d03d042 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs @@ -16,7 +16,7 @@ using System; using OpenTelemetry.Exporter.Prometheus.AspNetCore; -using OpenTelemetry.Exporter.Prometheus.Shared; +using OpenTelemetry.Exporter.Prometheus.HttpListener.Shared; using OpenTelemetry.Internal; namespace OpenTelemetry.Metrics diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs index bebf084ce33..d76a85d44d3 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs @@ -19,7 +19,7 @@ using System.Diagnostics; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using OpenTelemetry.Exporter.Prometheus.Shared; +using OpenTelemetry.Exporter.Prometheus.HttpListener.Shared; using OpenTelemetry.Internal; using OpenTelemetry.Metrics; diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt index a8f01b6d787..5f48f2c7562 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt @@ -1,12 +1,12 @@ -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.PrometheusExporterOptions() -> void -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.ScrapeEndpointPath.get -> string -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.ScrapeEndpointPath.set -> void -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.get -> int -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.set -> void OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.Prefixes.get -> System.Collections.Generic.IReadOnlyCollection OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.Prefixes.set -> void OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.PrometheusHttpListenerOptions() -> void +OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions +OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.PrometheusExporterOptions() -> void +OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.ScrapeEndpointPath.get -> string +OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.ScrapeEndpointPath.set -> void +OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.get -> int +OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.set -> void OpenTelemetry.Metrics.PrometheusExporterHttpListenerMeterProviderBuilderExtensions -static OpenTelemetry.Metrics.PrometheusExporterHttpListenerMeterProviderBuilderExtensions.AddPrometheusHttpListener(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configureExporterOptions = null, System.Action configureListenerOptions = null) -> OpenTelemetry.Metrics.MeterProviderBuilder +static OpenTelemetry.Metrics.PrometheusExporterHttpListenerMeterProviderBuilderExtensions.AddPrometheusHttpListener(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configureExporterOptions = null, System.Action configureListenerOptions = null) -> OpenTelemetry.Metrics.MeterProviderBuilder diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index a8f01b6d787..5f48f2c7562 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,12 +1,12 @@ -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.PrometheusExporterOptions() -> void -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.ScrapeEndpointPath.get -> string -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.ScrapeEndpointPath.set -> void -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.get -> int -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.set -> void OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.Prefixes.get -> System.Collections.Generic.IReadOnlyCollection OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.Prefixes.set -> void OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.PrometheusHttpListenerOptions() -> void +OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions +OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.PrometheusExporterOptions() -> void +OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.ScrapeEndpointPath.get -> string +OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.ScrapeEndpointPath.set -> void +OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.get -> int +OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.set -> void OpenTelemetry.Metrics.PrometheusExporterHttpListenerMeterProviderBuilderExtensions -static OpenTelemetry.Metrics.PrometheusExporterHttpListenerMeterProviderBuilderExtensions.AddPrometheusHttpListener(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configureExporterOptions = null, System.Action configureListenerOptions = null) -> OpenTelemetry.Metrics.MeterProviderBuilder +static OpenTelemetry.Metrics.PrometheusExporterHttpListenerMeterProviderBuilderExtensions.AddPrometheusHttpListener(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configureExporterOptions = null, System.Action configureListenerOptions = null) -> OpenTelemetry.Metrics.MeterProviderBuilder diff --git a/src/OpenTelemetry.Exporter.Prometheus.Shared/AssemblyInfo.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/AssemblyInfo.cs similarity index 77% rename from src/OpenTelemetry.Exporter.Prometheus.Shared/AssemblyInfo.cs rename to src/OpenTelemetry.Exporter.Prometheus.HttpListener/AssemblyInfo.cs index d4361cf0913..e05d6714457 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.Shared/AssemblyInfo.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/AssemblyInfo.cs @@ -17,9 +17,9 @@ using System.Runtime.CompilerServices; #if SIGNED +[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.HttpListener.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] [assembly: InternalsVisibleTo("Benchmarks, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] -[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.Shared.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] #else +[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.HttpListener.Tests")] [assembly: InternalsVisibleTo("Benchmarks")] -[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.Shared.Tests")] #endif diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj index 7769147c0f6..ff00ef3db79 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj @@ -14,21 +14,11 @@ false - - $(DefineConstants);PROMETHEUS_HTTPLISTENER - - - - - - - - diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusExporterHttpListenerMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusExporterHttpListenerMeterProviderBuilderExtensions.cs index 9ab648b6be0..8c53f09837d 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusExporterHttpListenerMeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusExporterHttpListenerMeterProviderBuilderExtensions.cs @@ -16,7 +16,7 @@ using System; using OpenTelemetry.Exporter.Prometheus.HttpListener; -using OpenTelemetry.Exporter.Prometheus.Shared; +using OpenTelemetry.Exporter.Prometheus.HttpListener.Shared; using OpenTelemetry.Internal; namespace OpenTelemetry.Metrics diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs index 28700603b1b..6badb4d5337 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs @@ -18,7 +18,7 @@ using System.Net; using System.Threading; using System.Threading.Tasks; -using OpenTelemetry.Exporter.Prometheus.Shared; +using OpenTelemetry.Exporter.Prometheus.HttpListener.Shared; using OpenTelemetry.Internal; namespace OpenTelemetry.Exporter.Prometheus.HttpListener diff --git a/src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusCollectionManager.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusCollectionManager.cs similarity index 96% rename from src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusCollectionManager.cs rename to src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusCollectionManager.cs index 87f3d6bff02..43da102246f 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusCollectionManager.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusCollectionManager.cs @@ -20,7 +20,7 @@ using System.Threading.Tasks; using OpenTelemetry.Metrics; -namespace OpenTelemetry.Exporter.Prometheus.Shared +namespace OpenTelemetry.Exporter.Prometheus.HttpListener.Shared { internal sealed class PrometheusCollectionManager { @@ -91,7 +91,7 @@ public Task EnterCollect() this.ExitGlobalLock(); CollectionResponse response; - bool result = this.ExecuteCollect(); + var result = this.ExecuteCollect(); if (result) { this.previousDataViewGeneratedAtUtc = DateTime.UtcNow; @@ -169,14 +169,14 @@ private void WaitForReadersToComplete() private bool ExecuteCollect() { this.exporter.OnExport = this.onCollectRef; - bool result = this.exporter.Collect(Timeout.Infinite); + var result = this.exporter.Collect(Timeout.Infinite); this.exporter.OnExport = null; return result; } private ExportResult OnCollect(Batch metrics) { - int cursor = 0; + var cursor = 0; try { @@ -191,7 +191,7 @@ private ExportResult OnCollect(Batch metrics) } catch (IndexOutOfRangeException) { - int bufferSize = this.buffer.Length * 2; + var bufferSize = this.buffer.Length * 2; // there are two cases we might run into the following condition: // 1. we have many metrics to be exported - in this case we probably want diff --git a/src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusExporter.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporter.cs similarity index 95% rename from src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusExporter.cs rename to src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporter.cs index 25cd97c7a4b..ec2dbf0467b 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusExporter.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporter.cs @@ -17,12 +17,10 @@ using System; #if PROMETHEUS_ASPNETCORE using OpenTelemetry.Exporter.Prometheus.AspNetCore; -#elif PROMETHEUS_HTTPLISTENER -using OpenTelemetry.Exporter.Prometheus.HttpListener; #endif using OpenTelemetry.Metrics; -namespace OpenTelemetry.Exporter.Prometheus.Shared +namespace OpenTelemetry.Exporter.Prometheus.HttpListener.Shared { /// /// Exporter of OpenTelemetry metrics to Prometheus. diff --git a/src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusExporterEventSource.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporterEventSource.cs similarity index 97% rename from src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusExporterEventSource.cs rename to src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporterEventSource.cs index 34f296c4451..6cdbf9a9024 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusExporterEventSource.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporterEventSource.cs @@ -18,7 +18,7 @@ using System.Diagnostics.Tracing; using OpenTelemetry.Internal; -namespace OpenTelemetry.Exporter.Prometheus.Shared +namespace OpenTelemetry.Exporter.Prometheus.HttpListener.Shared { /// /// EventSource events emitted from the project. diff --git a/src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusExporterOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporterOptions.cs similarity index 93% rename from src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusExporterOptions.cs rename to src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporterOptions.cs index 6c5cbd53db5..4662ee3e263 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporterOptions.cs @@ -19,10 +19,8 @@ #if PROMETHEUS_ASPNETCORE namespace OpenTelemetry.Exporter.Prometheus.AspNetCore -#elif PROMETHEUS_HTTPLISTENER -namespace OpenTelemetry.Exporter.Prometheus.HttpListener #else -namespace OpenTelemetry.Exporter.Prometheus.Shared +namespace OpenTelemetry.Exporter.Prometheus.HttpListener.Shared #endif { /// diff --git a/src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusSerializer.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusSerializer.cs similarity index 99% rename from src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusSerializer.cs rename to src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusSerializer.cs index a8738d1853d..ac8bd042c62 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusSerializer.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusSerializer.cs @@ -22,7 +22,7 @@ using System.Runtime.CompilerServices; using OpenTelemetry.Internal; -namespace OpenTelemetry.Exporter.Prometheus.Shared +namespace OpenTelemetry.Exporter.Prometheus { /// /// Basic PrometheusSerializer which has no OpenTelemetry dependency. diff --git a/src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusSerializerExt.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusSerializerExt.cs similarity index 99% rename from src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusSerializerExt.cs rename to src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusSerializerExt.cs index 1178c3069b6..805a51a2488 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.Shared/PrometheusSerializerExt.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusSerializerExt.cs @@ -16,7 +16,7 @@ using OpenTelemetry.Metrics; -namespace OpenTelemetry.Exporter.Prometheus.Shared +namespace OpenTelemetry.Exporter.Prometheus { /// /// OpenTelemetry additions to the PrometheusSerializer. diff --git a/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/net462/PublicAPI.Shipped.txt b/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/net462/PublicAPI.Shipped.txt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/net462/PublicAPI.Unshipped.txt deleted file mode 100644 index d61e4fb9ed9..00000000000 --- a/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/net462/PublicAPI.Unshipped.txt +++ /dev/null @@ -1,6 +0,0 @@ -OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions -OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.PrometheusExporterOptions() -> void -OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.ScrapeEndpointPath.get -> string -OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.ScrapeEndpointPath.set -> void -OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.get -> int -OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.set -> void diff --git a/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/netcoreapp3.1/PublicAPI.Shipped.txt b/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/netcoreapp3.1/PublicAPI.Shipped.txt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt deleted file mode 100644 index 539467ab62d..00000000000 --- a/src/OpenTelemetry.Exporter.Prometheus.Shared/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt +++ /dev/null @@ -1,6 +0,0 @@ -OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions -OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.PrometheusExporterOptions() -> void -OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.ScrapeEndpointPath.get -> string -OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.ScrapeEndpointPath.set -> void -OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.get -> int -OpenTelemetry.Exporter.Prometheus.Shared.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.set -> void diff --git a/src/OpenTelemetry.Exporter.Prometheus.Shared/OpenTelemetry.Exporter.Prometheus.Shared.csproj b/src/OpenTelemetry.Exporter.Prometheus.Shared/OpenTelemetry.Exporter.Prometheus.Shared.csproj deleted file mode 100644 index 169e97169c4..00000000000 --- a/src/OpenTelemetry.Exporter.Prometheus.Shared/OpenTelemetry.Exporter.Prometheus.Shared.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - - - netcoreapp3.1;net462 - Prometheus exporter shared code for both Prometheus exporter HttpListener and Prometheus exporter AspNetCore - $(PackageTags);prometheus;metrics - - - - - false - - - - - - - - - - - - - - - - diff --git a/test/Benchmarks/Benchmarks.csproj b/test/Benchmarks/Benchmarks.csproj index 3a3cc739d91..20ee4fb7456 100644 --- a/test/Benchmarks/Benchmarks.csproj +++ b/test/Benchmarks/Benchmarks.csproj @@ -29,6 +29,6 @@ - + diff --git a/test/Benchmarks/Exporter/PrometheusSerializerBenchmarks.cs b/test/Benchmarks/Exporter/PrometheusSerializerBenchmarks.cs index efd31fa6f86..c08d069bb50 100644 --- a/test/Benchmarks/Exporter/PrometheusSerializerBenchmarks.cs +++ b/test/Benchmarks/Exporter/PrometheusSerializerBenchmarks.cs @@ -18,7 +18,7 @@ using System.Diagnostics.Metrics; using BenchmarkDotNet.Attributes; using OpenTelemetry; -using OpenTelemetry.Exporter.Prometheus.Shared; +using OpenTelemetry.Exporter.Prometheus; using OpenTelemetry.Metrics; using OpenTelemetry.Tests; diff --git a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusCollectionManagerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusCollectionManagerTests.cs index adeb3ac29c0..aa680d6495c 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusCollectionManagerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusCollectionManagerTests.cs @@ -21,7 +21,7 @@ #endif using System.Threading; using System.Threading.Tasks; -using OpenTelemetry.Exporter.Prometheus.Shared; +using OpenTelemetry.Exporter.Prometheus.HttpListener.Shared; using OpenTelemetry.Metrics; using OpenTelemetry.Tests; using Xunit; diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests.csproj b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests.csproj index 22b68c4e9ea..4588d2168b5 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests.csproj +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests.csproj @@ -20,6 +20,7 @@ + diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs new file mode 100644 index 00000000000..e6c0865cc3a --- /dev/null +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs @@ -0,0 +1,387 @@ +// +// 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. +// + +using System.Collections.Generic; +using System.Diagnostics.Metrics; +using System.Text; +using OpenTelemetry.Metrics; +using OpenTelemetry.Tests; +using Xunit; + +namespace OpenTelemetry.Exporter.Prometheus +{ + public sealed class PrometheusSerializerTests + { + [Fact] + public void GaugeZeroDimension() + { + var buffer = new byte[85000]; + var metrics = new List(); + + using var meter = new Meter(Utils.GetCurrentMethodName()); + using var provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddInMemoryExporter(metrics) + .Build(); + + meter.CreateObservableGauge("test_gauge", () => 123); + + provider.ForceFlush(); + + var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); + Assert.Matches( + ("^" + + "# TYPE test_gauge gauge\n" + + "test_gauge 123 \\d+\n" + + "$").Replace('\'', '"'), + Encoding.UTF8.GetString(buffer, 0, cursor)); + } + + [Fact] + public void GaugeZeroDimensionWithDescription() + { + var buffer = new byte[85000]; + var metrics = new List(); + + using var meter = new Meter(Utils.GetCurrentMethodName()); + using var provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddInMemoryExporter(metrics) + .Build(); + + meter.CreateObservableGauge("test_gauge", () => 123, description: "Hello, world!"); + + provider.ForceFlush(); + + var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); + Assert.Matches( + ("^" + + "# HELP test_gauge Hello, world!\n" + + "# TYPE test_gauge gauge\n" + + "test_gauge 123 \\d+\n" + + "$").Replace('\'', '"'), + Encoding.UTF8.GetString(buffer, 0, cursor)); + } + + [Fact] + public void GaugeZeroDimensionWithUnit() + { + var buffer = new byte[85000]; + var metrics = new List(); + + using var meter = new Meter(Utils.GetCurrentMethodName()); + using var provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddInMemoryExporter(metrics) + .Build(); + + meter.CreateObservableGauge("test_gauge", () => 123, unit: "seconds"); + + provider.ForceFlush(); + + var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); + Assert.Matches( + ("^" + + "# TYPE test_gauge_seconds gauge\n" + + "test_gauge_seconds 123 \\d+\n" + + "$").Replace('\'', '"'), + Encoding.UTF8.GetString(buffer, 0, cursor)); + } + + [Fact] + public void GaugeOneDimension() + { + var buffer = new byte[85000]; + var metrics = new List(); + + using var meter = new Meter(Utils.GetCurrentMethodName()); + using var provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddInMemoryExporter(metrics) + .Build(); + + meter.CreateObservableGauge( + "test_gauge", + () => new Measurement(123, new KeyValuePair("tagKey", "tagValue"))); + + provider.ForceFlush(); + + var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); + Assert.Matches( + ("^" + + "# TYPE test_gauge gauge\n" + + "test_gauge{tagKey='tagValue'} 123 \\d+\n" + + "$").Replace('\'', '"'), + Encoding.UTF8.GetString(buffer, 0, cursor)); + } + + [Fact] + public void GaugeDoubleSubnormal() + { + var buffer = new byte[85000]; + var metrics = new List(); + + using var meter = new Meter(Utils.GetCurrentMethodName()); + using var provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddInMemoryExporter(metrics) + .Build(); + + meter.CreateObservableGauge("test_gauge", () => new List> + { + new(double.NegativeInfinity, new("x", "1"), new("y", "2")), + new(double.PositiveInfinity, new("x", "3"), new("y", "4")), + new(double.NaN, new("x", "5"), new("y", "6")), + }); + + provider.ForceFlush(); + + var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); + Assert.Matches( + ("^" + + "# TYPE test_gauge gauge\n" + + "test_gauge{x='1',y='2'} -Inf \\d+\n" + + "test_gauge{x='3',y='4'} \\+Inf \\d+\n" + + "test_gauge{x='5',y='6'} Nan \\d+\n" + + "$").Replace('\'', '"'), + Encoding.UTF8.GetString(buffer, 0, cursor)); + } + + [Fact] + public void SumDoubleInfinites() + { + var buffer = new byte[85000]; + var metrics = new List(); + + using var meter = new Meter(Utils.GetCurrentMethodName()); + using var provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddInMemoryExporter(metrics) + .Build(); + + var counter = meter.CreateCounter("test_counter"); + counter.Add(1.0E308); + counter.Add(1.0E308); + + provider.ForceFlush(); + + var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); + Assert.Matches( + ("^" + + "# TYPE test_counter counter\n" + + "test_counter \\+Inf \\d+\n" + + "$").Replace('\'', '"'), + Encoding.UTF8.GetString(buffer, 0, cursor)); + } + + [Fact] + public void HistogramZeroDimension() + { + var buffer = new byte[85000]; + var metrics = new List(); + + using var meter = new Meter(Utils.GetCurrentMethodName()); + using var provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddInMemoryExporter(metrics) + .Build(); + + var histogram = meter.CreateHistogram("test_histogram"); + histogram.Record(18); + histogram.Record(100); + + provider.ForceFlush(); + + var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); + Assert.Matches( + ("^" + + "# TYPE test_histogram histogram\n" + + "test_histogram_bucket{le='0'} 0 \\d+\n" + + "test_histogram_bucket{le='5'} 0 \\d+\n" + + "test_histogram_bucket{le='10'} 0 \\d+\n" + + "test_histogram_bucket{le='25'} 1 \\d+\n" + + "test_histogram_bucket{le='50'} 1 \\d+\n" + + "test_histogram_bucket{le='75'} 1 \\d+\n" + + "test_histogram_bucket{le='100'} 2 \\d+\n" + + "test_histogram_bucket{le='250'} 2 \\d+\n" + + "test_histogram_bucket{le='500'} 2 \\d+\n" + + "test_histogram_bucket{le='1000'} 2 \\d+\n" + + "test_histogram_bucket{le='\\+Inf'} 2 \\d+\n" + + "test_histogram_sum 118 \\d+\n" + + "test_histogram_count 2 \\d+\n" + + "$").Replace('\'', '"'), + Encoding.UTF8.GetString(buffer, 0, cursor)); + } + + [Fact] + public void HistogramOneDimension() + { + var buffer = new byte[85000]; + var metrics = new List(); + + using var meter = new Meter(Utils.GetCurrentMethodName()); + using var provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddInMemoryExporter(metrics) + .Build(); + + var histogram = meter.CreateHistogram("test_histogram"); + histogram.Record(18, new KeyValuePair("x", "1")); + histogram.Record(100, new KeyValuePair("x", "1")); + + provider.ForceFlush(); + + var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); + Assert.Matches( + ("^" + + "# TYPE test_histogram histogram\n" + + "test_histogram_bucket{x='1',le='0'} 0 \\d+\n" + + "test_histogram_bucket{x='1',le='5'} 0 \\d+\n" + + "test_histogram_bucket{x='1',le='10'} 0 \\d+\n" + + "test_histogram_bucket{x='1',le='25'} 1 \\d+\n" + + "test_histogram_bucket{x='1',le='50'} 1 \\d+\n" + + "test_histogram_bucket{x='1',le='75'} 1 \\d+\n" + + "test_histogram_bucket{x='1',le='100'} 2 \\d+\n" + + "test_histogram_bucket{x='1',le='250'} 2 \\d+\n" + + "test_histogram_bucket{x='1',le='500'} 2 \\d+\n" + + "test_histogram_bucket{x='1',le='1000'} 2 \\d+\n" + + "test_histogram_bucket{x='1',le='\\+Inf'} 2 \\d+\n" + + "test_histogram_sum{x='1'} 118 \\d+\n" + + "test_histogram_count{x='1'} 2 \\d+\n" + + "$").Replace('\'', '"'), + Encoding.UTF8.GetString(buffer, 0, cursor)); + } + + [Fact] + public void HistogramTwoDimensions() + { + var buffer = new byte[85000]; + var metrics = new List(); + + using var meter = new Meter(Utils.GetCurrentMethodName()); + using var provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddInMemoryExporter(metrics) + .Build(); + + var histogram = meter.CreateHistogram("test_histogram"); + histogram.Record(18, new("x", "1"), new("y", "2")); + histogram.Record(100, new("x", "1"), new("y", "2")); + + provider.ForceFlush(); + + var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); + Assert.Matches( + ("^" + + "# TYPE test_histogram histogram\n" + + "test_histogram_bucket{x='1',y='2',le='0'} 0 \\d+\n" + + "test_histogram_bucket{x='1',y='2',le='5'} 0 \\d+\n" + + "test_histogram_bucket{x='1',y='2',le='10'} 0 \\d+\n" + + "test_histogram_bucket{x='1',y='2',le='25'} 1 \\d+\n" + + "test_histogram_bucket{x='1',y='2',le='50'} 1 \\d+\n" + + "test_histogram_bucket{x='1',y='2',le='75'} 1 \\d+\n" + + "test_histogram_bucket{x='1',y='2',le='100'} 2 \\d+\n" + + "test_histogram_bucket{x='1',y='2',le='250'} 2 \\d+\n" + + "test_histogram_bucket{x='1',y='2',le='500'} 2 \\d+\n" + + "test_histogram_bucket{x='1',y='2',le='1000'} 2 \\d+\n" + + "test_histogram_bucket{x='1',y='2',le='\\+Inf'} 2 \\d+\n" + + "test_histogram_sum{x='1',y='2'} 118 \\d+\n" + + "test_histogram_count{x='1',y='2'} 2 \\d+\n" + + "$").Replace('\'', '"'), + Encoding.UTF8.GetString(buffer, 0, cursor)); + } + + [Fact] + public void HistogramInfinites() + { + var buffer = new byte[85000]; + var metrics = new List(); + + using var meter = new Meter(Utils.GetCurrentMethodName()); + using var provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddInMemoryExporter(metrics) + .Build(); + + var histogram = meter.CreateHistogram("test_histogram"); + histogram.Record(18); + histogram.Record(double.PositiveInfinity); + histogram.Record(double.PositiveInfinity); + + provider.ForceFlush(); + + var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); + Assert.Matches( + ("^" + + "# TYPE test_histogram histogram\n" + + "test_histogram_bucket{le='0'} 0 \\d+\n" + + "test_histogram_bucket{le='5'} 0 \\d+\n" + + "test_histogram_bucket{le='10'} 0 \\d+\n" + + "test_histogram_bucket{le='25'} 1 \\d+\n" + + "test_histogram_bucket{le='50'} 1 \\d+\n" + + "test_histogram_bucket{le='75'} 1 \\d+\n" + + "test_histogram_bucket{le='100'} 1 \\d+\n" + + "test_histogram_bucket{le='250'} 1 \\d+\n" + + "test_histogram_bucket{le='500'} 1 \\d+\n" + + "test_histogram_bucket{le='1000'} 1 \\d+\n" + + "test_histogram_bucket{le='\\+Inf'} 3 \\d+\n" + + "test_histogram_sum \\+Inf \\d+\n" + + "test_histogram_count 3 \\d+\n" + + "$").Replace('\'', '"'), + Encoding.UTF8.GetString(buffer, 0, cursor)); + } + + [Fact] + public void HistogramNaN() + { + var buffer = new byte[85000]; + var metrics = new List(); + + using var meter = new Meter(Utils.GetCurrentMethodName()); + using var provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddInMemoryExporter(metrics) + .Build(); + + var histogram = meter.CreateHistogram("test_histogram"); + histogram.Record(18); + histogram.Record(double.PositiveInfinity); + histogram.Record(double.NaN); + + provider.ForceFlush(); + + var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); + Assert.Matches( + ("^" + + "# TYPE test_histogram histogram\n" + + "test_histogram_bucket{le='0'} 0 \\d+\n" + + "test_histogram_bucket{le='5'} 0 \\d+\n" + + "test_histogram_bucket{le='10'} 0 \\d+\n" + + "test_histogram_bucket{le='25'} 1 \\d+\n" + + "test_histogram_bucket{le='50'} 1 \\d+\n" + + "test_histogram_bucket{le='75'} 1 \\d+\n" + + "test_histogram_bucket{le='100'} 1 \\d+\n" + + "test_histogram_bucket{le='250'} 1 \\d+\n" + + "test_histogram_bucket{le='500'} 1 \\d+\n" + + "test_histogram_bucket{le='1000'} 1 \\d+\n" + + "test_histogram_bucket{le='\\+Inf'} 3 \\d+\n" + + "test_histogram_sum Nan \\d+\n" + + "test_histogram_count 3 \\d+\n" + + "$").Replace('\'', '"'), + Encoding.UTF8.GetString(buffer, 0, cursor)); + } + } +} From cc5a56e796a750ddc5274da2efa7c1a63823b322 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Mon, 1 Aug 2022 10:33:00 -0700 Subject: [PATCH 44/58] Dust off Prometheus Exporters (#3507) --- .../getting-started-prometheus-grafana.csproj | 3 - examples/Console/TestPrometheusExporter.cs | 3 +- .../netcoreapp3.1/PublicAPI.Unshipped.txt | 14 +- ...etry.Exporter.Prometheus.AspNetCore.csproj | 13 +- ...eusExporterApplicationBuilderExtensions.cs | 2 +- ...sExporterEndpointRouteBuilderExtensions.cs | 2 +- ...sExporterMeterProviderBuilderExtensions.cs | 5 +- .../PrometheusExporterMiddleware.cs | 3 +- .../PrometheusExporterOptions.cs | 10 +- .../.publicApi/net462/PublicAPI.Unshipped.txt | 20 +- .../netstandard2.0/PublicAPI.Unshipped.txt | 20 +- .../PrometheusCollectionManager.cs | 10 +- .../PrometheusExporter.cs | 24 +- .../PrometheusExporterEventSource.cs | 2 +- .../PrometheusSerializer.cs | 0 .../PrometheusSerializerExt.cs | 0 ...ry.Exporter.Prometheus.HttpListener.csproj | 2 +- .../PrometheusHttpListener.cs | 5 +- ...ListenerMeterProviderBuilderExtensions.cs} | 45 +- .../PrometheusHttpListenerOptions.cs | 9 +- .../README.md | 13 +- .../PrometheusCollectionManagerTests.cs | 5 +- ...ests.cs => PrometheusHttpListenerTests.cs} | 16 +- .../PrometheusSerializerTests.cs | 2 +- ...ry.Exporter.Prometheus.Shared.Tests.csproj | 32 -- .../PrometheusSerializerTests.cs | 387 ------------------ .../Program.cs | 3 +- test/OpenTelemetry.Tests.Stress/Skeleton.cs | 3 +- 28 files changed, 97 insertions(+), 556 deletions(-) rename src/{OpenTelemetry.Exporter.Prometheus.HttpListener/Shared => OpenTelemetry.Exporter.Prometheus.AspNetCore}/PrometheusExporterOptions.cs (85%) rename src/OpenTelemetry.Exporter.Prometheus.HttpListener/{Shared => Internal}/PrometheusCollectionManager.cs (95%) rename src/OpenTelemetry.Exporter.Prometheus.HttpListener/{Shared => Internal}/PrometheusExporter.cs (74%) rename src/OpenTelemetry.Exporter.Prometheus.HttpListener/{Shared => Internal}/PrometheusExporterEventSource.cs (97%) rename src/OpenTelemetry.Exporter.Prometheus.HttpListener/{Shared => Internal}/PrometheusSerializer.cs (100%) rename src/OpenTelemetry.Exporter.Prometheus.HttpListener/{Shared => Internal}/PrometheusSerializerExt.cs (100%) rename src/OpenTelemetry.Exporter.Prometheus.HttpListener/{PrometheusExporterHttpListenerMeterProviderBuilderExtensions.cs => PrometheusHttpListenerMeterProviderBuilderExtensions.cs} (52%) rename test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/{PrometheusExporterHttpListenerTests.cs => PrometheusHttpListenerTests.cs} (88%) delete mode 100644 test/OpenTelemetry.Exporter.Prometheus.Shared.Tests/OpenTelemetry.Exporter.Prometheus.Shared.Tests.csproj delete mode 100644 test/OpenTelemetry.Exporter.Prometheus.Shared.Tests/PrometheusSerializerTests.cs diff --git a/docs/metrics/getting-started-prometheus-grafana/getting-started-prometheus-grafana.csproj b/docs/metrics/getting-started-prometheus-grafana/getting-started-prometheus-grafana.csproj index 455ed30ceb4..8d59ff99ce3 100644 --- a/docs/metrics/getting-started-prometheus-grafana/getting-started-prometheus-grafana.csproj +++ b/docs/metrics/getting-started-prometheus-grafana/getting-started-prometheus-grafana.csproj @@ -1,7 +1,4 @@ - - netstandard2.0 - diff --git a/examples/Console/TestPrometheusExporter.cs b/examples/Console/TestPrometheusExporter.cs index 29fc3879f39..0b1284c4ae0 100644 --- a/examples/Console/TestPrometheusExporter.cs +++ b/examples/Console/TestPrometheusExporter.cs @@ -52,8 +52,7 @@ internal static object Run(int port) .AddMeter(MyMeter.Name) .AddMeter(MyMeter2.Name) .AddPrometheusHttpListener( - exporterOptions => exporterOptions.ScrapeResponseCacheDurationMilliseconds = 0, - listenerOptions => listenerOptions.Prefixes = new string[] { $"http://localhost:{port}/" }) + options => options.Prefixes = new string[] { $"http://localhost:{port}/" }) .Build(); var process = Process.GetCurrentProcess(); diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt index f9271183c1c..3c9d701eb94 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt @@ -1,11 +1,11 @@ Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions Microsoft.AspNetCore.Builder.PrometheusExporterEndpointRouteBuilderExtensions -OpenTelemetry.Exporter.Prometheus.AspNetCore.PrometheusExporterOptions -OpenTelemetry.Exporter.Prometheus.AspNetCore.PrometheusExporterOptions.PrometheusExporterOptions() -> void -OpenTelemetry.Exporter.Prometheus.AspNetCore.PrometheusExporterOptions.ScrapeEndpointPath.get -> string -OpenTelemetry.Exporter.Prometheus.AspNetCore.PrometheusExporterOptions.ScrapeEndpointPath.set -> void -OpenTelemetry.Exporter.Prometheus.AspNetCore.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.get -> int -OpenTelemetry.Exporter.Prometheus.AspNetCore.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.set -> void +OpenTelemetry.Exporter.Prometheus.PrometheusExporterOptions +OpenTelemetry.Exporter.Prometheus.PrometheusExporterOptions.PrometheusExporterOptions() -> void +OpenTelemetry.Exporter.Prometheus.PrometheusExporterOptions.ScrapeEndpointPath.get -> string +OpenTelemetry.Exporter.Prometheus.PrometheusExporterOptions.ScrapeEndpointPath.set -> void +OpenTelemetry.Exporter.Prometheus.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.get -> int +OpenTelemetry.Exporter.Prometheus.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.set -> void OpenTelemetry.Metrics.PrometheusExporterMeterProviderBuilderExtensions static Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions.UseOpenTelemetryPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Builder.IApplicationBuilder app) -> Microsoft.AspNetCore.Builder.IApplicationBuilder static Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions.UseOpenTelemetryPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, OpenTelemetry.Metrics.MeterProvider meterProvider, System.Func predicate, string path, System.Action configureBranchedPipeline) -> Microsoft.AspNetCore.Builder.IApplicationBuilder @@ -14,4 +14,4 @@ static Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensio static Microsoft.AspNetCore.Builder.PrometheusExporterEndpointRouteBuilderExtensions.MapPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder static Microsoft.AspNetCore.Builder.PrometheusExporterEndpointRouteBuilderExtensions.MapPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string path = null, OpenTelemetry.Metrics.MeterProvider meterProvider = null, System.Action configureBranchedPipeline = null) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder static Microsoft.AspNetCore.Builder.PrometheusExporterEndpointRouteBuilderExtensions.MapPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string path) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder -static OpenTelemetry.Metrics.PrometheusExporterMeterProviderBuilderExtensions.AddPrometheusExporter(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configure = null) -> OpenTelemetry.Metrics.MeterProviderBuilder +static OpenTelemetry.Metrics.PrometheusExporterMeterProviderBuilderExtensions.AddPrometheusExporter(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configure = null) -> OpenTelemetry.Metrics.MeterProviderBuilder diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj index 33a00e05f1a..f8f55cb18e3 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj @@ -3,7 +3,7 @@ netcoreapp3.1 - AspNetCore middleware for hosting OpenTelemetry .NET Prometheus exporter + ASP.NET Core middleware for hosting OpenTelemetry .NET Prometheus Exporter $(PackageTags);prometheus;metrics core- @@ -19,14 +19,13 @@ - - - - - - + + + + + diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs index 00fe170e6fb..491e70eac02 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs @@ -19,7 +19,7 @@ using System; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; -using OpenTelemetry.Exporter.Prometheus.AspNetCore; +using OpenTelemetry.Exporter.Prometheus; using OpenTelemetry.Internal; using OpenTelemetry.Metrics; diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs index 372a96e8a81..5d260878ffa 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs @@ -20,7 +20,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; -using OpenTelemetry.Exporter.Prometheus.AspNetCore; +using OpenTelemetry.Exporter.Prometheus; using OpenTelemetry.Internal; using OpenTelemetry.Metrics; diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs index d230d03d042..7039db2a369 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs @@ -15,8 +15,7 @@ // using System; -using OpenTelemetry.Exporter.Prometheus.AspNetCore; -using OpenTelemetry.Exporter.Prometheus.HttpListener.Shared; +using OpenTelemetry.Exporter.Prometheus; using OpenTelemetry.Internal; namespace OpenTelemetry.Metrics @@ -51,7 +50,7 @@ private static MeterProviderBuilder AddPrometheusExporter(MeterProviderBuilder b { configure?.Invoke(options); - var exporter = new PrometheusExporter(options); + var exporter = new PrometheusExporter(scrapeEndpointPath: options.ScrapeEndpointPath, scrapeResponseCacheDurationMilliseconds: options.ScrapeResponseCacheDurationMilliseconds); var reader = new BaseExportingMetricReader(exporter) { TemporalityPreference = MetricReaderTemporalityPreference.Cumulative, diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs index d76a85d44d3..826ca135763 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs @@ -19,11 +19,10 @@ using System.Diagnostics; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using OpenTelemetry.Exporter.Prometheus.HttpListener.Shared; using OpenTelemetry.Internal; using OpenTelemetry.Metrics; -namespace OpenTelemetry.Exporter.Prometheus.AspNetCore +namespace OpenTelemetry.Exporter.Prometheus { /// /// ASP.NET Core middleware for exposing a Prometheus metrics scraping endpoint. diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporterOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterOptions.cs similarity index 85% rename from src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporterOptions.cs rename to src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterOptions.cs index 4662ee3e263..bf7576117b1 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterOptions.cs @@ -14,14 +14,9 @@ // limitations under the License. // -using System; using OpenTelemetry.Internal; -#if PROMETHEUS_ASPNETCORE -namespace OpenTelemetry.Exporter.Prometheus.AspNetCore -#else -namespace OpenTelemetry.Exporter.Prometheus.HttpListener.Shared -#endif +namespace OpenTelemetry.Exporter.Prometheus { /// /// Prometheus exporter options. @@ -29,12 +24,11 @@ namespace OpenTelemetry.Exporter.Prometheus.HttpListener.Shared public class PrometheusExporterOptions { internal const string DefaultScrapeEndpointPath = "/metrics"; - internal Func GetUtcNowDateTimeOffset = () => DateTimeOffset.UtcNow; private int scrapeResponseCacheDurationMilliseconds = 10 * 1000; /// - /// Gets or sets the path to use for the scraping endpoint. Default value: /metrics. + /// Gets or sets the path to use for the scraping endpoint. Default value: "/metrics". /// public string ScrapeEndpointPath { get; set; } = DefaultScrapeEndpointPath; diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt index 5f48f2c7562..7b61e63513e 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt @@ -1,12 +1,8 @@ -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.Prefixes.get -> System.Collections.Generic.IReadOnlyCollection -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.Prefixes.set -> void -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.PrometheusHttpListenerOptions() -> void -OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions -OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.PrometheusExporterOptions() -> void -OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.ScrapeEndpointPath.get -> string -OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.ScrapeEndpointPath.set -> void -OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.get -> int -OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.set -> void -OpenTelemetry.Metrics.PrometheusExporterHttpListenerMeterProviderBuilderExtensions -static OpenTelemetry.Metrics.PrometheusExporterHttpListenerMeterProviderBuilderExtensions.AddPrometheusHttpListener(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configureExporterOptions = null, System.Action configureListenerOptions = null) -> OpenTelemetry.Metrics.MeterProviderBuilder +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.Prefixes.get -> System.Collections.Generic.IReadOnlyCollection +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.Prefixes.set -> void +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.PrometheusHttpListenerOptions() -> void +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.ScrapeEndpointPath.get -> string +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.ScrapeEndpointPath.set -> void +OpenTelemetry.Metrics.PrometheusHttpListenerMeterProviderBuilderExtensions +static OpenTelemetry.Metrics.PrometheusHttpListenerMeterProviderBuilderExtensions.AddPrometheusHttpListener(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configure = null) -> OpenTelemetry.Metrics.MeterProviderBuilder diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index 5f48f2c7562..7b61e63513e 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,12 +1,8 @@ -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.Prefixes.get -> System.Collections.Generic.IReadOnlyCollection -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.Prefixes.set -> void -OpenTelemetry.Exporter.Prometheus.HttpListener.PrometheusHttpListenerOptions.PrometheusHttpListenerOptions() -> void -OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions -OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.PrometheusExporterOptions() -> void -OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.ScrapeEndpointPath.get -> string -OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.ScrapeEndpointPath.set -> void -OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.get -> int -OpenTelemetry.Exporter.Prometheus.HttpListener.Shared.PrometheusExporterOptions.ScrapeResponseCacheDurationMilliseconds.set -> void -OpenTelemetry.Metrics.PrometheusExporterHttpListenerMeterProviderBuilderExtensions -static OpenTelemetry.Metrics.PrometheusExporterHttpListenerMeterProviderBuilderExtensions.AddPrometheusHttpListener(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configureExporterOptions = null, System.Action configureListenerOptions = null) -> OpenTelemetry.Metrics.MeterProviderBuilder +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.Prefixes.get -> System.Collections.Generic.IReadOnlyCollection +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.Prefixes.set -> void +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.PrometheusHttpListenerOptions() -> void +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.ScrapeEndpointPath.get -> string +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.ScrapeEndpointPath.set -> void +OpenTelemetry.Metrics.PrometheusHttpListenerMeterProviderBuilderExtensions +static OpenTelemetry.Metrics.PrometheusHttpListenerMeterProviderBuilderExtensions.AddPrometheusHttpListener(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configure = null) -> OpenTelemetry.Metrics.MeterProviderBuilder diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusCollectionManager.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs similarity index 95% rename from src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusCollectionManager.cs rename to src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs index 43da102246f..704422b637e 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusCollectionManager.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs @@ -20,12 +20,12 @@ using System.Threading.Tasks; using OpenTelemetry.Metrics; -namespace OpenTelemetry.Exporter.Prometheus.HttpListener.Shared +namespace OpenTelemetry.Exporter.Prometheus { internal sealed class PrometheusCollectionManager { private readonly PrometheusExporter exporter; - private readonly int scrapeResponseCacheDurationInMilliseconds; + private readonly int scrapeResponseCacheDurationMilliseconds; private readonly Func, ExportResult> onCollectRef; private byte[] buffer = new byte[85000]; // encourage the object to live in LOH (large object heap) private int globalLockState; @@ -38,7 +38,7 @@ internal sealed class PrometheusCollectionManager public PrometheusCollectionManager(PrometheusExporter exporter) { this.exporter = exporter; - this.scrapeResponseCacheDurationInMilliseconds = this.exporter.Options.ScrapeResponseCacheDurationMilliseconds; + this.scrapeResponseCacheDurationMilliseconds = this.exporter.ScrapeResponseCacheDurationMilliseconds; this.onCollectRef = this.OnCollect; } @@ -53,8 +53,8 @@ public Task EnterCollect() // If we are within {ScrapeResponseCacheDurationMilliseconds} of the // last successful collect, return the previous view. if (this.previousDataViewGeneratedAtUtc.HasValue - && this.scrapeResponseCacheDurationInMilliseconds > 0 - && this.previousDataViewGeneratedAtUtc.Value.AddMilliseconds(this.scrapeResponseCacheDurationInMilliseconds) >= DateTime.UtcNow) + && this.scrapeResponseCacheDurationMilliseconds > 0 + && this.previousDataViewGeneratedAtUtc.Value.AddMilliseconds(this.scrapeResponseCacheDurationMilliseconds) >= DateTime.UtcNow) { Interlocked.Increment(ref this.readerCount); this.ExitGlobalLock(); diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporter.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs similarity index 74% rename from src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporter.cs rename to src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs index ec2dbf0467b..93514ef4c97 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporter.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs @@ -15,12 +15,10 @@ // using System; -#if PROMETHEUS_ASPNETCORE -using OpenTelemetry.Exporter.Prometheus.AspNetCore; -#endif +using OpenTelemetry.Internal; using OpenTelemetry.Metrics; -namespace OpenTelemetry.Exporter.Prometheus.HttpListener.Shared +namespace OpenTelemetry.Exporter.Prometheus { /// /// Exporter of OpenTelemetry metrics to Prometheus. @@ -28,8 +26,6 @@ namespace OpenTelemetry.Exporter.Prometheus.HttpListener.Shared [ExportModes(ExportModes.Pull)] internal sealed class PrometheusExporter : BaseExporter, IPullMetricExporter { - internal const string HttpListenerStartFailureExceptionMessage = "PrometheusExporter http listener could not be started."; - internal readonly PrometheusExporterOptions Options; private Func funcCollect; private Func, ExportResult> funcExport; private bool disposed = false; @@ -37,10 +33,16 @@ internal sealed class PrometheusExporter : BaseExporter, IPullMetricExpo /// /// Initializes a new instance of the class. /// - /// Options for the exporter. - public PrometheusExporter(PrometheusExporterOptions options) + /// Scraping endpoint. + /// + /// The cache duration in milliseconds for scrape responses. Default value: 0. + /// + public PrometheusExporter(string scrapeEndpointPath = null, int scrapeResponseCacheDurationMilliseconds = 0) { - this.Options = options; + Guard.ThrowIfOutOfRange(scrapeResponseCacheDurationMilliseconds, min: 0); + + this.ScrapeEndpointPath = scrapeEndpointPath ?? "/metrics"; + this.ScrapeResponseCacheDurationMilliseconds = scrapeResponseCacheDurationMilliseconds; this.CollectionManager = new PrometheusCollectionManager(this); } @@ -63,6 +65,10 @@ internal Func, ExportResult> OnExport internal PrometheusCollectionManager CollectionManager { get; } + internal int ScrapeResponseCacheDurationMilliseconds { get; } + + internal string ScrapeEndpointPath { get; } + /// public override ExportResult Export(in Batch metrics) { diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporterEventSource.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterEventSource.cs similarity index 97% rename from src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporterEventSource.cs rename to src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterEventSource.cs index 6cdbf9a9024..b678cb436e2 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusExporterEventSource.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterEventSource.cs @@ -18,7 +18,7 @@ using System.Diagnostics.Tracing; using OpenTelemetry.Internal; -namespace OpenTelemetry.Exporter.Prometheus.HttpListener.Shared +namespace OpenTelemetry.Exporter.Prometheus { /// /// EventSource events emitted from the project. diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusSerializer.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs similarity index 100% rename from src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusSerializer.cs rename to src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusSerializerExt.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs similarity index 100% rename from src/OpenTelemetry.Exporter.Prometheus.HttpListener/Shared/PrometheusSerializerExt.cs rename to src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj index ff00ef3db79..e9c46371703 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj @@ -3,7 +3,7 @@ netstandard2.0;net462 - Stand-alone HttpListener for hosting OpenTelemetry .NET exporter + Stand-alone HttpListener for hosting OpenTelemetry .NET Prometheus Exporter $(PackageTags);prometheus;metrics core- diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs index 6badb4d5337..efd022d79b1 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs @@ -18,10 +18,9 @@ using System.Net; using System.Threading; using System.Threading.Tasks; -using OpenTelemetry.Exporter.Prometheus.HttpListener.Shared; using OpenTelemetry.Internal; -namespace OpenTelemetry.Exporter.Prometheus.HttpListener +namespace OpenTelemetry.Exporter.Prometheus { internal sealed class PrometheusHttpListener : IDisposable { @@ -47,7 +46,7 @@ public PrometheusHttpListener(PrometheusExporter exporter, PrometheusHttpListene } this.exporter = exporter; - string path = this.exporter.Options.ScrapeEndpointPath ?? PrometheusExporterOptions.DefaultScrapeEndpointPath; + string path = this.exporter.ScrapeEndpointPath; if (!path.StartsWith("/")) { path = $"/{path}"; diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusExporterHttpListenerMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs similarity index 52% rename from src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusExporterHttpListenerMeterProviderBuilderExtensions.cs rename to src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs index 8c53f09837d..6c0b7b75a41 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusExporterHttpListenerMeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,7 @@ // using System; -using OpenTelemetry.Exporter.Prometheus.HttpListener; -using OpenTelemetry.Exporter.Prometheus.HttpListener.Shared; +using OpenTelemetry.Exporter.Prometheus; using OpenTelemetry.Internal; namespace OpenTelemetry.Metrics @@ -24,19 +23,17 @@ namespace OpenTelemetry.Metrics /// /// Extension methods to simplify registering a PrometheusHttpListener. /// - public static class PrometheusExporterHttpListenerMeterProviderBuilderExtensions + public static class PrometheusHttpListenerMeterProviderBuilderExtensions { /// - /// Adds Prometheus exporter to MeterProviderBuilder. + /// Adds PrometheusHttpListener to MeterProviderBuilder. /// /// builder to use. - /// Exporter configuration options. - /// HttpListener options. + /// PrometheusHttpListenerOptions options. /// The instance of to chain calls. public static MeterProviderBuilder AddPrometheusHttpListener( this MeterProviderBuilder builder, - Action configureExporterOptions = null, - Action configureListenerOptions = null) + Action configure = null) { Guard.ThrowIfNull(builder); @@ -44,44 +41,30 @@ public static MeterProviderBuilder AddPrometheusHttpListener( { return deferredMeterProviderBuilder.Configure((sp, builder) => { - AddPrometheusHttpListener( - builder, - sp.GetOptions(), - sp.GetOptions(), - configureExporterOptions, - configureListenerOptions); + AddPrometheusHttpListener(builder, sp.GetOptions(), configure); }); } - return AddPrometheusHttpListener( - builder, - new PrometheusExporterOptions(), - new PrometheusHttpListenerOptions(), - configureExporterOptions, - configureListenerOptions); + return AddPrometheusHttpListener(builder, new PrometheusHttpListenerOptions(), configure); } private static MeterProviderBuilder AddPrometheusHttpListener( MeterProviderBuilder builder, - PrometheusExporterOptions exporterOptions, - PrometheusHttpListenerOptions listenerOptions, - Action configureExporterOptions = null, - Action configureListenerOptions = null) + PrometheusHttpListenerOptions options, + Action configure = null) { - configureExporterOptions?.Invoke(exporterOptions); - configureListenerOptions?.Invoke(listenerOptions); + configure?.Invoke(options); - var exporter = new PrometheusExporter(exporterOptions); + var exporter = new PrometheusExporter(scrapeEndpointPath: options.ScrapeEndpointPath); var reader = new BaseExportingMetricReader(exporter) { TemporalityPreference = MetricReaderTemporalityPreference.Cumulative, }; - const string HttpListenerStartFailureExceptionMessage = "PrometheusExporter HttpListener could not be started."; try { - var listener = new PrometheusHttpListener(exporter, listenerOptions); + var listener = new PrometheusHttpListener(exporter, options); exporter.OnDispose = () => listener.Dispose(); listener.Start(); } @@ -95,7 +78,7 @@ private static MeterProviderBuilder AddPrometheusHttpListener( { } - throw new InvalidOperationException(HttpListenerStartFailureExceptionMessage, ex); + throw new InvalidOperationException("PrometheusExporter HttpListener could not be started.", ex); } return builder.AddReader(reader); diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs index 264a5f229fd..dc1e8e5d9ea 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs @@ -18,7 +18,7 @@ using System.Collections.Generic; using OpenTelemetry.Internal; -namespace OpenTelemetry.Exporter.Prometheus.HttpListener +namespace OpenTelemetry.Exporter.Prometheus { /// /// options. @@ -27,9 +27,14 @@ public class PrometheusHttpListenerOptions { private IReadOnlyCollection prefixes = new string[] { "http://localhost:9464/" }; + /// + /// Gets or sets the path to use for the scraping endpoint. Default value: "/metrics". + /// + public string ScrapeEndpointPath { get; set; } = "/metrics"; + /// /// Gets or sets the prefixes to use for the http listener. - /// Default value: http://localhost:9464/. + /// Default value: ["http://localhost:9464/"]. /// public IReadOnlyCollection Prefixes { diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md index 8e772a4754e..b997b5adecc 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md @@ -23,8 +23,7 @@ dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener ### Step 2: Add PrometheusHttpListener -Add and configure `PrometheusHttpListener` with `PrometheusExporterOptions` as -the first argument and `PrometheusHttpListenerOptions` as the second argument. +Add and configure `PrometheusHttpListener` with `PrometheusHttpListenerOptions`. For example: @@ -32,8 +31,7 @@ For example: using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(MyMeter.Name) .AddPrometheusHttpListener( - exporterOptions => exporterOptions.ScrapeResponseCacheDurationMilliseconds = 0, - listenerOptions => listenerOptions.Prefixes = new string[] { "http://localhost:9464/" }) + options => options.Prefixes = new string[] { "http://localhost:9464/" }) .Build(); ``` @@ -50,13 +48,6 @@ For details see: Defines the path for the Prometheus scrape endpoint for by `UseOpenTelemetryPrometheusScrapingEndpoint`. Default value: `"/metrics"`. -### ScrapeResponseCacheDurationMilliseconds - -Configures scrape endpoint response caching. Multiple scrape requests within the -cache duration time period will receive the same previously generated response. -The default value is `10000` (10 seconds). Set to `0` to disable response -caching. - ## Troubleshooting This component uses an diff --git a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusCollectionManagerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusCollectionManagerTests.cs index aa680d6495c..6f739cbc175 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusCollectionManagerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusCollectionManagerTests.cs @@ -21,12 +21,11 @@ #endif using System.Threading; using System.Threading.Tasks; -using OpenTelemetry.Exporter.Prometheus.HttpListener.Shared; using OpenTelemetry.Metrics; using OpenTelemetry.Tests; using Xunit; -namespace OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests +namespace OpenTelemetry.Exporter.Prometheus.Tests { public sealed class PrometheusCollectionManagerTests { @@ -110,7 +109,7 @@ public async Task EnterExitCollectTest() exporter.CollectionManager.ExitCollect(); } - Thread.Sleep(exporter.Options.ScrapeResponseCacheDurationMilliseconds); + Thread.Sleep(exporter.ScrapeResponseCacheDurationMilliseconds); counter.Add(100); diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusExporterHttpListenerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs similarity index 88% rename from test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusExporterHttpListenerTests.cs rename to test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs index d197ffea85a..bfaa2f06f56 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusExporterHttpListenerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,9 +24,9 @@ using OpenTelemetry.Tests; using Xunit; -namespace OpenTelemetry.Exporter.Prometheus.HttpListener.Tests +namespace OpenTelemetry.Exporter.Prometheus.Tests { - public class PrometheusExporterHttpListenerTests + public class PrometheusHttpListenerTests { private readonly string meterName = Utils.GetCurrentMethodName(); @@ -38,7 +38,7 @@ public class PrometheusExporterHttpListenerTests public void ServerEndpointSanityCheckPositiveTest(params string[] uris) { using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() - .AddPrometheusHttpListener(null, listenerOptions => listenerOptions.Prefixes = uris) + .AddPrometheusHttpListener(options => options.Prefixes = uris) .Build(); } @@ -52,7 +52,7 @@ public void ServerEndpointSanityCheckNegativeTest(params string[] uris) try { using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() - .AddPrometheusHttpListener(null, listenerOptions => listenerOptions.Prefixes = uris) + .AddPrometheusHttpListener(options => options.Prefixes = uris) .Build(); } catch (Exception ex) @@ -97,9 +97,9 @@ private async Task RunPrometheusExporterHttpServerIntegrationTest(bool skipMetri address = $"http://localhost:{port}/"; provider = Sdk.CreateMeterProviderBuilder() - .AddMeter(meter.Name) - .AddPrometheusHttpListener(null, listenerOptions => listenerOptions.Prefixes = new string[] { address }) - .Build(); + .AddMeter(meter.Name) + .AddPrometheusHttpListener(options => options.Prefixes = new string[] { address }) + .Build(); } var tags = new KeyValuePair[] diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs index e6c0865cc3a..cb2ea1d5641 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs @@ -21,7 +21,7 @@ using OpenTelemetry.Tests; using Xunit; -namespace OpenTelemetry.Exporter.Prometheus +namespace OpenTelemetry.Exporter.Prometheus.Tests { public sealed class PrometheusSerializerTests { diff --git a/test/OpenTelemetry.Exporter.Prometheus.Shared.Tests/OpenTelemetry.Exporter.Prometheus.Shared.Tests.csproj b/test/OpenTelemetry.Exporter.Prometheus.Shared.Tests/OpenTelemetry.Exporter.Prometheus.Shared.Tests.csproj deleted file mode 100644 index 640b3541f2e..00000000000 --- a/test/OpenTelemetry.Exporter.Prometheus.Shared.Tests/OpenTelemetry.Exporter.Prometheus.Shared.Tests.csproj +++ /dev/null @@ -1,32 +0,0 @@ - - - Unit test project of Prometheus exporter shared code for both Prometheus exporter HttpListener and Prometheus Exporter AspNetCore - - netcoreapp3.1 - $(TargetFrameworks);net462 - - false - - - - - - - - all - runtime; build; native; contentfiles; analyzers - - - - - - - - - - - - - - - diff --git a/test/OpenTelemetry.Exporter.Prometheus.Shared.Tests/PrometheusSerializerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.Shared.Tests/PrometheusSerializerTests.cs deleted file mode 100644 index 1def2012b8e..00000000000 --- a/test/OpenTelemetry.Exporter.Prometheus.Shared.Tests/PrometheusSerializerTests.cs +++ /dev/null @@ -1,387 +0,0 @@ -// -// 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. -// - -using System.Collections.Generic; -using System.Diagnostics.Metrics; -using System.Text; -using OpenTelemetry.Metrics; -using OpenTelemetry.Tests; -using Xunit; - -namespace OpenTelemetry.Exporter.Prometheus.Shared.Tests -{ - public sealed class PrometheusSerializerTests - { - [Fact] - public void GaugeZeroDimension() - { - var buffer = new byte[85000]; - var metrics = new List(); - - using var meter = new Meter(Utils.GetCurrentMethodName()); - using var provider = Sdk.CreateMeterProviderBuilder() - .AddMeter(meter.Name) - .AddInMemoryExporter(metrics) - .Build(); - - meter.CreateObservableGauge("test_gauge", () => 123); - - provider.ForceFlush(); - - var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); - Assert.Matches( - ("^" - + "# TYPE test_gauge gauge\n" - + "test_gauge 123 \\d+\n" - + "$").Replace('\'', '"'), - Encoding.UTF8.GetString(buffer, 0, cursor)); - } - - [Fact] - public void GaugeZeroDimensionWithDescription() - { - var buffer = new byte[85000]; - var metrics = new List(); - - using var meter = new Meter(Utils.GetCurrentMethodName()); - using var provider = Sdk.CreateMeterProviderBuilder() - .AddMeter(meter.Name) - .AddInMemoryExporter(metrics) - .Build(); - - meter.CreateObservableGauge("test_gauge", () => 123, description: "Hello, world!"); - - provider.ForceFlush(); - - var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); - Assert.Matches( - ("^" - + "# HELP test_gauge Hello, world!\n" - + "# TYPE test_gauge gauge\n" - + "test_gauge 123 \\d+\n" - + "$").Replace('\'', '"'), - Encoding.UTF8.GetString(buffer, 0, cursor)); - } - - [Fact] - public void GaugeZeroDimensionWithUnit() - { - var buffer = new byte[85000]; - var metrics = new List(); - - using var meter = new Meter(Utils.GetCurrentMethodName()); - using var provider = Sdk.CreateMeterProviderBuilder() - .AddMeter(meter.Name) - .AddInMemoryExporter(metrics) - .Build(); - - meter.CreateObservableGauge("test_gauge", () => 123, unit: "seconds"); - - provider.ForceFlush(); - - var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); - Assert.Matches( - ("^" - + "# TYPE test_gauge_seconds gauge\n" - + "test_gauge_seconds 123 \\d+\n" - + "$").Replace('\'', '"'), - Encoding.UTF8.GetString(buffer, 0, cursor)); - } - - [Fact] - public void GaugeOneDimension() - { - var buffer = new byte[85000]; - var metrics = new List(); - - using var meter = new Meter(Utils.GetCurrentMethodName()); - using var provider = Sdk.CreateMeterProviderBuilder() - .AddMeter(meter.Name) - .AddInMemoryExporter(metrics) - .Build(); - - meter.CreateObservableGauge( - "test_gauge", - () => new Measurement(123, new KeyValuePair("tagKey", "tagValue"))); - - provider.ForceFlush(); - - var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); - Assert.Matches( - ("^" - + "# TYPE test_gauge gauge\n" - + "test_gauge{tagKey='tagValue'} 123 \\d+\n" - + "$").Replace('\'', '"'), - Encoding.UTF8.GetString(buffer, 0, cursor)); - } - - [Fact] - public void GaugeDoubleSubnormal() - { - var buffer = new byte[85000]; - var metrics = new List(); - - using var meter = new Meter(Utils.GetCurrentMethodName()); - using var provider = Sdk.CreateMeterProviderBuilder() - .AddMeter(meter.Name) - .AddInMemoryExporter(metrics) - .Build(); - - meter.CreateObservableGauge("test_gauge", () => new List> - { - new(double.NegativeInfinity, new("x", "1"), new("y", "2")), - new(double.PositiveInfinity, new("x", "3"), new("y", "4")), - new(double.NaN, new("x", "5"), new("y", "6")), - }); - - provider.ForceFlush(); - - var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); - Assert.Matches( - ("^" - + "# TYPE test_gauge gauge\n" - + "test_gauge{x='1',y='2'} -Inf \\d+\n" - + "test_gauge{x='3',y='4'} \\+Inf \\d+\n" - + "test_gauge{x='5',y='6'} Nan \\d+\n" - + "$").Replace('\'', '"'), - Encoding.UTF8.GetString(buffer, 0, cursor)); - } - - [Fact] - public void SumDoubleInfinites() - { - var buffer = new byte[85000]; - var metrics = new List(); - - using var meter = new Meter(Utils.GetCurrentMethodName()); - using var provider = Sdk.CreateMeterProviderBuilder() - .AddMeter(meter.Name) - .AddInMemoryExporter(metrics) - .Build(); - - var counter = meter.CreateCounter("test_counter"); - counter.Add(1.0E308); - counter.Add(1.0E308); - - provider.ForceFlush(); - - var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); - Assert.Matches( - ("^" - + "# TYPE test_counter counter\n" - + "test_counter \\+Inf \\d+\n" - + "$").Replace('\'', '"'), - Encoding.UTF8.GetString(buffer, 0, cursor)); - } - - [Fact] - public void HistogramZeroDimension() - { - var buffer = new byte[85000]; - var metrics = new List(); - - using var meter = new Meter(Utils.GetCurrentMethodName()); - using var provider = Sdk.CreateMeterProviderBuilder() - .AddMeter(meter.Name) - .AddInMemoryExporter(metrics) - .Build(); - - var histogram = meter.CreateHistogram("test_histogram"); - histogram.Record(18); - histogram.Record(100); - - provider.ForceFlush(); - - var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); - Assert.Matches( - ("^" - + "# TYPE test_histogram histogram\n" - + "test_histogram_bucket{le='0'} 0 \\d+\n" - + "test_histogram_bucket{le='5'} 0 \\d+\n" - + "test_histogram_bucket{le='10'} 0 \\d+\n" - + "test_histogram_bucket{le='25'} 1 \\d+\n" - + "test_histogram_bucket{le='50'} 1 \\d+\n" - + "test_histogram_bucket{le='75'} 1 \\d+\n" - + "test_histogram_bucket{le='100'} 2 \\d+\n" - + "test_histogram_bucket{le='250'} 2 \\d+\n" - + "test_histogram_bucket{le='500'} 2 \\d+\n" - + "test_histogram_bucket{le='1000'} 2 \\d+\n" - + "test_histogram_bucket{le='\\+Inf'} 2 \\d+\n" - + "test_histogram_sum 118 \\d+\n" - + "test_histogram_count 2 \\d+\n" - + "$").Replace('\'', '"'), - Encoding.UTF8.GetString(buffer, 0, cursor)); - } - - [Fact] - public void HistogramOneDimension() - { - var buffer = new byte[85000]; - var metrics = new List(); - - using var meter = new Meter(Utils.GetCurrentMethodName()); - using var provider = Sdk.CreateMeterProviderBuilder() - .AddMeter(meter.Name) - .AddInMemoryExporter(metrics) - .Build(); - - var histogram = meter.CreateHistogram("test_histogram"); - histogram.Record(18, new KeyValuePair("x", "1")); - histogram.Record(100, new KeyValuePair("x", "1")); - - provider.ForceFlush(); - - var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); - Assert.Matches( - ("^" - + "# TYPE test_histogram histogram\n" - + "test_histogram_bucket{x='1',le='0'} 0 \\d+\n" - + "test_histogram_bucket{x='1',le='5'} 0 \\d+\n" - + "test_histogram_bucket{x='1',le='10'} 0 \\d+\n" - + "test_histogram_bucket{x='1',le='25'} 1 \\d+\n" - + "test_histogram_bucket{x='1',le='50'} 1 \\d+\n" - + "test_histogram_bucket{x='1',le='75'} 1 \\d+\n" - + "test_histogram_bucket{x='1',le='100'} 2 \\d+\n" - + "test_histogram_bucket{x='1',le='250'} 2 \\d+\n" - + "test_histogram_bucket{x='1',le='500'} 2 \\d+\n" - + "test_histogram_bucket{x='1',le='1000'} 2 \\d+\n" - + "test_histogram_bucket{x='1',le='\\+Inf'} 2 \\d+\n" - + "test_histogram_sum{x='1'} 118 \\d+\n" - + "test_histogram_count{x='1'} 2 \\d+\n" - + "$").Replace('\'', '"'), - Encoding.UTF8.GetString(buffer, 0, cursor)); - } - - [Fact] - public void HistogramTwoDimensions() - { - var buffer = new byte[85000]; - var metrics = new List(); - - using var meter = new Meter(Utils.GetCurrentMethodName()); - using var provider = Sdk.CreateMeterProviderBuilder() - .AddMeter(meter.Name) - .AddInMemoryExporter(metrics) - .Build(); - - var histogram = meter.CreateHistogram("test_histogram"); - histogram.Record(18, new("x", "1"), new("y", "2")); - histogram.Record(100, new("x", "1"), new("y", "2")); - - provider.ForceFlush(); - - var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); - Assert.Matches( - ("^" - + "# TYPE test_histogram histogram\n" - + "test_histogram_bucket{x='1',y='2',le='0'} 0 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='5'} 0 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='10'} 0 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='25'} 1 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='50'} 1 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='75'} 1 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='100'} 2 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='250'} 2 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='500'} 2 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='1000'} 2 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='\\+Inf'} 2 \\d+\n" - + "test_histogram_sum{x='1',y='2'} 118 \\d+\n" - + "test_histogram_count{x='1',y='2'} 2 \\d+\n" - + "$").Replace('\'', '"'), - Encoding.UTF8.GetString(buffer, 0, cursor)); - } - - [Fact] - public void HistogramInfinites() - { - var buffer = new byte[85000]; - var metrics = new List(); - - using var meter = new Meter(Utils.GetCurrentMethodName()); - using var provider = Sdk.CreateMeterProviderBuilder() - .AddMeter(meter.Name) - .AddInMemoryExporter(metrics) - .Build(); - - var histogram = meter.CreateHistogram("test_histogram"); - histogram.Record(18); - histogram.Record(double.PositiveInfinity); - histogram.Record(double.PositiveInfinity); - - provider.ForceFlush(); - - var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); - Assert.Matches( - ("^" - + "# TYPE test_histogram histogram\n" - + "test_histogram_bucket{le='0'} 0 \\d+\n" - + "test_histogram_bucket{le='5'} 0 \\d+\n" - + "test_histogram_bucket{le='10'} 0 \\d+\n" - + "test_histogram_bucket{le='25'} 1 \\d+\n" - + "test_histogram_bucket{le='50'} 1 \\d+\n" - + "test_histogram_bucket{le='75'} 1 \\d+\n" - + "test_histogram_bucket{le='100'} 1 \\d+\n" - + "test_histogram_bucket{le='250'} 1 \\d+\n" - + "test_histogram_bucket{le='500'} 1 \\d+\n" - + "test_histogram_bucket{le='1000'} 1 \\d+\n" - + "test_histogram_bucket{le='\\+Inf'} 3 \\d+\n" - + "test_histogram_sum \\+Inf \\d+\n" - + "test_histogram_count 3 \\d+\n" - + "$").Replace('\'', '"'), - Encoding.UTF8.GetString(buffer, 0, cursor)); - } - - [Fact] - public void HistogramNaN() - { - var buffer = new byte[85000]; - var metrics = new List(); - - using var meter = new Meter(Utils.GetCurrentMethodName()); - using var provider = Sdk.CreateMeterProviderBuilder() - .AddMeter(meter.Name) - .AddInMemoryExporter(metrics) - .Build(); - - var histogram = meter.CreateHistogram("test_histogram"); - histogram.Record(18); - histogram.Record(double.PositiveInfinity); - histogram.Record(double.NaN); - - provider.ForceFlush(); - - var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); - Assert.Matches( - ("^" - + "# TYPE test_histogram histogram\n" - + "test_histogram_bucket{le='0'} 0 \\d+\n" - + "test_histogram_bucket{le='5'} 0 \\d+\n" - + "test_histogram_bucket{le='10'} 0 \\d+\n" - + "test_histogram_bucket{le='25'} 1 \\d+\n" - + "test_histogram_bucket{le='50'} 1 \\d+\n" - + "test_histogram_bucket{le='75'} 1 \\d+\n" - + "test_histogram_bucket{le='100'} 1 \\d+\n" - + "test_histogram_bucket{le='250'} 1 \\d+\n" - + "test_histogram_bucket{le='500'} 1 \\d+\n" - + "test_histogram_bucket{le='1000'} 1 \\d+\n" - + "test_histogram_bucket{le='\\+Inf'} 3 \\d+\n" - + "test_histogram_sum Nan \\d+\n" - + "test_histogram_count 3 \\d+\n" - + "$").Replace('\'', '"'), - Encoding.UTF8.GetString(buffer, 0, cursor)); - } - } -} diff --git a/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs b/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs index 6a56c5a4bcb..91d5e64a26a 100644 --- a/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs +++ b/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs @@ -47,8 +47,7 @@ public static void Main() using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(TestMeter.Name) .AddPrometheusHttpListener( - exporterOptions => exporterOptions.ScrapeResponseCacheDurationMilliseconds = 0, - listenerOptions => listenerOptions.Prefixes = new string[] { $"http://localhost:9185/" }) + options => options.Prefixes = new string[] { $"http://localhost:9185/" }) .Build(); Stress(prometheusPort: 9184); diff --git a/test/OpenTelemetry.Tests.Stress/Skeleton.cs b/test/OpenTelemetry.Tests.Stress/Skeleton.cs index e372b108710..967bcd53a49 100644 --- a/test/OpenTelemetry.Tests.Stress/Skeleton.cs +++ b/test/OpenTelemetry.Tests.Stress/Skeleton.cs @@ -76,8 +76,7 @@ public static void Stress(int concurrency = 0, int prometheusPort = 0) .AddMeter(meter.Name) .AddRuntimeInstrumentation() .AddPrometheusHttpListener( - exporterOptions => exporterOptions.ScrapeResponseCacheDurationMilliseconds = 0, - listenerOptions => listenerOptions.Prefixes = new string[] { $"http://localhost:{prometheusPort}/" }) + options => options.Prefixes = new string[] { $"http://localhost:{prometheusPort}/" }) .Build() : null; var statistics = new long[concurrency]; From 365828c4e2a1fa03a34b92da031e096af1939db3 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Mon, 1 Aug 2022 11:09:15 -0700 Subject: [PATCH 45/58] Nit fixed in prometheus for asp.net core (#3510) --- .../OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj index f8f55cb18e3..27c0377af01 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj @@ -14,10 +14,6 @@ false - - $(DefineConstants);PROMETHEUS_ASPNETCORE - - @@ -32,7 +28,7 @@ - + From 61ba0ed7102bd813b7a1278f9f0a6378dd76036a Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Mon, 1 Aug 2022 11:40:12 -0700 Subject: [PATCH 46/58] Nit fixes to prometheus asp.net core (#3511) --- .../PrometheusExporterApplicationBuilderExtensions.cs | 3 --- .../PrometheusExporterEndpointRouteBuilderExtensions.cs | 3 --- .../PrometheusExporterMiddleware.cs | 2 -- 3 files changed, 8 deletions(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs index 491e70eac02..100b4d69952 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs @@ -14,8 +14,6 @@ // limitations under the License. // -#if NETCOREAPP3_1_OR_GREATER - using System; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; @@ -150,4 +148,3 @@ public static IApplicationBuilder UseOpenTelemetryPrometheusScrapingEndpoint( } } } -#endif diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs index 5d260878ffa..e24e829aa63 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs @@ -14,8 +14,6 @@ // limitations under the License. // -#if NETCOREAPP3_1_OR_GREATER - using System; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; @@ -108,4 +106,3 @@ public static IEndpointConventionBuilder MapPrometheusScrapingEndpoint( } } } -#endif diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs index 826ca135763..2b2368a60af 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs @@ -14,7 +14,6 @@ // limitations under the License. // -#if NETCOREAPP3_1_OR_GREATER using System; using System.Diagnostics; using System.Threading.Tasks; @@ -102,4 +101,3 @@ public async Task InvokeAsync(HttpContext httpContext) } } } -#endif From 493ab1a9d672a0f3f4cfe03517ad110637ae1bbe Mon Sep 17 00:00:00 2001 From: Yun-Ting Lin Date: Mon, 1 Aug 2022 17:07:42 -0700 Subject: [PATCH 47/58] [Prometheus] Update changelog and minor cleanup. (#3512) --- .../CHANGELOG.md | 15 ++++++++------- .../CHANGELOG.md | 15 ++++++++------- .../PrometheusHttpListener.cs | 2 +- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md index f2df4b1a83b..ca3dd407279 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md @@ -2,11 +2,12 @@ ## Unreleased -* Split up Prometheus projects based on its hosting mechanism, HttpListener and AspNetCore, - into their own projects and assemblies. The shared code for both hosting mechanism - now lives in the `OpenTelemetry.Exporter.Prometheus.Shared` project and will not - be released. - ([#3430](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3430)) +* Split up Prometheus projects based on its hosting mechanism, + HttpListener and AspNetCore, into their own projects + and assemblies. + ([#3430](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3430) + [#3503](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3503) + [#3507](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3507)) * Added `IEndpointRouteBuilder` extension methods to help with Prometheus middleware configuration on ASP.NET Core @@ -81,8 +82,8 @@ Released 2021-Sep-23 Released 2021-Sep-13 * Bug fixes - ([#2289](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2289)) - ([#2309](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2309)) + ([#2289](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2289) + [#2309](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2309)) ## 1.2.0-alpha2 diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md index 9f4ac19cfa2..324932a4262 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -2,11 +2,12 @@ ## Unreleased -* Split up Prometheus projects based on its hosting mechanism, HttpListener and AspNetCore, - into their own projects and assemblies. The shared code for both hosting mechanism - now lives in the `OpenTelemetry.Exporter.Prometheus.Shared` project and will not - be released. - ([#3430](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3430)) +* Split up Prometheus projects based on its hosting mechanism, + HttpListener and AspNetCore, into their own projects + and assemblies. + ([#3430](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3430) + [#3503](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3503) + [#3507](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3507)) ## 1.3.0-rc.2 @@ -77,8 +78,8 @@ Released 2021-Sep-23 Released 2021-Sep-13 * Bug fixes - ([#2289](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2289)) - ([#2309](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2309)) + ([#2289](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2289) + [#2309](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2309)) ## 1.2.0-alpha2 diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs index efd022d79b1..ce3a5a48ed8 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs @@ -25,7 +25,7 @@ namespace OpenTelemetry.Exporter.Prometheus internal sealed class PrometheusHttpListener : IDisposable { private readonly PrometheusExporter exporter; - private readonly System.Net.HttpListener httpListener = new(); + private readonly HttpListener httpListener = new(); private readonly object syncObject = new(); private CancellationTokenSource tokenSource; From 9b0cc13eae1e9cb9350e1b45994725d62bf1f9ea Mon Sep 17 00:00:00 2001 From: Yun-Ting Lin Date: Mon, 1 Aug 2022 17:37:25 -0700 Subject: [PATCH 48/58] Aligning formats for CHANGLOG.md files. (#3513) --- src/OpenTelemetry.Api/CHANGELOG.md | 4 ++-- src/OpenTelemetry.Exporter.Console/CHANGELOG.md | 2 +- src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md | 10 +++++----- .../CHANGELOG.md | 6 +++--- src/OpenTelemetry.Exporter.ZPages/CHANGELOG.md | 4 ++-- src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md | 14 +++++++------- src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md | 3 ++- .../CHANGELOG.md | 2 +- .../CHANGELOG.md | 2 +- src/OpenTelemetry/CHANGELOG.md | 2 +- 10 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/OpenTelemetry.Api/CHANGELOG.md b/src/OpenTelemetry.Api/CHANGELOG.md index e322f4f5c41..ab6a21171f2 100644 --- a/src/OpenTelemetry.Api/CHANGELOG.md +++ b/src/OpenTelemetry.Api/CHANGELOG.md @@ -163,7 +163,7 @@ Released 2021-Jan-29 the `Status` (otel.status_code) tag (added on `Activity` using the `SetStatus` extension) will now be set as the `UNSET`, `OK`, or `ERROR` string representation instead of the `0`, `1`, or `2` integer representation. - ([#1579](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1579) & + ([#1579](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1579) [#1620](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1620)) * Metrics API/SDK support is in an experimental state and is not recommended for production use. All metric APIs have been marked with the `Obsolete` @@ -266,7 +266,7 @@ Released 2020-08-28 header ([#1048](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1048)) * Removed `DistributedContext` as it is no longer part of the spec - ([#1048](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1048))) + ([#1048](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1048)) * Renaming from `ot` to `otel` ([#1046](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1046)) * Added `RuntimeContext` API diff --git a/src/OpenTelemetry.Exporter.Console/CHANGELOG.md b/src/OpenTelemetry.Exporter.Console/CHANGELOG.md index 781d1454627..c81c727d695 100644 --- a/src/OpenTelemetry.Exporter.Console/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Console/CHANGELOG.md @@ -57,7 +57,7 @@ Released 2022-Mar-30 * Added StatusCode, StatusDescription support to `ConsoleActivityExporter`. ([#2929](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2929) - [#3061](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3061)) + [#3061](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3061)) * `AddConsoleExporter` extension method by default sets up exporter to export metrics every 10 seconds. diff --git a/src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md b/src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md index 78402461db4..dc7985b864f 100644 --- a/src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md @@ -209,11 +209,11 @@ Released 2021-Jan-29 Simple exporter, and settings for batch exporting properties. * Jaeger will now set the `error` tag when `otel.status_code` is set to `ERROR`. - ([#1579](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1579) & + ([#1579](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1579) [#1620](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1620)) * Jaeger will no longer send the `otel.status_code` tag if the value is `UNSET`. - ([#1609](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1609) & + ([#1609](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1609) [#1620](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1620)) * Span Event.Name will now be populated as the `event` field on Jaeger Logs @@ -265,14 +265,14 @@ Released 2020-Sep-15 Released 2020-08-28 -* Changed `JaegerExporter` to use `BatchExportActivityProcessor` by default +* Changed `JaegerExporter` to use `BatchExportActivityProcessor` by default. ([#1125](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1125)) * Span links will now be sent as `FOLLOWS_FROM` reference type. Previously they were sent as `CHILD_OF`. ([#970](https://github.com/open-telemetry/opentelemetry-dotnet/pull/970)) * Fixed issue when span has both the `net.peer.name` and `net.peer.port` - attributes but did not include `net.peer.port` in the `peer.service` field - ([#1195](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1195)). + attributes but did not include `net.peer.port` in the `peer.service` field. + ([#1195](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1195)) * Renamed extension method from `UseJaegerExporter` to `AddJaegerExporter`. diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md index 1a4ae40ade9..b04c352c41e 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md @@ -31,7 +31,7 @@ Released 2022-May-16 * Support `HttpProtobuf` protocol with logs & added `HttpClientFactory` option -([#3225](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3225)) + ([#3225](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3225)) * Removes net5.0 target and replaced with net6.0 as .NET 5.0 is going out of support. @@ -254,8 +254,8 @@ Released 2021-Apr-23 * Null values in string arrays are preserved according to [spec](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/common.md). - ([#1919](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1919)) and - ([#1945](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1945)). + ([#1919](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1919) + [#1945](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1945)) * When using OpenTelemetry.Extensions.Hosting you can now bind `OtlpExporterOptions` to `IConfiguration` using the `Configure` extension (ex: diff --git a/src/OpenTelemetry.Exporter.ZPages/CHANGELOG.md b/src/OpenTelemetry.Exporter.ZPages/CHANGELOG.md index cd272cb8e85..c9330f85220 100644 --- a/src/OpenTelemetry.Exporter.ZPages/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.ZPages/CHANGELOG.md @@ -76,9 +76,9 @@ Released 2020-Sep-15 Released 2020-08-28 -* Renamed extension method from `UseZPagesExporter` to `AddZPagesExporter` +* Renamed extension method from `UseZPagesExporter` to `AddZPagesExporter`. ([#1066](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1066)) -* Changed `ZPagesExporter` to use `ZPagesProcessor` by default +* Changed `ZPagesExporter` to use `ZPagesProcessor` by default. ([#1108](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1108)) ## 0.4.0-beta.2 diff --git a/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md b/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md index d62d7bc4efd..4196962db1f 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md @@ -192,12 +192,12 @@ Released 2021-Jan-29 * Zipkin will now set the `error` tag to the `Status.Description` value or an empty string when `Status.StatusCode` (`otel.status_code` tag) is set to `ERROR`. - ([#1579](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1579), - [#1620](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1620), & + ([#1579](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1579) + [#1620](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1620) [#1655](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1655)) * Zipkin will no longer send the `otel.status_code` tag if the value is `UNSET`. - ([#1609](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1609) & + ([#1609](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1609) [#1620](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1620)) * Zipkin bool tag values will now be sent as `true`/`false` instead of @@ -249,13 +249,13 @@ Released 2020-Sep-15 Released 2020-08-28 -* Renamed extension method from `UseZipkinExporter` to `AddZipkinExporter` +* Renamed extension method from `UseZipkinExporter` to `AddZipkinExporter`. ([#1066](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1066)) -* Changed `ZipkinExporter` to use `BatchExportActivityProcessor` by default +* Changed `ZipkinExporter` to use `BatchExportActivityProcessor` by default. ([#1103](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1103)) * Fixed issue when span has both the `net.peer.name` and `net.peer.port` - attributes but did not include `net.peer.port` in the service address field - ([#1168](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1168)). + attributes but did not include `net.peer.port` in the service address field. + ([#1168](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1168)) ## 0.4.0-beta.2 diff --git a/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md b/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md index 799952828e8..93a35a7677d 100644 --- a/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md +++ b/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md @@ -37,7 +37,8 @@ Released 2022-Feb-02 Released 2021-Oct-08 * Removes upper constraint for Microsoft.Extensions.Hosting.Abstractions - dependency. ([#2179](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2179)) + dependency. + ([#2179](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2179)) * Added `AddOpenTelemetryMetrics` extensions on `IServiceCollection` to register OpenTelemetry `MeterProvider` with application services. Added diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index d456038b9b8..7e48cbf5487 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -153,7 +153,7 @@ Released 2020-Sep-15 added by the library are removed from the span. The information from these attributes is contained in other attributes that follow the conventions of OpenTelemetry. - ([#1260](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1260)). + ([#1260](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1260)) ## 0.5.0-beta.2 diff --git a/src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md index 8b25cf20d74..2ca87b172f7 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md @@ -98,7 +98,7 @@ Released 2020-Sep-15 * The `grpc.method` and `grpc.status_code` attributes added by the library are removed from the span. The information from these attributes is contained in other attributes that follow the conventions of OpenTelemetry. - ([#1260](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1260)). + ([#1260](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1260)) ## 0.5.0-beta.2 diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 99114364c52..b62f0338902 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -365,7 +365,7 @@ Released 2021-Mar-19 * Added `IncludeScopes`, `IncludeFormattedMessage`, & `ParseStateValues` on `OpenTelemetryLoggerOptions`. Added `FormattedMessage`, `StateValues`, & `ForEachScope` on `LogRecord`. - ([#1869](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1869) & + ([#1869](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1869) [#1883](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1883)) * Added `SetResourceBuilder` support to `OpenTelemetryLoggerOptions`. ([#1913](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1913)) From fec60fe9ab28e1647a207bd4cebacf907e4177f9 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Tue, 2 Aug 2022 11:57:19 -0700 Subject: [PATCH 49/58] ConsoleLogExporter special casing original format (#3516) --- src/OpenTelemetry.Exporter.Console/ConsoleLogRecordExporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.Console/ConsoleLogRecordExporter.cs b/src/OpenTelemetry.Exporter.Console/ConsoleLogRecordExporter.cs index c35cf543222..d538c59fb16 100644 --- a/src/OpenTelemetry.Exporter.Console/ConsoleLogRecordExporter.cs +++ b/src/OpenTelemetry.Exporter.Console/ConsoleLogRecordExporter.cs @@ -66,7 +66,7 @@ public override ExportResult Export(in Batch batch) // Special casing {OriginalFormat} // See https://github.com/open-telemetry/opentelemetry-dotnet/pull/3182 // for explanation. - var valueToTransform = logRecord.StateValues[i].Key.Equals("{OriginalValue}") + var valueToTransform = logRecord.StateValues[i].Key.Equals("{OriginalFormat}") ? new KeyValuePair("OriginalFormat (a.k.a Body)", logRecord.StateValues[i].Value) : logRecord.StateValues[i]; From 0244fa024bc7897c35c748344de61ea92e43848f Mon Sep 17 00:00:00 2001 From: Timothy Mothra Date: Tue, 2 Aug 2022 13:10:11 -0700 Subject: [PATCH 50/58] refactor test exporters, use instead `DelegatingTestExporter` (#3486) --- .../IntegrationTests.cs | 53 +++++++++++++------ ...xporter.OpenTelemetryProtocol.Tests.csproj | 2 +- .../Shared/DelegatingExporter.cs | 27 ++++++++++ .../Shared/DelegatingTestExporter.cs | 46 ---------------- 4 files changed, 65 insertions(+), 63 deletions(-) create mode 100644 test/OpenTelemetry.Tests/Shared/DelegatingExporter.cs delete mode 100644 test/OpenTelemetry.Tests/Shared/DelegatingTestExporter.cs diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/IntegrationTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/IntegrationTests.cs index 91466b01299..b8206f03936 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/IntegrationTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/IntegrationTests.cs @@ -15,6 +15,7 @@ // using System; +using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Metrics; using System.Diagnostics.Tracing; @@ -79,7 +80,8 @@ public void TraceExportResultIsSuccess(OtlpExportProtocol protocol, string endpo }, }; - DelegatingTestExporter delegatingExporter = null; + DelegatingExporter delegatingExporter = null; + var exportResults = new List(); var activitySourceName = "otlp.collector.test"; @@ -93,7 +95,16 @@ public void TraceExportResultIsSuccess(OtlpExportProtocol protocol, string endpo serviceProvider: null, configureExporterInstance: otlpExporter => { - delegatingExporter = new DelegatingTestExporter(otlpExporter, onExportAction: () => handle.Set()); + delegatingExporter = new DelegatingExporter + { + OnExportFunc = (batch) => + { + var result = otlpExporter.Export(batch); + exportResults.Add(result); + handle.Set(); + return result; + }, + }; return delegatingExporter; }); @@ -108,21 +119,21 @@ public void TraceExportResultIsSuccess(OtlpExportProtocol protocol, string endpo if (forceFlush) { Assert.True(tracerProvider.ForceFlush()); - Assert.Single(delegatingExporter.ExportResults); - Assert.Equal(ExportResult.Success, delegatingExporter.ExportResults[0]); + Assert.Single(exportResults); + Assert.Equal(ExportResult.Success, exportResults[0]); } else if (exporterOptions.ExportProcessorType == ExportProcessorType.Batch) { Assert.True(handle.WaitOne(ExportIntervalMilliseconds * 2)); - Assert.Single(delegatingExporter.ExportResults); - Assert.Equal(ExportResult.Success, delegatingExporter.ExportResults[0]); + Assert.Single(exportResults); + Assert.Equal(ExportResult.Success, exportResults[0]); } } if (!forceFlush && exportProcessorType == ExportProcessorType.Simple) { - Assert.Single(delegatingExporter.ExportResults); - Assert.Equal(ExportResult.Success, delegatingExporter.ExportResults[0]); + Assert.Single(exportResults); + Assert.Equal(ExportResult.Success, exportResults[0]); } } @@ -155,7 +166,8 @@ public void MetricExportResultIsSuccess(OtlpExportProtocol protocol, string endp Protocol = protocol, }; - DelegatingTestExporter delegatingExporter = null; + DelegatingExporter delegatingExporter = null; + var exportResults = new List(); var meterName = "otlp.collector.test"; @@ -174,7 +186,16 @@ public void MetricExportResultIsSuccess(OtlpExportProtocol protocol, string endp serviceProvider: null, configureExporterInstance: otlpExporter => { - delegatingExporter = new DelegatingTestExporter(otlpExporter, onExportAction: () => handle.Set()); + delegatingExporter = new DelegatingExporter + { + OnExportFunc = (batch) => + { + var result = otlpExporter.Export(batch); + exportResults.Add(result); + handle.Set(); + return result; + }, + }; return delegatingExporter; }); @@ -191,21 +212,21 @@ public void MetricExportResultIsSuccess(OtlpExportProtocol protocol, string endp if (forceFlush) { Assert.True(meterProvider.ForceFlush()); - Assert.Single(delegatingExporter.ExportResults); - Assert.Equal(ExportResult.Success, delegatingExporter.ExportResults[0]); + Assert.Single(exportResults); + Assert.Equal(ExportResult.Success, exportResults[0]); } else if (!useManualExport) { Assert.True(handle.WaitOne(ExportIntervalMilliseconds * 2)); - Assert.Single(delegatingExporter.ExportResults); - Assert.Equal(ExportResult.Success, delegatingExporter.ExportResults[0]); + Assert.Single(exportResults); + Assert.Equal(ExportResult.Success, exportResults[0]); } } if (!forceFlush && useManualExport) { - Assert.Single(delegatingExporter.ExportResults); - Assert.Equal(ExportResult.Success, delegatingExporter.ExportResults[0]); + Assert.Single(exportResults); + Assert.Equal(ExportResult.Success, exportResults[0]); } } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj index 2ddffb5be2a..271f7649e84 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj @@ -27,7 +27,7 @@ - + diff --git a/test/OpenTelemetry.Tests/Shared/DelegatingExporter.cs b/test/OpenTelemetry.Tests/Shared/DelegatingExporter.cs new file mode 100644 index 00000000000..13cef6924d9 --- /dev/null +++ b/test/OpenTelemetry.Tests/Shared/DelegatingExporter.cs @@ -0,0 +1,27 @@ +// +// 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. +// + +using System; + +namespace OpenTelemetry.Tests; + +internal sealed class DelegatingExporter : BaseExporter + where T : class +{ + public Func, ExportResult> OnExportFunc { get; set; } = (batch) => default; + + public override ExportResult Export(in Batch batch) => this.OnExportFunc(batch); +} diff --git a/test/OpenTelemetry.Tests/Shared/DelegatingTestExporter.cs b/test/OpenTelemetry.Tests/Shared/DelegatingTestExporter.cs deleted file mode 100644 index 10800369105..00000000000 --- a/test/OpenTelemetry.Tests/Shared/DelegatingTestExporter.cs +++ /dev/null @@ -1,46 +0,0 @@ -// -// 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. -// - -using System; -using System.Collections.Generic; - -namespace OpenTelemetry.Tests -{ - public class DelegatingTestExporter : BaseExporter - where T : class - { - private readonly BaseExporter exporter; - private readonly Action onExportAction; - - public DelegatingTestExporter( - BaseExporter exporter, - Action onExportAction = null) - { - this.exporter = exporter; - this.onExportAction = onExportAction; - } - - public List ExportResults { get; } = new(); - - public override ExportResult Export(in Batch batch) - { - var result = this.exporter.Export(batch); - this.ExportResults.Add(result); - this.onExportAction?.Invoke(); - return result; - } - } -} From 59ed0334f8e2bb440ddbda192147c8db184b6595 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Tue, 2 Aug 2022 15:39:41 -0700 Subject: [PATCH 51/58] Improve PrometheusHttpListener options (#3521) --- .../README.md | 6 +- examples/Console/TestPrometheusExporter.cs | 2 +- .../.publicApi/net462/PublicAPI.Unshipped.txt | 4 +- .../netstandard2.0/PublicAPI.Unshipped.txt | 4 +- .../CHANGELOG.md | 4 ++ .../PrometheusHttpListener.cs | 12 ++-- .../PrometheusHttpListenerOptions.cs | 20 +++---- .../README.md | 22 +++---- .../PrometheusHttpListenerTests.cs | 58 +++++++++++-------- .../Program.cs | 2 +- test/OpenTelemetry.Tests.Stress/Skeleton.cs | 2 +- 11 files changed, 68 insertions(+), 68 deletions(-) diff --git a/docs/metrics/getting-started-prometheus-grafana/README.md b/docs/metrics/getting-started-prometheus-grafana/README.md index 62953f4f130..1e2f2e35e99 100644 --- a/docs/metrics/getting-started-prometheus-grafana/README.md +++ b/docs/metrics/getting-started-prometheus-grafana/README.md @@ -49,10 +49,10 @@ with .AddPrometheusHttpListener() ``` -`PrometheusHttpListener` is a wrapper that contains `PrometheusExporter`. -With `AddPrometheusHttpListener()`, OpenTelemetry `PrometheusExporter` will export +`PrometheusHttpListener` is a wrapper that contains `PrometheusExporter`. With +`AddPrometheusHttpListener()`, OpenTelemetry `PrometheusExporter` will export data via the endpoint defined by -[PrometheusHttpListenerOptions.Prefixes](../../../src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md#prefixes), +[PrometheusHttpListenerOptions.UriPrefixes](../../../src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md#uriprefixes), which is `http://localhost:9464/` by default. ```mermaid diff --git a/examples/Console/TestPrometheusExporter.cs b/examples/Console/TestPrometheusExporter.cs index 0b1284c4ae0..befb0e5d4e9 100644 --- a/examples/Console/TestPrometheusExporter.cs +++ b/examples/Console/TestPrometheusExporter.cs @@ -52,7 +52,7 @@ internal static object Run(int port) .AddMeter(MyMeter.Name) .AddMeter(MyMeter2.Name) .AddPrometheusHttpListener( - options => options.Prefixes = new string[] { $"http://localhost:{port}/" }) + options => options.UriPrefixes = new string[] { $"http://localhost:{port}/" }) .Build(); var process = Process.GetCurrentProcess(); diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt index 7b61e63513e..e05aa673ed4 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt @@ -1,6 +1,6 @@ OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions -OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.Prefixes.get -> System.Collections.Generic.IReadOnlyCollection -OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.Prefixes.set -> void +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.UriPrefixes.get -> System.Collections.Generic.IReadOnlyCollection +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.UriPrefixes.set -> void OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.PrometheusHttpListenerOptions() -> void OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.ScrapeEndpointPath.get -> string OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.ScrapeEndpointPath.set -> void diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index 7b61e63513e..e05aa673ed4 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,6 +1,6 @@ OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions -OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.Prefixes.get -> System.Collections.Generic.IReadOnlyCollection -OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.Prefixes.set -> void +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.UriPrefixes.get -> System.Collections.Generic.IReadOnlyCollection +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.UriPrefixes.set -> void OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.PrometheusHttpListenerOptions() -> void OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.ScrapeEndpointPath.get -> string OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.ScrapeEndpointPath.set -> void diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md index 324932a4262..9d516152808 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -8,6 +8,10 @@ ([#3430](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3430) [#3503](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3503) [#3507](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3507)) +* Fixed bug + [#2840](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2840) by + allowing `+` and `*` to be used in the URI prefixes (e.g. `"http://*:9184"`). + ([#3521](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3521)) ## 1.3.0-rc.2 diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs index ce3a5a48ed8..3bd122a7732 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs @@ -39,14 +39,12 @@ internal sealed class PrometheusHttpListener : IDisposable public PrometheusHttpListener(PrometheusExporter exporter, PrometheusHttpListenerOptions options) { Guard.ThrowIfNull(exporter); - - if ((options.Prefixes?.Count ?? 0) <= 0) - { - throw new ArgumentException("No Prefixes were specified on PrometheusHttpListenerOptions."); - } + Guard.ThrowIfNull(options); this.exporter = exporter; + string path = this.exporter.ScrapeEndpointPath; + if (!path.StartsWith("/")) { path = $"/{path}"; @@ -57,9 +55,9 @@ public PrometheusHttpListener(PrometheusExporter exporter, PrometheusHttpListene path = $"{path}/"; } - foreach (string prefix in options.Prefixes) + foreach (string uriPrefix in options.UriPrefixes) { - this.httpListener.Prefixes.Add($"{prefix.TrimEnd('/')}{path}"); + this.httpListener.Prefixes.Add($"{uriPrefix.TrimEnd('/')}{path}"); } } diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs index dc1e8e5d9ea..68f88c16424 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs @@ -25,7 +25,7 @@ namespace OpenTelemetry.Exporter.Prometheus /// public class PrometheusHttpListenerOptions { - private IReadOnlyCollection prefixes = new string[] { "http://localhost:9464/" }; + private IReadOnlyCollection uriPrefixes = new string[] { "http://localhost:9464/" }; /// /// Gets or sets the path to use for the scraping endpoint. Default value: "/metrics". @@ -33,28 +33,22 @@ public class PrometheusHttpListenerOptions public string ScrapeEndpointPath { get; set; } = "/metrics"; /// - /// Gets or sets the prefixes to use for the http listener. + /// Gets or sets the URI (Uniform Resource Identifier) prefixes to use for the http listener. /// Default value: ["http://localhost:9464/"]. /// - public IReadOnlyCollection Prefixes + public IReadOnlyCollection UriPrefixes { - get => this.prefixes; + get => this.uriPrefixes; set { Guard.ThrowIfNull(value); - foreach (string inputUri in value) + if (value.Count == 0) { - if (!(Uri.TryCreate(inputUri, UriKind.Absolute, out var uri) && - (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps))) - { - throw new ArgumentException( - "Prometheus HttpListener prefix path should be a valid URI with http/https scheme.", - nameof(this.prefixes)); - } + throw new ArgumentException("Empty list provided.", nameof(this.UriPrefixes)); } - this.prefixes = value; + this.uriPrefixes = value; } } } diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md index b997b5adecc..b97cb0bd258 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md @@ -15,38 +15,32 @@ instance for Prometheus to scrape. ### Step 1: Install Package -Install - ```shell dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener ``` ### Step 2: Add PrometheusHttpListener -Add and configure `PrometheusHttpListener` with `PrometheusHttpListenerOptions`. - -For example: - ```csharp -using var meterProvider = Sdk.CreateMeterProviderBuilder() +var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(MyMeter.Name) .AddPrometheusHttpListener( - options => options.Prefixes = new string[] { "http://localhost:9464/" }) + options => options.UriPrefixes = new string[] { "http://localhost:9464/" }) .Build(); ``` -### Prefixes +### UriPrefixes -Defines the prefixes which will be used by the listener. The default value is `["http://localhost:9464/"]`. -You may specify multiple endpoints. +Defines one or more URI (Uniform Resource Identifier) prefixes which will be +used by the HTTP listener. The default value is `["http://localhost:9464/"]`. -For details see: +Refer to [HttpListenerPrefixCollection.Add(String)](https://docs.microsoft.com/dotnet/api/system.net.httplistenerprefixcollection.add) +for more details. ### ScrapeEndpointPath -Defines the path for the Prometheus scrape endpoint for by -`UseOpenTelemetryPrometheusScrapingEndpoint`. Default value: `"/metrics"`. +Defines the Prometheus scrape endpoint path. Default value: `"/metrics"`. ## Troubleshooting diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs index bfaa2f06f56..fb281a3563f 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs @@ -31,42 +31,52 @@ public class PrometheusHttpListenerTests private readonly string meterName = Utils.GetCurrentMethodName(); [Theory] - [InlineData("http://example.com")] + [InlineData("http://+:9184")] + [InlineData("http://*:9184")] + [InlineData("http://+:9184/")] + [InlineData("http://*:9184/")] [InlineData("https://example.com")] [InlineData("http://127.0.0.1")] [InlineData("http://example.com", "https://example.com", "http://127.0.0.1")] - public void ServerEndpointSanityCheckPositiveTest(params string[] uris) + [InlineData("http://example.com")] + public void UriPrefixesPositiveTest(params string[] uriPrefixes) { using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() - .AddPrometheusHttpListener(options => options.Prefixes = uris) + .AddPrometheusHttpListener(options => options.UriPrefixes = uriPrefixes) .Build(); } - [Theory] - [InlineData("")] - [InlineData(null)] - [InlineData("ftp://example.com")] - [InlineData("http://example.com", "https://example.com", "ftp://example.com")] - public void ServerEndpointSanityCheckNegativeTest(params string[] uris) + [Fact] + public void UriPrefixesNull() { - try + Assert.Throws(() => { using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() - .AddPrometheusHttpListener(options => options.Prefixes = uris) + .AddPrometheusHttpListener(options => options.UriPrefixes = null) .Build(); - } - catch (Exception ex) + }); + } + + [Fact] + public void UriPrefixesEmptyList() + { + Assert.Throws(() => { - if (ex is not ArgumentNullException) - { - Assert.Equal("System.ArgumentException", ex.GetType().ToString()); -#if NETFRAMEWORK - Assert.Equal("Prometheus HttpListener prefix path should be a valid URI with http/https scheme.\r\nParameter name: prefixes", ex.Message); -#else - Assert.Equal("Prometheus HttpListener prefix path should be a valid URI with http/https scheme. (Parameter 'prefixes')", ex.Message); -#endif - } - } + using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() + .AddPrometheusHttpListener(options => options.UriPrefixes = new string[] { }) + .Build(); + }); + } + + [Fact] + public void UriPrefixesInvalid() + { + Assert.Throws(() => + { + using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() + .AddPrometheusHttpListener(options => options.UriPrefixes = new string[] { "ftp://example.com" }) + .Build(); + }); } [Fact] @@ -98,7 +108,7 @@ private async Task RunPrometheusExporterHttpServerIntegrationTest(bool skipMetri provider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) - .AddPrometheusHttpListener(options => options.Prefixes = new string[] { address }) + .AddPrometheusHttpListener(options => options.UriPrefixes = new string[] { address }) .Build(); } diff --git a/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs b/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs index 91d5e64a26a..da3215aee79 100644 --- a/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs +++ b/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs @@ -47,7 +47,7 @@ public static void Main() using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(TestMeter.Name) .AddPrometheusHttpListener( - options => options.Prefixes = new string[] { $"http://localhost:9185/" }) + options => options.UriPrefixes = new string[] { $"http://localhost:9185/" }) .Build(); Stress(prometheusPort: 9184); diff --git a/test/OpenTelemetry.Tests.Stress/Skeleton.cs b/test/OpenTelemetry.Tests.Stress/Skeleton.cs index 967bcd53a49..c69e2543a1a 100644 --- a/test/OpenTelemetry.Tests.Stress/Skeleton.cs +++ b/test/OpenTelemetry.Tests.Stress/Skeleton.cs @@ -76,7 +76,7 @@ public static void Stress(int concurrency = 0, int prometheusPort = 0) .AddMeter(meter.Name) .AddRuntimeInstrumentation() .AddPrometheusHttpListener( - options => options.Prefixes = new string[] { $"http://localhost:{prometheusPort}/" }) + options => options.UriPrefixes = new string[] { $"http://localhost:{prometheusPort}/" }) .Build() : null; var statistics = new long[concurrency]; From 08463091ca859202d4d1c3aeb154c90bb533c322 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Tue, 2 Aug 2022 17:10:00 -0700 Subject: [PATCH 52/58] Minor improvements to Prometheus Exporter (ASP.NET Core) (#3522) --- .../PrometheusExporterOptions.cs | 4 ++-- .../README.md | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterOptions.cs index bf7576117b1..248b991f4c5 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterOptions.cs @@ -25,7 +25,7 @@ public class PrometheusExporterOptions { internal const string DefaultScrapeEndpointPath = "/metrics"; - private int scrapeResponseCacheDurationMilliseconds = 10 * 1000; + private int scrapeResponseCacheDurationMilliseconds = 300; /// /// Gets or sets the path to use for the scraping endpoint. Default value: "/metrics". @@ -33,7 +33,7 @@ public class PrometheusExporterOptions public string ScrapeEndpointPath { get; set; } = DefaultScrapeEndpointPath; /// - /// Gets or sets the cache duration in milliseconds for scrape responses. Default value: 10,000 (10 seconds). + /// Gets or sets the cache duration in milliseconds for scrape responses. Default value: 300. /// /// /// Note: Specify 0 to disable response caching. diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/README.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/README.md index 855c20637a6..d9cb8bfdc1a 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/README.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/README.md @@ -15,15 +15,15 @@ to scrape. ### Step 1: Install Package -Install - ```shell dotnet add package OpenTelemetry.Exporter.Prometheus.AspNetCore ``` ### Step 2: Configure OpenTelemetry MeterProvider -* When using OpenTelemetry.Extensions.Hosting package on .NET Core 3.1+: +* When using + [OpenTelemetry.Extensions.Hosting](../OpenTelemetry.Extensions.Hosting/README.md) + package on .NET Core 3.1+: ```csharp services.AddOpenTelemetryMetrics(builder => @@ -34,7 +34,7 @@ dotnet add package OpenTelemetry.Exporter.Prometheus.AspNetCore * Or configure directly: - Call the `AddPrometheusExporter` `MeterProviderBuilder` extension to + Call the `MeterProviderBuilder.AddPrometheusExporter` extension to register the Prometheus exporter. ```csharp @@ -94,8 +94,7 @@ registered by Configures scrape endpoint response caching. Multiple scrape requests within the cache duration time period will receive the same previously generated response. -The default value is `10000` (10 seconds). Set to `0` to disable response -caching. +The default value is `300`. Set to `0` to disable response caching. ## Troubleshooting From 436211580f0f11175781557ae7a4909ef02910f2 Mon Sep 17 00:00:00 2001 From: Paulo Janotti Date: Tue, 2 Aug 2022 17:38:10 -0700 Subject: [PATCH 53/58] Fix OpenTracing shim under legacy AspNetCore activities (#3506) --- .../CHANGELOG.md | 4 +++ .../SpanBuilderShim.cs | 19 +------------ .../SpanBuilderShimTests.cs | 28 +++++++++++++++++-- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md b/src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md index 30a4b655820..aff4eaa3b30 100644 --- a/src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md +++ b/src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +* Fix: Handling of OpenTracing spans when used in conjunction + with legacy "Microsoft.AspNetCore.Hosting.HttpRequestIn" activities. + ([#3509](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3506)) + ## 1.0.0-rc9.4 Released 2022-Jun-03 diff --git a/src/OpenTelemetry.Shims.OpenTracing/SpanBuilderShim.cs b/src/OpenTelemetry.Shims.OpenTracing/SpanBuilderShim.cs index 483549e8f97..5cdbe55e85d 100644 --- a/src/OpenTelemetry.Shims.OpenTracing/SpanBuilderShim.cs +++ b/src/OpenTelemetry.Shims.OpenTracing/SpanBuilderShim.cs @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using OpenTelemetry.Internal; using OpenTelemetry.Trace; using OpenTracing; @@ -50,14 +49,6 @@ internal sealed class SpanBuilderShim : ISpanBuilder /// private readonly List> attributes = new(); - /// - /// The set of operation names for System.Diagnostics.Activity based automatic instrumentations that indicate a root span. - /// - private readonly IList rootOperationNamesForActivityBasedAutoInstrumentations = new List - { - "Microsoft.AspNetCore.Hosting.HttpRequestIn", - }; - /// /// The parent as an TelemetrySpan, if any. /// @@ -79,7 +70,7 @@ internal sealed class SpanBuilderShim : ISpanBuilder private bool error; - public SpanBuilderShim(Tracer tracer, string spanName, IList rootOperationNamesForActivityBasedAutoInstrumentations = null) + public SpanBuilderShim(Tracer tracer, string spanName) { Guard.ThrowIfNull(tracer); Guard.ThrowIfNull(spanName); @@ -87,7 +78,6 @@ public SpanBuilderShim(Tracer tracer, string spanName, IList rootOperati this.tracer = tracer; this.spanName = spanName; this.ScopeManager = new ScopeManagerShim(this.tracer); - this.rootOperationNamesForActivityBasedAutoInstrumentations = rootOperationNamesForActivityBasedAutoInstrumentations ?? this.rootOperationNamesForActivityBasedAutoInstrumentations; } private IScopeManager ScopeManager { get; } @@ -172,13 +162,6 @@ public ISpan Start() { span = this.tracer.StartSpan(this.spanName, this.spanKind, this.parentSpanContext, default, this.links, this.explicitStartTime ?? default); } - else if (this.parentSpan == null && !this.parentSpanContext.IsValid && Activity.Current != null && Activity.Current.IdFormat == ActivityIdFormat.W3C) - { - if (this.rootOperationNamesForActivityBasedAutoInstrumentations.Contains(Activity.Current.OperationName)) - { - span = Tracer.CurrentSpan; - } - } if (span == null) { diff --git a/test/OpenTelemetry.Shims.OpenTracing.Tests/SpanBuilderShimTests.cs b/test/OpenTelemetry.Shims.OpenTracing.Tests/SpanBuilderShimTests.cs index c149928d78a..27f9cc11cd3 100644 --- a/test/OpenTelemetry.Shims.OpenTracing.Tests/SpanBuilderShimTests.cs +++ b/test/OpenTelemetry.Shims.OpenTracing.Tests/SpanBuilderShimTests.cs @@ -127,13 +127,13 @@ public void Start_ActivityOperationRootSpanChecks() // matching root operation name var tracer = TracerProvider.Default.GetTracer(TracerName); - var shim = new SpanBuilderShim(tracer, "foo", new List { "foo" }); + var shim = new SpanBuilderShim(tracer, "foo"); var spanShim1 = (SpanShim)shim.Start(); Assert.Equal("foo", spanShim1.Span.Activity.OperationName); // mis-matched root operation name - shim = new SpanBuilderShim(tracer, "foo", new List { "bar" }); + shim = new SpanBuilderShim(tracer, "foo"); var spanShim2 = (SpanShim)shim.Start(); Assert.Equal("foo", spanShim2.Span.Activity.OperationName); @@ -330,5 +330,29 @@ public void Start() Assert.NotNull(span); Assert.Equal("foo", span.Span.Activity.OperationName); } + + [Fact] + public void Start_UnderAspNetCoreInstrumentation() + { + // Simulate a span from AspNetCore instrumentation as parent. + using var source = new ActivitySource("Microsoft.AspNetCore.Hosting.HttpRequestIn"); + using var parentSpan = source.StartActivity("OTelParent"); + Assert.NotNull(parentSpan); + + // Start the OpenTracing span. + var tracer = TracerProvider.Default.GetTracer(TracerName); + var builderShim = new SpanBuilderShim(tracer, "foo"); + var spanShim = builderShim.StartActive().Span as SpanShim; + Assert.NotNull(spanShim); + + var telemetrySpan = spanShim.Span; + Assert.Same(telemetrySpan.Activity, Activity.Current); + Assert.Same(parentSpan, telemetrySpan.Activity.Parent); + + // Dispose the spanShim.Span and ensure correct state for Activity.Current + spanShim.Span.Dispose(); + + Assert.Same(parentSpan, Activity.Current); + } } } From 15e2e0a4f3560b25523e7afd52877be070e7ba66 Mon Sep 17 00:00:00 2001 From: Utkarsh Umesan Pillai Date: Tue, 2 Aug 2022 18:28:08 -0700 Subject: [PATCH 54/58] Update CHANGELOG for 1.4.0-alpha.1 release (#3523) --- src/OpenTelemetry.Api/CHANGELOG.md | 4 ++++ src/OpenTelemetry.Exporter.Console/CHANGELOG.md | 4 ++++ src/OpenTelemetry.Exporter.InMemory/CHANGELOG.md | 4 ++++ src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md | 4 ++++ .../CHANGELOG.md | 4 ++++ src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md | 4 ++++ src/OpenTelemetry.Exporter.ZPages/CHANGELOG.md | 4 ++++ src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md | 4 ++++ src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md | 4 ++++ src/OpenTelemetry.Extensions.Propagators/CHANGELOG.md | 4 ++++ src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md | 4 ++++ src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md | 4 ++++ src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md | 4 ++++ src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md | 4 ++++ src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md | 4 ++++ src/OpenTelemetry/CHANGELOG.md | 4 ++++ 16 files changed, 64 insertions(+) diff --git a/src/OpenTelemetry.Api/CHANGELOG.md b/src/OpenTelemetry.Api/CHANGELOG.md index ab6a21171f2..d17ef3eb0e3 100644 --- a/src/OpenTelemetry.Api/CHANGELOG.md +++ b/src/OpenTelemetry.Api/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.4.0-alpha.1 + +Released 2022-Aug-02 + * Add `Activity.RecordException` overload accepting additional attributes to add to the `ActivityEvent`. [#3433](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3433) diff --git a/src/OpenTelemetry.Exporter.Console/CHANGELOG.md b/src/OpenTelemetry.Exporter.Console/CHANGELOG.md index c81c727d695..fde2a3c421d 100644 --- a/src/OpenTelemetry.Exporter.Console/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Console/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.4.0-alpha.1 + +Released 2022-Aug-02 + * The `MetricReaderOptions` defaults can be overridden using `OTEL_METRIC_EXPORT_INTERVAL` and `OTEL_METRIC_EXPORT_TIMEOUT` environmental variables as defined in the diff --git a/src/OpenTelemetry.Exporter.InMemory/CHANGELOG.md b/src/OpenTelemetry.Exporter.InMemory/CHANGELOG.md index 20be374518e..9cd73b34aa2 100644 --- a/src/OpenTelemetry.Exporter.InMemory/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.InMemory/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.4.0-alpha.1 + +Released 2022-Aug-02 + * `InMemoryExporter` will now buffer scopes when exporting `LogRecord` ([#3360](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3360)) diff --git a/src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md b/src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md index dc7985b864f..b136e59c426 100644 --- a/src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.4.0-alpha.1 + +Released 2022-Aug-02 + ## 1.3.0 Released 2022-Jun-03 diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol.Logs/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol.Logs/CHANGELOG.md index 8a0a3f260ff..22bad645336 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol.Logs/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol.Logs/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.4.0-alpha.1 + +Released 2022-Aug-02 + ## 1.3.0-rc.2 Released 2022-June-1 diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md index b04c352c41e..a68b5b9494a 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.4.0-alpha.1 + +Released 2022-Aug-02 + * Adds support for limiting the length and count of attributes exported from the OTLP exporter. These [Attribute Limits](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#attribute-limits) diff --git a/src/OpenTelemetry.Exporter.ZPages/CHANGELOG.md b/src/OpenTelemetry.Exporter.ZPages/CHANGELOG.md index c9330f85220..97f7a2abd16 100644 --- a/src/OpenTelemetry.Exporter.ZPages/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.ZPages/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.0.0-rc9.5 + +Released 2022-Aug-02 + ## 1.0.0-rc9.4 Released 2022-Jun-03 diff --git a/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md b/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md index 4196962db1f..d07671b0809 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.4.0-alpha.1 + +Released 2022-Aug-02 + ## 1.3.0 Released 2022-Jun-03 diff --git a/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md b/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md index 93a35a7677d..76d47d93281 100644 --- a/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md +++ b/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.0.0-rc9.5 + +Released 2022-Aug-02 + ## 1.0.0-rc9.4 Released 2022-Jun-03 diff --git a/src/OpenTelemetry.Extensions.Propagators/CHANGELOG.md b/src/OpenTelemetry.Extensions.Propagators/CHANGELOG.md index b8ded0360f5..d4a09cab221 100644 --- a/src/OpenTelemetry.Extensions.Propagators/CHANGELOG.md +++ b/src/OpenTelemetry.Extensions.Propagators/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.4.0-alpha.1 + +Released 2022-Aug-02 + * Add `JaegerPropagator`. ([1881](https://github.com/open-telemetry/opentelemetry-dotnet/issues/1881)) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index 7e48cbf5487..7dd26e20f5d 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.0.0-rc9.5 + +Released 2022-Aug-02 + * Fix Remote IP Address - NULL reference exception. ([#3481](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3481)) * Metrics instrumentation to correctly populate `http.flavor` tag. diff --git a/src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md index 2ca87b172f7..843828f26cb 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.0.0-rc9.5 + +Released 2022-Aug-02 + ## 1.0.0-rc9.4 Released 2022-Jun-03 diff --git a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md index 1a5041471bc..20fb945c219 100644 --- a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.0.0-rc9.5 + +Released 2022-Aug-02 + * Added `http.scheme` tag to tracing instrumentation. ([#3464](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3464)) diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md index 015fcd8f5b4..3f0e444a328 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.0.0-rc9.5 + +Released 2022-Aug-02 + * Update the `ActivitySource.Name` from "OpenTelemetry.SqlClient" to "OpenTelemetry.Instrumentation.SqlClient". ([#3435](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3435)) diff --git a/src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md b/src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md index aff4eaa3b30..4affe1b02a0 100644 --- a/src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md +++ b/src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.0.0-rc9.5 + +Released 2022-Aug-02 + * Fix: Handling of OpenTracing spans when used in conjunction with legacy "Microsoft.AspNetCore.Hosting.HttpRequestIn" activities. ([#3509](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3506)) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index b62f0338902..fe70f994bad 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -5,6 +5,10 @@ * Use binary search for histograms with 50 or more supplied boundaries. ([#3252](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3252)) +## 1.4.0-alpha.1 + +Released 2022-Aug-02 + * `TracerProviderSDK` modified for spans with remote parent. For such spans activity will be created irrespective of SamplingResult, to maintain context propagation. From b5190727eb10ee7b75992753f28d55bcedfba0b8 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Tue, 2 Aug 2022 20:29:55 -0700 Subject: [PATCH 55/58] Nit cleanup (#3518) --- .../OpenTelemetry.Tests.Stress.Logs.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/OpenTelemetry.Tests.Stress.Logs/OpenTelemetry.Tests.Stress.Logs.csproj b/test/OpenTelemetry.Tests.Stress.Logs/OpenTelemetry.Tests.Stress.Logs.csproj index ff88e203fb4..45c21139ba3 100644 --- a/test/OpenTelemetry.Tests.Stress.Logs/OpenTelemetry.Tests.Stress.Logs.csproj +++ b/test/OpenTelemetry.Tests.Stress.Logs/OpenTelemetry.Tests.Stress.Logs.csproj @@ -3,7 +3,7 @@ Exe - net6.0;net5.0;netcoreapp3.1;net462 + net6.0;netcoreapp3.1;net462 From dad7f999b275a4e8f478a1baf6389b9bb21ad5bc Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Wed, 3 Aug 2022 10:16:31 -0700 Subject: [PATCH 56/58] Nit internal fixes for PrometheusExporter (#3525) --- .../PrometheusExporterMeterProviderBuilderExtensions.cs | 2 +- .../Internal/PrometheusExporter.cs | 6 +----- .../PrometheusHttpListener.cs | 2 +- .../PrometheusHttpListenerMeterProviderBuilderExtensions.cs | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs index 7039db2a369..25189d1aa37 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs @@ -50,7 +50,7 @@ private static MeterProviderBuilder AddPrometheusExporter(MeterProviderBuilder b { configure?.Invoke(options); - var exporter = new PrometheusExporter(scrapeEndpointPath: options.ScrapeEndpointPath, scrapeResponseCacheDurationMilliseconds: options.ScrapeResponseCacheDurationMilliseconds); + var exporter = new PrometheusExporter(scrapeResponseCacheDurationMilliseconds: options.ScrapeResponseCacheDurationMilliseconds); var reader = new BaseExportingMetricReader(exporter) { TemporalityPreference = MetricReaderTemporalityPreference.Cumulative, diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs index 93514ef4c97..b0d5a5eb7db 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs @@ -33,15 +33,13 @@ internal sealed class PrometheusExporter : BaseExporter, IPullMetricExpo /// /// Initializes a new instance of the class. /// - /// Scraping endpoint. /// /// The cache duration in milliseconds for scrape responses. Default value: 0. /// - public PrometheusExporter(string scrapeEndpointPath = null, int scrapeResponseCacheDurationMilliseconds = 0) + public PrometheusExporter(int scrapeResponseCacheDurationMilliseconds = 0) { Guard.ThrowIfOutOfRange(scrapeResponseCacheDurationMilliseconds, min: 0); - this.ScrapeEndpointPath = scrapeEndpointPath ?? "/metrics"; this.ScrapeResponseCacheDurationMilliseconds = scrapeResponseCacheDurationMilliseconds; this.CollectionManager = new PrometheusCollectionManager(this); } @@ -67,8 +65,6 @@ internal Func, ExportResult> OnExport internal int ScrapeResponseCacheDurationMilliseconds { get; } - internal string ScrapeEndpointPath { get; } - /// public override ExportResult Export(in Batch metrics) { diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs index 3bd122a7732..24707794140 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs @@ -43,7 +43,7 @@ public PrometheusHttpListener(PrometheusExporter exporter, PrometheusHttpListene this.exporter = exporter; - string path = this.exporter.ScrapeEndpointPath; + string path = options.ScrapeEndpointPath; if (!path.StartsWith("/")) { diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs index 6c0b7b75a41..e6fd9f82258 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs @@ -55,7 +55,7 @@ private static MeterProviderBuilder AddPrometheusHttpListener( { configure?.Invoke(options); - var exporter = new PrometheusExporter(scrapeEndpointPath: options.ScrapeEndpointPath); + var exporter = new PrometheusExporter(); var reader = new BaseExportingMetricReader(exporter) { From e60763cff020f8453f115570a4f47cd20fcfcae0 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Wed, 3 Aug 2022 15:27:54 -0700 Subject: [PATCH 57/58] Add Min and Max to Histograms --- .../Implementation/MetricItemExtensions.cs | 6 + .../.publicApi/net6.0/PublicAPI.Unshipped.txt | 2 + src/OpenTelemetry/CHANGELOG.md | 4 + src/OpenTelemetry/Metrics/AggregationType.cs | 31 ++- .../ExplicitBucketHistogramConfiguration.cs | 2 +- src/OpenTelemetry/Metrics/HistogramBuckets.cs | 8 +- src/OpenTelemetry/Metrics/Metric.cs | 19 +- src/OpenTelemetry/Metrics/MetricPoint.cs | 222 ++++++++++++------ src/OpenTelemetry/Metrics/MetricReaderExt.cs | 6 +- src/OpenTelemetry/Metrics/MetricType.cs | 7 +- .../MetricTests.cs | 2 +- .../HttpClientTests.netcore31.cs | 2 +- .../Metrics/MetricTestData.cs | 16 ++ .../Metrics/MetricViewTests.cs | 73 ++++++ 14 files changed, 303 insertions(+), 97 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/MetricItemExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/MetricItemExtensions.cs index fe21b8d058e..515d76d0ab8 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/MetricItemExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/MetricItemExtensions.cs @@ -237,6 +237,7 @@ internal static OtlpMetrics.Metric ToOtlpMetric(this Metric metric) } case MetricType.Histogram: + case MetricType.HistogramWithMinMax: { var histogram = new OtlpMetrics.Histogram { @@ -254,6 +255,11 @@ internal static OtlpMetrics.Metric ToOtlpMetric(this Metric metric) AddAttributes(metricPoint.Tags, dataPoint.Attributes); dataPoint.Count = (ulong)metricPoint.GetHistogramCount(); dataPoint.Sum = metricPoint.GetHistogramSum(); + if (metric.MetricType == MetricType.HistogramWithMinMax) + { + dataPoint.Min = metricPoint.GetHistogramMin(); + dataPoint.Max = metricPoint.GetHistogramMax(); + } foreach (var histogramMeasurement in metricPoint.GetHistogramBuckets()) { diff --git a/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt index a045a0ffba5..af7f98c1dca 100644 --- a/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt @@ -12,6 +12,8 @@ OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMillisecond OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void *REMOVED*static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action? configure = null) -> Microsoft.Extensions.Logging.ILoggingBuilder! +OpenTelemetry.Metrics.MetricPoint.GetHistogramMax() -> double +OpenTelemetry.Metrics.MetricPoint.GetHistogramMin() -> double static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder) -> Microsoft.Extensions.Logging.ILoggingBuilder! static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder! static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, bool disposeProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder! diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index fe70f994bad..da136e377d0 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -5,6 +5,10 @@ * Use binary search for histograms with 50 or more supplied boundaries. ([#3252](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3252)) +* Make recording of `Min` and `Max` for histograms configurable, enabled by + default. + ([#2735](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2735)) + ## 1.4.0-alpha.1 Released 2022-Aug-02 diff --git a/src/OpenTelemetry/Metrics/AggregationType.cs b/src/OpenTelemetry/Metrics/AggregationType.cs index bd87c9e5b4f..104d04bf706 100644 --- a/src/OpenTelemetry/Metrics/AggregationType.cs +++ b/src/OpenTelemetry/Metrics/AggregationType.cs @@ -14,6 +14,8 @@ // limitations under the License. // +using OpenTelemetry.Metrics; + namespace OpenTelemetry.Metrics { internal enum AggregationType @@ -54,13 +56,36 @@ internal enum AggregationType DoubleGauge = 5, /// - /// Histogram. + /// Histogram with sum, count, buckets. /// Histogram = 6, /// - /// Histogram with sum, count only. + /// Histogram with sum, count, min, max, buckets. + /// + HistogramMinMax = 7, + + /// + /// Histogram with sum, count. + /// + HistogramSumCount = 8, + + /// + /// Histogram with sum, count, min, max. /// - HistogramSumCount = 7, + HistogramSumCountMinMax = 9, + } +} + +#pragma warning disable SA1649 // File name should match first type name +internal static class AggregationTypeMethods +#pragma warning restore SA1649 // File name should match first type name +{ + public static bool IsHistogram(this AggregationType aggType) + { + return aggType == AggregationType.Histogram + || aggType == AggregationType.HistogramMinMax + || aggType == AggregationType.HistogramSumCount + || aggType == AggregationType.HistogramSumCountMinMax; } } diff --git a/src/OpenTelemetry/Metrics/ExplicitBucketHistogramConfiguration.cs b/src/OpenTelemetry/Metrics/ExplicitBucketHistogramConfiguration.cs index 58bfc245b10..8be618748fc 100644 --- a/src/OpenTelemetry/Metrics/ExplicitBucketHistogramConfiguration.cs +++ b/src/OpenTelemetry/Metrics/ExplicitBucketHistogramConfiguration.cs @@ -21,7 +21,7 @@ namespace OpenTelemetry.Metrics /// /// Stores configuration for a histogram metric stream with explicit bucket boundaries. /// - public class ExplicitBucketHistogramConfiguration : MetricStreamConfiguration + public class ExplicitBucketHistogramConfiguration : HistogramConfiguration { /// /// Gets or sets the optional boundaries of the histogram metric stream. diff --git a/src/OpenTelemetry/Metrics/HistogramBuckets.cs b/src/OpenTelemetry/Metrics/HistogramBuckets.cs index d45379fbc84..905877a6467 100644 --- a/src/OpenTelemetry/Metrics/HistogramBuckets.cs +++ b/src/OpenTelemetry/Metrics/HistogramBuckets.cs @@ -31,13 +31,17 @@ public class HistogramBuckets internal readonly double[] ExplicitBounds; internal readonly long[] RunningBucketCounts; - internal readonly long[] SnapshotBucketCounts; internal double RunningSum; - internal double SnapshotSum; + internal double RunningMin = double.PositiveInfinity; + internal double SnapshotMin; + + internal double RunningMax = double.NegativeInfinity; + internal double SnapshotMax; + internal int IsCriticalSectionOccupied = 0; private readonly BucketLookupNode bucketLookupTreeRoot; diff --git a/src/OpenTelemetry/Metrics/Metric.cs b/src/OpenTelemetry/Metrics/Metric.cs index 61443173f70..83afa754176 100644 --- a/src/OpenTelemetry/Metrics/Metric.cs +++ b/src/OpenTelemetry/Metrics/Metric.cs @@ -34,7 +34,8 @@ internal Metric( AggregationTemporality temporality, int maxMetricPointsPerMetricStream, double[] histogramBounds = null, - string[] tagKeysInteresting = null) + string[] tagKeysInteresting = null, + bool histogramRecordMinMax = true) { this.InstrumentIdentity = instrumentIdentity; @@ -88,17 +89,11 @@ internal Metric( || instrumentIdentity.InstrumentType == typeof(Histogram) || instrumentIdentity.InstrumentType == typeof(Histogram)) { - this.MetricType = MetricType.Histogram; - - if (histogramBounds != null - && histogramBounds.Length == 0) - { - aggType = AggregationType.HistogramSumCount; - } - else - { - aggType = AggregationType.Histogram; - } + this.MetricType = histogramRecordMinMax ? MetricType.HistogramWithMinMax : MetricType.Histogram; + + aggType = histogramBounds != null && histogramBounds.Length == 0 + ? (histogramRecordMinMax ? AggregationType.HistogramSumCountMinMax : AggregationType.HistogramSumCount) + : (histogramRecordMinMax ? AggregationType.HistogramMinMax : AggregationType.Histogram); } else { diff --git a/src/OpenTelemetry/Metrics/MetricPoint.cs b/src/OpenTelemetry/Metrics/MetricPoint.cs index 149f20bc8f8..c0b6ebba278 100644 --- a/src/OpenTelemetry/Metrics/MetricPoint.cs +++ b/src/OpenTelemetry/Metrics/MetricPoint.cs @@ -58,11 +58,13 @@ internal MetricPoint( this.deltaLastValue = default; this.MetricPointStatus = MetricPointStatus.NoCollectPending; - if (this.aggType == AggregationType.Histogram) + if (this.aggType == AggregationType.Histogram || + this.aggType == AggregationType.HistogramMinMax) { this.histogramBuckets = new HistogramBuckets(histogramExplicitBounds); } - else if (this.aggType == AggregationType.HistogramSumCount) + else if (this.aggType == AggregationType.HistogramSumCount || + this.aggType == AggregationType.HistogramSumCountMinMax) { this.histogramBuckets = new HistogramBuckets(null); } @@ -187,7 +189,7 @@ public readonly double GetGaugeLastValueDouble() [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly long GetHistogramCount() { - if (this.aggType != AggregationType.Histogram && this.aggType != AggregationType.HistogramSumCount) + if (!this.aggType.IsHistogram()) { this.ThrowNotSupportedMetricTypeException(nameof(this.GetHistogramCount)); } @@ -205,7 +207,7 @@ public readonly long GetHistogramCount() [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly double GetHistogramSum() { - if (this.aggType != AggregationType.Histogram && this.aggType != AggregationType.HistogramSumCount) + if (!this.aggType.IsHistogram()) { this.ThrowNotSupportedMetricTypeException(nameof(this.GetHistogramSum)); } @@ -223,7 +225,7 @@ public readonly double GetHistogramSum() [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly HistogramBuckets GetHistogramBuckets() { - if (this.aggType != AggregationType.Histogram && this.aggType != AggregationType.HistogramSumCount) + if (!this.aggType.IsHistogram()) { this.ThrowNotSupportedMetricTypeException(nameof(this.GetHistogramBuckets)); } @@ -231,6 +233,44 @@ public readonly HistogramBuckets GetHistogramBuckets() return this.histogramBuckets; } + /// + /// Gets the minimum value of the histogram associated with the metric point. + /// + /// + /// Applies to metric type. + /// + /// Minimum value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double GetHistogramMin() + { + if (this.aggType != AggregationType.HistogramMinMax && + this.aggType != AggregationType.HistogramSumCountMinMax) + { + this.ThrowNotSupportedMetricTypeException(nameof(this.GetHistogramMin)); + } + + return this.histogramBuckets.SnapshotMin; + } + + /// + /// Gets the maximum value of the histogram associated with the metric point. + /// + /// + /// Applies to metric type. + /// + /// Maximum value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double GetHistogramMax() + { + if (this.aggType != AggregationType.HistogramMinMax && + this.aggType != AggregationType.HistogramSumCountMinMax) + { + this.ThrowNotSupportedMetricTypeException(nameof(this.GetHistogramMax)); + } + + return this.histogramBuckets.SnapshotMax; + } + internal readonly MetricPoint Copy() { MetricPoint copy = this; @@ -261,7 +301,9 @@ internal void Update(long number) } case AggregationType.Histogram: + case AggregationType.HistogramMinMax: case AggregationType.HistogramSumCount: + case AggregationType.HistogramSumCountMinMax: { this.Update((double)number); break; @@ -324,50 +366,25 @@ internal void Update(double number) case AggregationType.Histogram: { - int i = this.histogramBuckets.FindBucketIndex(number); - - var sw = default(SpinWait); - while (true) - { - if (Interlocked.Exchange(ref this.histogramBuckets.IsCriticalSectionOccupied, 1) == 0) - { - unchecked - { - this.runningValue.AsLong++; - this.histogramBuckets.RunningSum += number; - this.histogramBuckets.RunningBucketCounts[i]++; - } - - this.histogramBuckets.IsCriticalSectionOccupied = 0; - break; - } - - sw.SpinOnce(); - } + this.HistogramUpdate(number, true, false); + break; + } + case AggregationType.HistogramMinMax: + { + this.HistogramUpdate(number, true, true); break; } case AggregationType.HistogramSumCount: { - var sw = default(SpinWait); - while (true) - { - if (Interlocked.Exchange(ref this.histogramBuckets.IsCriticalSectionOccupied, 1) == 0) - { - unchecked - { - this.runningValue.AsLong++; - this.histogramBuckets.RunningSum += number; - } - - this.histogramBuckets.IsCriticalSectionOccupied = 0; - break; - } - - sw.SpinOnce(); - } + this.HistogramUpdate(number, false, false); + break; + } + case AggregationType.HistogramSumCountMinMax: + { + this.HistogramUpdate(number, false, true); break; } } @@ -487,46 +504,25 @@ internal void TakeSnapshot(bool outputDelta) case AggregationType.Histogram: { - lock (this.histogramBuckets.LockObject) - { - this.snapshotValue.AsLong = this.runningValue.AsLong; - this.histogramBuckets.SnapshotSum = this.histogramBuckets.RunningSum; - if (outputDelta) - { - this.runningValue.AsLong = 0; - this.histogramBuckets.RunningSum = 0; - } - - for (int i = 0; i < this.histogramBuckets.RunningBucketCounts.Length; i++) - { - this.histogramBuckets.SnapshotBucketCounts[i] = this.histogramBuckets.RunningBucketCounts[i]; - if (outputDelta) - { - this.histogramBuckets.RunningBucketCounts[i] = 0; - } - } - - this.MetricPointStatus = MetricPointStatus.NoCollectPending; - } + this.HistogramSnapshot(outputDelta, true, false); + break; + } + case AggregationType.HistogramMinMax: + { + this.HistogramSnapshot(outputDelta, true, true); break; } case AggregationType.HistogramSumCount: { - lock (this.histogramBuckets.LockObject) - { - this.snapshotValue.AsLong = this.runningValue.AsLong; - this.histogramBuckets.SnapshotSum = this.histogramBuckets.RunningSum; - if (outputDelta) - { - this.runningValue.AsLong = 0; - this.histogramBuckets.RunningSum = 0; - } - - this.MetricPointStatus = MetricPointStatus.NoCollectPending; - } + this.HistogramSnapshot(outputDelta, false, false); + break; + } + case AggregationType.HistogramSumCountMinMax: + { + this.HistogramSnapshot(outputDelta, false, true); break; } } @@ -537,5 +533,81 @@ private readonly void ThrowNotSupportedMetricTypeException(string methodName) { throw new NotSupportedException($"{methodName} is not supported for this metric type."); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void HistogramUpdate(double number, bool hasBuckets, bool hasMinMax) + { + int i = hasBuckets + ? this.histogramBuckets.FindBucketIndex(number) + : 0; + + var sw = default(SpinWait); + while (true) + { + if (Interlocked.Exchange(ref this.histogramBuckets.IsCriticalSectionOccupied, 1) == 0) + { + unchecked + { + this.runningValue.AsLong++; + this.histogramBuckets.RunningSum += number; + if (hasBuckets) + { + this.histogramBuckets.RunningBucketCounts[i]++; + } + + if (hasMinMax) + { + this.histogramBuckets.RunningMin = Math.Min(this.histogramBuckets.RunningMin, number); + this.histogramBuckets.RunningMax = Math.Max(this.histogramBuckets.RunningMax, number); + } + } + + this.histogramBuckets.IsCriticalSectionOccupied = 0; + break; + } + + sw.SpinOnce(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void HistogramSnapshot(bool outputDelta, bool hasBuckets, bool hasMinMax) + { + lock (this.histogramBuckets.LockObject) + { + this.snapshotValue.AsLong = this.runningValue.AsLong; + this.histogramBuckets.SnapshotSum = this.histogramBuckets.RunningSum; + if (hasMinMax) + { + this.histogramBuckets.SnapshotMin = this.histogramBuckets.RunningMin; + this.histogramBuckets.SnapshotMax = this.histogramBuckets.RunningMax; + } + + if (outputDelta) + { + this.runningValue.AsLong = 0; + this.histogramBuckets.RunningSum = 0; + if (hasMinMax) + { + this.histogramBuckets.RunningMin = 0; + this.histogramBuckets.RunningMax = 0; + } + } + + if (hasBuckets) + { + for (int i = 0; i < this.histogramBuckets.RunningBucketCounts.Length; i++) + { + this.histogramBuckets.SnapshotBucketCounts[i] = this.histogramBuckets.RunningBucketCounts[i]; + if (outputDelta) + { + this.histogramBuckets.RunningBucketCounts[i] = 0; + } + } + } + + this.MetricPointStatus = MetricPointStatus.NoCollectPending; + } + } } } diff --git a/src/OpenTelemetry/Metrics/MetricReaderExt.cs b/src/OpenTelemetry/Metrics/MetricReaderExt.cs index c8cb0db0a0e..6d7342b597a 100644 --- a/src/OpenTelemetry/Metrics/MetricReaderExt.cs +++ b/src/OpenTelemetry/Metrics/MetricReaderExt.cs @@ -157,7 +157,11 @@ internal List AddMetricsListWithViews(Instrument instrument, List Histogram = 0x40, + + /// + /// Histogram with minimum and maximum values. + /// + HistogramWithMinMax = 0x50, } } diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs index 53bac48facc..b2dfc036d28 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs @@ -80,7 +80,7 @@ public async Task RequestMetricIsCaptured() var metric = requestMetrics[0]; Assert.NotNull(metric); - Assert.True(metric.MetricType == MetricType.Histogram); + Assert.True(metric.MetricType == MetricType.HistogramWithMinMax); var metricPoints = new List(); foreach (var p in metric.GetMetricPoints()) { diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.netcore31.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.netcore31.cs index 05c39935426..94d2ab171ca 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.netcore31.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.netcore31.cs @@ -142,7 +142,7 @@ public async Task HttpOutCallsAreCollectedSuccessfullyAsync(HttpTestData.HttpOut var metric = requestMetrics[0]; Assert.NotNull(metric); - Assert.True(metric.MetricType == MetricType.Histogram); + Assert.True(metric.MetricType == MetricType.HistogramWithMinMax); var metricPoints = new List(); foreach (var p in metric.GetMetricPoints()) diff --git a/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs b/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs index 0664dc18c00..f1a2b517a93 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs @@ -49,5 +49,21 @@ public static IEnumerable InvalidHistogramBoundaries new object[] { new double[] { 0, 1, 1, 2 } }, new object[] { new double[] { 0, 1, 2, -1 } }, }; + + public static IEnumerable ValidHistogramMinMax + => new List + { + new object[] { new double[] { -10, 0, 1, 9, 10, 11, 19 }, new HistogramConfiguration(), -10, 19 }, + new object[] { new double[] { double.NegativeInfinity, 0, double.PositiveInfinity }, new HistogramConfiguration(), double.NegativeInfinity, double.PositiveInfinity }, + new object[] { new double[] { 1 }, new HistogramConfiguration(), 1, 1 }, + new object[] { new double[] { 5, 100, 4, 101, -2, 97 }, new ExplicitBucketHistogramConfiguration() { Boundaries = new double[] { 10, 20 } }, -2, 101 }, + }; + + public static IEnumerable InvalidHistogramMinMax + => new List + { + new object[] { new double[] { 1 }, new HistogramConfiguration() { RecordMinMax = false } }, + new object[] { new double[] { 1 }, new ExplicitBucketHistogramConfiguration() { Boundaries = new double[] { 10, 20 }, RecordMinMax = false } }, + }; } } diff --git a/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs b/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs index 3a12cd4ac05..323c87abaf2 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs @@ -572,6 +572,79 @@ public void ViewToProduceCustomHistogramBound() Assert.Equal(boundaries.Length + 1, actualCount); } + [Theory] + [MemberData(nameof(MetricTestData.ValidHistogramMinMax), MemberType = typeof(MetricTestData))] + public void HistogramMinMax(double[] values, HistogramConfiguration histogramConfiguration, double expectedMin, double expectedMax) + { + using var meter = new Meter(Utils.GetCurrentMethodName()); + var histogram = meter.CreateHistogram("MyHistogram"); + var exportedItems = new List(); + using var meterProvider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddView(histogram.Name, histogramConfiguration) + .AddInMemoryExporter(exportedItems) + .Build(); + + for (var i = 0; i < values.Length; i++) + { + histogram.Record(values[i]); + } + + meterProvider.ForceFlush(MaxTimeToAllowForFlush); + + Assert.Single(exportedItems); + var metric1 = exportedItems[0]; + + Assert.Equal("MyHistogram", metric1.Name); + + var metricPoints = new List(); + foreach (ref readonly var mp in metric1.GetMetricPoints()) + { + metricPoints.Add(mp); + } + + Assert.Single(metricPoints); + var metricPoint = metricPoints[0]; + + Assert.Equal(expectedMin, metricPoint.GetHistogramMin()); + Assert.Equal(expectedMax, metricPoint.GetHistogramMax()); + } + + [Theory] + [MemberData(nameof(MetricTestData.InvalidHistogramMinMax), MemberType = typeof(MetricTestData))] + public void HistogramMinMaxThrows(double[] values, HistogramConfiguration histogramConfiguration) + { + using var meter = new Meter(Utils.GetCurrentMethodName()); + var histogram = meter.CreateHistogram("MyHistogram"); + var exportedItems = new List(); + using var meterProvider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddView(histogram.Name, histogramConfiguration) + .AddInMemoryExporter(exportedItems) + .Build(); + + for (var i = 0; i < values.Length; i++) + { + histogram.Record(values[i]); + } + + meterProvider.ForceFlush(MaxTimeToAllowForFlush); + + var metricPoints = new List(); + foreach (ref readonly var mp in exportedItems[0].GetMetricPoints()) + { + metricPoints.Add(mp); + } + + var histogramPoint = metricPoints[0]; + + var ex = Assert.Throws(() => histogramPoint.GetHistogramMin()); + Assert.Contains($"{nameof(histogramPoint.GetHistogramMin)} is not supported for this metric type.", ex.Message); + + ex = Assert.Throws(() => histogramPoint.GetHistogramMax()); + Assert.Contains($"{nameof(histogramPoint.GetHistogramMax)} is not supported for this metric type.", ex.Message); + } + [Fact] public void ViewToSelectTagKeys() { From 9693e39e94bc2bb4232253c44d28ba5ae43ff7c2 Mon Sep 17 00:00:00 2001 From: Michael Maxwell Date: Wed, 3 Aug 2022 15:28:51 -0700 Subject: [PATCH 58/58] Forgot HistogramConfiguration.cs file --- .../Metrics/HistogramConfiguration.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/OpenTelemetry/Metrics/HistogramConfiguration.cs diff --git a/src/OpenTelemetry/Metrics/HistogramConfiguration.cs b/src/OpenTelemetry/Metrics/HistogramConfiguration.cs new file mode 100644 index 00000000000..37980098937 --- /dev/null +++ b/src/OpenTelemetry/Metrics/HistogramConfiguration.cs @@ -0,0 +1,26 @@ +// +// 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. +// + +namespace OpenTelemetry.Metrics; + +public class HistogramConfiguration : MetricStreamConfiguration +{ + /// + /// Gets or sets a value indicating whether Min, Max + /// should be collected. + /// + public bool RecordMinMax { get; set; } = true; +}