diff --git a/examples/Console/Program.cs b/examples/Console/Program.cs index a4c6b6a687d..42dac16bb1f 100644 --- a/examples/Console/Program.cs +++ b/examples/Console/Program.cs @@ -42,14 +42,12 @@ public class Program /// Arguments from command line. public static void Main(string[] args) { - bool prompt = true; - Parser.Default.ParseArguments(args) .MapResult( (JaegerOptions options) => TestJaegerExporter.Run(options.Host, options.Port), (ZipkinOptions options) => TestZipkinExporter.Run(options.Uri), (PrometheusOptions options) => TestPrometheusExporter.Run(options.Port, options.PushIntervalInSecs, options.DurationInMins), - (MetricsOptions options) => TestMetrics.Run(options, ref prompt), + (MetricsOptions options) => TestMetrics.Run(options), (GrpcNetClientOptions options) => TestGrpcNetClient.Run(), (HttpClientOptions options) => TestHttpClient.Run(), (RedisOptions options) => TestRedis.Run(options.Uri), @@ -60,11 +58,6 @@ public static void Main(string[] args) (OtlpOptions options) => TestOtlpExporter.Run(options.Endpoint), (InMemoryOptions options) => TestInMemoryExporter.Run(options), errs => 1); - - if (prompt) - { - System.Console.ReadLine(); - } } } @@ -103,25 +96,28 @@ internal class PrometheusOptions [Verb("metrics", HelpText = "Specify the options required to test Metrics")] internal class MetricsOptions { - [Option('p', "prompt", HelpText = "Prompt for exit", Default = false)] - public bool? Prompt { get; set; } + [Option('g', "Gauge", HelpText = "Include Observable Gauge.", Required = false)] + public bool? FlagGauge { get; set; } - [Option("runtime", Default = 5000, HelpText = "Run time in milliseconds.", Required = false)] - public int RunTime { get; set; } + [Option('u', "UpDownCounter", HelpText = "Include Observable Up/Down Counter.", Required = false)] + public bool? FlagUpDownCounter { get; set; } - [Option("observationPeriodMilliseconds", Default = 100, HelpText = "Observation period.", Required = false)] - public int ObservationPeriodMilliseconds { get; set; } + [Option('c', "Counter", HelpText = "Include Counter.", Required = false)] + public bool? FlagCounter { get; set; } - [Option("collectionPeriodMilliseconds", Default = 500, HelpText = "Collection period.", Required = false)] - public int CollectionPeriodMilliseconds { get; set; } + [Option('h', "Histogram", HelpText = "Include Histogram.", Required = false)] + public bool? FlagHistogram { get; set; } - [Option('o', "runObservable", Default = true, HelpText = "Run observable counters.", Required = false)] - public bool? RunObservable { get; set; } + [Option("defaultCollection", Default = 500, HelpText = "Default collection period in milliseconds.", Required = false)] + public int DefaultCollectionPeriodMilliseconds { get; set; } + + [Option("runtime", Default = 5000, HelpText = "Run time in milliseconds.", Required = false)] + public int RunTime { get; set; } - [Option('t', "numTasks", Default = 1, HelpText = "Run # of tasks.", Required = false)] + [Option("tasks", Default = 1, HelpText = "Run # of concurrent tasks.", Required = false)] public int NumTasks { get; set; } - [Option("maxLoops", Default = 0, HelpText = "Maximum number of loops. 0 = No Limit", Required = false)] + [Option("maxLoops", Default = 0, HelpText = "Maximum number of loops/iterations per task. (0 = No Limit)", Required = false)] public int MaxLoops { get; set; } } diff --git a/examples/Console/TestGrpcNetClient.cs b/examples/Console/TestGrpcNetClient.cs index ade965950c8..a15bd6fbde3 100644 --- a/examples/Console/TestGrpcNetClient.cs +++ b/examples/Console/TestGrpcNetClient.cs @@ -66,6 +66,7 @@ internal static object Run() } System.Console.WriteLine("Press Enter key to exit."); + System.Console.ReadLine(); return null; } diff --git a/examples/Console/TestHttpClient.cs b/examples/Console/TestHttpClient.cs index 1e08e48d5d4..78d0047a993 100644 --- a/examples/Console/TestHttpClient.cs +++ b/examples/Console/TestHttpClient.cs @@ -47,6 +47,7 @@ internal static object Run() } System.Console.WriteLine("Press Enter key to exit."); + System.Console.ReadLine(); return null; } diff --git a/examples/Console/TestMetrics.cs b/examples/Console/TestMetrics.cs index 259ca4c3d69..3568d4ae833 100644 --- a/examples/Console/TestMetrics.cs +++ b/examples/Console/TestMetrics.cs @@ -26,25 +26,46 @@ namespace Examples.Console { internal class TestMetrics { - internal static object Run(MetricsOptions options, ref bool prompt) + internal static object Run(MetricsOptions options) { - prompt = options.Prompt.Value; - using var provider = Sdk.CreateMeterProviderBuilder() .AddSource("TestMeter") // All instruments from this meter are enabled. - .SetObservationPeriod(options.ObservationPeriodMilliseconds) - .SetCollectionPeriod(options.CollectionPeriodMilliseconds) - .AddProcessor(new TagEnrichmentProcessor("newAttrib", "newAttribValue")) - .AddExportProcessor(new MetricConsoleExporter()) + .SetDefaultCollectionPeriod(options.DefaultCollectionPeriodMilliseconds) + .AddProcessor(new TagEnrichmentProcessor("resource", "here")) + .AddExportProcessor(new MetricConsoleExporter("A")) + .AddExportProcessor(new MetricConsoleExporter("B"), 5 * options.DefaultCollectionPeriodMilliseconds) .Build(); using var meter = new Meter("TestMeter", "0.0.1"); - var counter = meter.CreateCounter("counter1"); + Counter counter = null; + if (options.FlagCounter ?? true) + { + counter = meter.CreateCounter("counter"); + } + + Histogram histogram = null; + if (options.FlagHistogram ?? true) + { + histogram = meter.CreateHistogram("histogram"); + } + + if (options.FlagGauge ?? true) + { + var observableCounter = meter.CreateObservableGauge("gauge", () => + { + return new List>() + { + new Measurement( + (int)Process.GetCurrentProcess().PrivateMemorySize64, + new KeyValuePair("tag1", "value1")), + }; + }); + } - if (options.RunObservable ?? true) + if (options.FlagUpDownCounter ?? true) { - var observableCounter = meter.CreateObservableGauge("CurrentMemoryUsage", () => + var observableCounter = meter.CreateObservableCounter("updown", () => { return new List>() { @@ -76,22 +97,42 @@ internal static object Run(MetricsOptions options, ref bool prompt) break; } - counter.Add(10); + histogram?.Record(10); + + histogram?.Record( + 100, + new KeyValuePair("tag1", "value1")); + + histogram?.Record( + 200, + new KeyValuePair("tag1", "value2"), + new KeyValuePair("tag2", "value2")); + + histogram?.Record( + 100, + new KeyValuePair("tag1", "value1")); - counter.Add( + histogram?.Record( + 200, + new KeyValuePair("tag2", "value2"), + new KeyValuePair("tag1", "value2")); + + counter?.Add(10); + + counter?.Add( 100, new KeyValuePair("tag1", "value1")); - counter.Add( + counter?.Add( 200, new KeyValuePair("tag1", "value2"), new KeyValuePair("tag2", "value2")); - counter.Add( + counter?.Add( 100, new KeyValuePair("tag1", "value1")); - counter.Add( + counter?.Add( 200, new KeyValuePair("tag2", "value2"), new KeyValuePair("tag1", "value2")); @@ -102,13 +143,14 @@ internal static object Run(MetricsOptions options, ref bool prompt) } cts.CancelAfter(options.RunTime); - Task.WaitAll(tasks.ToArray()); - - if (prompt) + System.Console.WriteLine($"Wait for {options.RunTime} milliseconds."); + while (!cts.IsCancellationRequested) { - System.Console.WriteLine("Press Enter key to exit."); + Task.Delay(1000).Wait(); } + Task.WaitAll(tasks.ToArray()); + return null; } } diff --git a/examples/Console/TestOTelShimWithConsoleExporter.cs b/examples/Console/TestOTelShimWithConsoleExporter.cs index cb439b8a041..984ac7ad380 100644 --- a/examples/Console/TestOTelShimWithConsoleExporter.cs +++ b/examples/Console/TestOTelShimWithConsoleExporter.cs @@ -51,6 +51,7 @@ internal static object Run(OpenTelemetryShimOptions options) } System.Console.WriteLine("Press Enter key to exit."); + System.Console.ReadLine(); return null; } diff --git a/examples/Console/TestOpenTracingShim.cs b/examples/Console/TestOpenTracingShim.cs index a0fbdf9c286..a5d75eac7dc 100644 --- a/examples/Console/TestOpenTracingShim.cs +++ b/examples/Console/TestOpenTracingShim.cs @@ -60,6 +60,7 @@ internal static object Run(OpenTracingShimOptions options) } System.Console.WriteLine("Press Enter key to exit."); + System.Console.ReadLine(); return null; } diff --git a/examples/Console/TestPrometheusExporter.cs b/examples/Console/TestPrometheusExporter.cs index 3b78c169098..098846b4793 100644 --- a/examples/Console/TestPrometheusExporter.cs +++ b/examples/Console/TestPrometheusExporter.cs @@ -43,6 +43,8 @@ internal static object Run(int port, int pushIntervalInSecs, int totalDurationIn - targets: ['localhost:9184'] */ System.Console.WriteLine("Press Enter key to exit."); + System.Console.ReadLine(); + return null; } } diff --git a/examples/Console/TestRedis.cs b/examples/Console/TestRedis.cs index 745b077043c..12981cc7928 100644 --- a/examples/Console/TestRedis.cs +++ b/examples/Console/TestRedis.cs @@ -70,6 +70,9 @@ internal static object Run(string zipkinUri) } } + System.Console.Write("Press ENTER to stop."); + System.Console.ReadLine(); + return null; } diff --git a/src/OpenTelemetry/Metrics/Aggregator/LastValueAggregator.cs b/src/OpenTelemetry/Metrics/Aggregator/LastValueAggregator.cs deleted file mode 100644 index 757dd99150e..00000000000 --- a/src/OpenTelemetry/Metrics/Aggregator/LastValueAggregator.cs +++ /dev/null @@ -1,85 +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; -using System.Diagnostics.Metrics; -using System.Linq; - -namespace OpenTelemetry.Metrics -{ - internal class LastValueAggregator : Aggregator - { - private readonly Instrument instrument; - private readonly KeyValuePair[] tags; - - private readonly object lockUpdate = new object(); - private int count = 0; - private DataPoint lastDataPoint; - - internal LastValueAggregator(Instrument instrument, string[] names, object[] values) - { - this.instrument = instrument; - - if (names.Length != values.Length) - { - throw new ArgumentException($"Length of {nameof(names)} and {nameof(values)} must match."); - } - - this.tags = new KeyValuePair[names.Length]; - for (int i = 0; i < names.Length; i++) - { - this.tags[i] = new KeyValuePair(names[i], values[i]); - } - } - - internal override void Update(DateTimeOffset dt, T value) - where T : struct - { - lock (this.lockUpdate) - { - this.count++; - this.lastDataPoint = DataPoint.CreateDataPoint(dt, value, this.tags); - } - } - - internal override IEnumerable Collect() - { - // TODO: Need to determine how to convert to Metric - - if (this.count == 0) - { - return Enumerable.Empty(); - } - - DataPoint lastValue; - lock (this.lockUpdate) - { - lastValue = this.lastDataPoint; - this.count = 0; - } - - var metrics = new Metric[] - { - new Metric( - $"{this.instrument.Meter.Name}:{this.instrument.Name}:LastValue", - lastValue), - }; - - return metrics; - } - } -} diff --git a/src/OpenTelemetry/Metrics/Aggregator/SumAggregator.cs b/src/OpenTelemetry/Metrics/Aggregator/SumAggregator.cs deleted file mode 100644 index 4fc75ccce70..00000000000 --- a/src/OpenTelemetry/Metrics/Aggregator/SumAggregator.cs +++ /dev/null @@ -1,119 +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; -using System.Diagnostics.Metrics; -using System.Linq; - -namespace OpenTelemetry.Metrics -{ - internal class SumAggregator : Aggregator - { - private readonly Instrument instrument; - private readonly KeyValuePair[] tags; - private readonly object lockUpdate = new object(); - private Type valueType; - private long sum = 0; - private double dsum = 0; - private long count = 0; - - internal SumAggregator(Instrument instrument, string[] names, object[] values) - { - this.instrument = instrument; - - if (names.Length != values.Length) - { - throw new ArgumentException("Length of names[] and values[] must match."); - } - - this.tags = new KeyValuePair[names.Length]; - for (int i = 0; i < names.Length; i++) - { - this.tags[i] = new KeyValuePair(names[i], values[i]); - } - } - - internal override void Update(DateTimeOffset dt, T value) - where T : struct - { - lock (this.lockUpdate) - { - this.count++; - this.valueType = typeof(T); - - // TODO: Need to handle DataPoint appropriately - - if (typeof(T) == typeof(int)) - { - this.sum += (int)(object)value; - } - else if (typeof(T) == typeof(double)) - { - this.dsum += (double)(object)value; - } - else - { - throw new Exception("Unsupported Type"); - } - } - } - - internal override IEnumerable Collect() - { - // TODO: Need to determine how to convert to Metric - - if (this.count == 0) - { - return Enumerable.Empty(); - } - - var dt = MeterProviderSdk.GetDateTimeOffset(); - - IDataPoint datapointSum; - IDataPoint datapointCount; - lock (this.lockUpdate) - { - datapointCount = new DataPoint(dt, (int)this.count, this.tags); - - if (this.valueType == typeof(int)) - { - datapointSum = new DataPoint(dt, (int)this.sum, this.tags); - this.sum = 0; - } - else if (this.valueType == typeof(double)) - { - datapointSum = new DataPoint(dt, (double)this.dsum, this.tags); - this.dsum = 0; - } - else - { - throw new Exception("Unsupported Type"); - } - - this.count = 0; - } - - var metrics = new Metric[] - { - new Metric($"{this.instrument.Meter.Name}:{this.instrument.Name}:Count", datapointCount), - new Metric($"{this.instrument.Meter.Name}:{this.instrument.Name}:Sum", datapointSum), - }; - - return metrics; - } - } -} diff --git a/src/OpenTelemetry/Metrics/AggregatorStore.cs b/src/OpenTelemetry/Metrics/AggregatorStore.cs index 9f3fe9a7870..fafcd6e4ab0 100644 --- a/src/OpenTelemetry/Metrics/AggregatorStore.cs +++ b/src/OpenTelemetry/Metrics/AggregatorStore.cs @@ -15,9 +15,9 @@ // using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.Metrics; +using System.Linq; namespace OpenTelemetry.Metrics { @@ -31,54 +31,74 @@ internal class AggregatorStore private readonly object lockKeyValue2MetricAggs = new object(); - // Two-Level lookup. TagKeys x [ TagValues x Aggregators ] - private readonly Dictionary> keyValue2MetricAggs = - new Dictionary>(new StringArrayEqualityComparer()); + // Two-Level lookup. TagKeys x [ TagValues x Metrics ] + private readonly Dictionary> keyValue2MetricAggs = + new Dictionary>(new StringArrayEqualityComparer()); - private Aggregator[] tag0Aggregators = null; + private MetricAgg[] tag0Metrics = null; + + private IEnumerable timePeriods; internal AggregatorStore(MeterProviderSdk sdk, Instrument instrument) { this.sdk = sdk; this.instrument = instrument; - foreach (var processor in this.sdk.AggregateProcessors) - { - processor.Register(this); - } + this.timePeriods = this.sdk.ExportProcessors.Select(k => k.Value).Distinct(); } - internal Aggregator[] GetDefaultAggregator(string[] seqKey, object[] seqVal) + internal MetricAgg[] MapToMetrics(string[] seqKey, object[] seqVal) { - // TODO: Figure out default aggregator/s based on Instrument and configs + var metricpairs = new List(); + + var name = $"{this.instrument.Meter.Name}:{this.instrument.Name}"; - if (!this.instrument.IsObservable) + var tags = new KeyValuePair[seqKey.Length]; + for (int i = 0; i < seqKey.Length; i++) { - return new Aggregator[] - { - new SumAggregator(this.instrument, seqKey, seqVal), - new LastValueAggregator(this.instrument, seqKey, seqVal), - }; + tags[i] = new KeyValuePair(seqKey[i], seqVal[i]); } - return new Aggregator[] + var dt = DateTimeOffset.UtcNow; + + foreach (var timeperiod in this.timePeriods) { - new LastValueAggregator(this.instrument, seqKey, seqVal), - }; + // TODO: Need to map each instrument to metrics (based on View API) + + if (this.instrument.GetType().Name.Contains("Counter")) + { + metricpairs.Add(new MetricAgg(timeperiod, new SumMetricAggregator(name, dt, tags, false, true))); + metricpairs.Add(new MetricAgg(timeperiod, new SumMetricAggregator(name, dt, tags, true, true))); + } + else if (this.instrument.GetType().Name.Contains("Gauge")) + { + metricpairs.Add(new MetricAgg(timeperiod, new GaugeMetricAggregator(name, dt, tags))); + } + else if (this.instrument.GetType().Name.Contains("Histogram")) + { + metricpairs.Add(new MetricAgg(timeperiod, new HistogramMetricAggregator(name, dt, tags, false))); + } + else + { + metricpairs.Add(new MetricAgg(timeperiod, new SummaryMetricAggregator(name, dt, tags, false))); + } + } + + return metricpairs.ToArray(); } - internal Aggregator[] FindAggregators(ReadOnlySpan> tags) + internal MetricAgg[] FindMetricAggregators(ReadOnlySpan> tags) { int len = tags.Length; if (len == 0) { - if (this.tag0Aggregators == null) + if (this.tag0Metrics == null) { - this.tag0Aggregators = this.GetDefaultAggregator(AggregatorStore.EmptySeqKey, AggregatorStore.EmptySeqValue); + this.tag0Metrics = this.MapToMetrics(AggregatorStore.EmptySeqKey, AggregatorStore.EmptySeqValue); } - return this.tag0Aggregators; + return this.tag0Metrics; } var storage = ThreadStaticStorage.GetStorage(); @@ -90,14 +110,14 @@ internal Aggregator[] FindAggregators(ReadOnlySpan> Array.Sort(tagKey, tagValue); } - Aggregator[] aggregators; + MetricAgg[] metrics; lock (this.lockKeyValue2MetricAggs) { string[] seqKey = null; // GetOrAdd by TagKey at 1st Level of 2-level dictionary structure. - // Get back a Dictionary of [ Values x Aggregators[] ]. + // Get back a Dictionary of [ Values x Metrics[] ]. if (!this.keyValue2MetricAggs.TryGetValue(tagKey, out var value2metrics)) { // Note: We are using storage from ThreadStatic, so need to make a deep copy for Dictionary storage. @@ -105,13 +125,13 @@ internal Aggregator[] FindAggregators(ReadOnlySpan> seqKey = new string[len]; tagKey.CopyTo(seqKey, 0); - value2metrics = new Dictionary(new ObjectArrayEqualityComparer()); + value2metrics = new Dictionary(new ObjectArrayEqualityComparer()); this.keyValue2MetricAggs.Add(seqKey, value2metrics); } // GetOrAdd by TagValue at 2st Level of 2-level dictionary structure. - // Get back Aggregators[]. - if (!value2metrics.TryGetValue(tagValue, out aggregators)) + // Get back Metrics[]. + if (!value2metrics.TryGetValue(tagValue, out metrics)) { // Note: We are using storage from ThreadStatic, so need to make a deep copy for Dictionary storage. @@ -124,13 +144,13 @@ internal Aggregator[] FindAggregators(ReadOnlySpan> var seqVal = new object[len]; tagValue.CopyTo(seqVal, 0); - aggregators = this.GetDefaultAggregator(seqKey, seqVal); + metrics = this.MapToMetrics(seqKey, seqVal); - value2metrics.Add(seqVal, aggregators); + value2metrics.Add(seqVal, metrics); } } - return aggregators; + return metrics; } internal void Update(DateTimeOffset dt, T value, ReadOnlySpan> tags) @@ -141,34 +161,51 @@ internal void Update(DateTimeOffset dt, T value, ReadOnlySpan Collect() + internal List Collect(int periodMilliseconds) { - var aggs = new List(); + var collectedMetrics = new List(); + + var dt = DateTimeOffset.UtcNow; foreach (var keys in this.keyValue2MetricAggs) { foreach (var values in keys.Value) { - aggs.AddRange(values.Value); + foreach (var metric in values.Value) + { + if (metric.TimePeriod == periodMilliseconds) + { + var m = metric.Metric.Collect(dt); + if (m != null) + { + collectedMetrics.Add(m); + } + } + } } } - var metrics = new List(); + return collectedMetrics; + } + + internal class MetricAgg + { + internal int TimePeriod; + internal IAggregator Metric; - foreach (var aggregator in aggs) + internal MetricAgg(int timePeriod, IAggregator metric) { - metrics.AddRange(aggregator.Collect()); + this.TimePeriod = timePeriod; + this.Metric = metric; } - - return metrics; } } } diff --git a/src/OpenTelemetry/Metrics/DataPoint.cs b/src/OpenTelemetry/Metrics/DataPoint/DataPoint.cs similarity index 57% rename from src/OpenTelemetry/Metrics/DataPoint.cs rename to src/OpenTelemetry/Metrics/DataPoint/DataPoint.cs index 98544d3b374..1b59f5e6d82 100644 --- a/src/OpenTelemetry/Metrics/DataPoint.cs +++ b/src/OpenTelemetry/Metrics/DataPoint/DataPoint.cs @@ -16,65 +16,60 @@ using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; namespace OpenTelemetry.Metrics { internal readonly struct DataPoint : IDataPoint { - internal readonly Type ValueType; - internal readonly int IntValue; - internal readonly double DoubleValue; + private static readonly KeyValuePair[] EmptyTag = new KeyValuePair[0]; - private readonly DateTimeOffset timestamp; + private readonly Type valueType; + private readonly long longValue; + private readonly double doubleValue; - private readonly KeyValuePair[] tags; - - internal DataPoint(DateTimeOffset timestamp, int value, KeyValuePair[] tags) + internal DataPoint(DateTimeOffset timestamp, long value, KeyValuePair[] tags) { - this.timestamp = timestamp; - this.tags = tags; - this.ValueType = value.GetType(); - this.IntValue = value; - this.DoubleValue = 0; + this.Timestamp = timestamp; + this.Tags = tags; + this.valueType = typeof(long); + this.longValue = value; + this.doubleValue = 0; } internal DataPoint(DateTimeOffset timestamp, double value, KeyValuePair[] tags) { - this.timestamp = timestamp; - this.tags = tags; - this.ValueType = value.GetType(); - this.IntValue = 0; - this.DoubleValue = value; + this.Timestamp = timestamp; + this.Tags = tags; + this.valueType = typeof(double); + this.longValue = 0; + this.doubleValue = value; } - public KeyValuePair[] Tags + internal DataPoint(DateTimeOffset timestamp, long value) + : this(timestamp, value, DataPoint.EmptyTag) { - get - { - return this.tags; - } } - public DateTimeOffset Timestamp + internal DataPoint(DateTimeOffset timestamp, double value) + : this(timestamp, value, DataPoint.EmptyTag) { - get - { - return this.timestamp; - } } + public DateTimeOffset Timestamp { get; } + + public readonly KeyValuePair[] Tags { get; } + public object Value { get { - if (this.ValueType == typeof(int)) + if (this.valueType == typeof(long)) { - return this.IntValue; + return this.longValue; } - else if (this.ValueType == typeof(double)) + else if (this.valueType == typeof(double)) { - return this.DoubleValue; + return this.doubleValue; } else { @@ -89,8 +84,13 @@ internal static DataPoint CreateDataPoint(DateTimeOffset timestamp, T value, if (typeof(T) == typeof(int)) { + // Promoted to Long dp = new DataPoint(timestamp, (int)(object)value, tags); } + else if (typeof(T) == typeof(long)) + { + dp = new DataPoint(timestamp, (long)(object)value, tags); + } else if (typeof(T) == typeof(double)) { dp = new DataPoint(timestamp, (double)(object)value, tags); diff --git a/src/OpenTelemetry/Metrics/DataPoint{T}.cs b/src/OpenTelemetry/Metrics/DataPoint/DataPoint{T}.cs similarity index 63% rename from src/OpenTelemetry/Metrics/DataPoint{T}.cs rename to src/OpenTelemetry/Metrics/DataPoint/DataPoint{T}.cs index cc19d1a33b1..3885283e50d 100644 --- a/src/OpenTelemetry/Metrics/DataPoint{T}.cs +++ b/src/OpenTelemetry/Metrics/DataPoint/DataPoint{T}.cs @@ -16,48 +16,32 @@ using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; namespace OpenTelemetry.Metrics { internal readonly struct DataPoint : IDataPoint where T : struct { - private readonly T value; - - private readonly DateTimeOffset timestamp; + private static readonly KeyValuePair[] EmptyTag = new KeyValuePair[0]; - private readonly KeyValuePair[] tags; + private readonly T value; internal DataPoint(DateTimeOffset timestamp, T value, KeyValuePair[] tags) { - this.timestamp = timestamp; + this.Timestamp = timestamp; + this.Tags = tags; this.value = value; - this.tags = tags; } - public KeyValuePair[] Tags + internal DataPoint(DateTimeOffset timestamp, T value) + : this(timestamp, value, DataPoint.EmptyTag) { - get - { - return this.tags; - } } - public DateTimeOffset Timestamp - { - get - { - return this.timestamp; - } - } + public DateTimeOffset Timestamp { get; } - public object Value - { - get - { - return (object)this.value; - } - } + public readonly KeyValuePair[] Tags { get; } + + public object Value => (object)this.value; } } diff --git a/src/OpenTelemetry/Metrics/DataPoint/Exemplar.cs b/src/OpenTelemetry/Metrics/DataPoint/Exemplar.cs new file mode 100644 index 00000000000..8d9e8590d82 --- /dev/null +++ b/src/OpenTelemetry/Metrics/DataPoint/Exemplar.cs @@ -0,0 +1,115 @@ +// +// 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.Metrics +{ + internal readonly struct Exemplar : IExemplar + { + private static readonly KeyValuePair[] EmptyTag = new KeyValuePair[0]; + private static readonly byte[] EmptyId = new byte[0]; + + private readonly Type valueType; + private readonly long longValue; + private readonly double doubleValue; + + internal Exemplar(DateTimeOffset timestamp, long value, byte[] spanId, byte[] traceId, KeyValuePair[] filteredTags) + { + this.Timestamp = timestamp; + this.FilteredTags = filteredTags; + this.SpanId = spanId; + this.TraceId = traceId; + this.valueType = typeof(long); + this.longValue = value; + this.doubleValue = 0; + } + + internal Exemplar(DateTimeOffset timestamp, double value, byte[] spanId, byte[] traceId, KeyValuePair[] filteredTags) + { + this.Timestamp = timestamp; + this.FilteredTags = filteredTags; + this.SpanId = spanId; + this.TraceId = traceId; + this.valueType = typeof(double); + this.longValue = 0; + this.doubleValue = value; + } + + internal Exemplar(DateTimeOffset timestamp, long value) + : this(timestamp, value, Exemplar.EmptyId, Exemplar.EmptyId, Exemplar.EmptyTag) + { + } + + internal Exemplar(DateTimeOffset timestamp, double value) + : this(timestamp, value, Exemplar.EmptyId, Exemplar.EmptyId, Exemplar.EmptyTag) + { + } + + public DateTimeOffset Timestamp { get; } + + public readonly KeyValuePair[] FilteredTags { get; } + + public readonly byte[] SpanId { get; } + + public readonly byte[] TraceId { get; } + + public object Value + { + get + { + if (this.valueType == typeof(long)) + { + return this.longValue; + } + else if (this.valueType == typeof(double)) + { + return this.doubleValue; + } + else + { + throw new Exception("Unsupported Type"); + } + } + } + + internal static Exemplar CreateExemplar(DateTimeOffset timestamp, T value, byte[] spanId, byte[] traceId, KeyValuePair[] filteredTags) + { + Exemplar dp; + + if (typeof(T) == typeof(int)) + { + // Promoted to Long + dp = new Exemplar(timestamp, (int)(object)value, spanId, traceId, filteredTags); + } + else if (typeof(T) == typeof(long)) + { + dp = new Exemplar(timestamp, (long)(object)value, spanId, traceId, filteredTags); + } + else if (typeof(T) == typeof(double)) + { + dp = new Exemplar(timestamp, (double)(object)value, spanId, traceId, filteredTags); + } + else + { + throw new Exception("Unsupported Type"); + } + + return dp; + } + } +} diff --git a/src/OpenTelemetry/Metrics/DataPoint/Exemplar{T}.cs b/src/OpenTelemetry/Metrics/DataPoint/Exemplar{T}.cs new file mode 100644 index 00000000000..50ab08f4b66 --- /dev/null +++ b/src/OpenTelemetry/Metrics/DataPoint/Exemplar{T}.cs @@ -0,0 +1,54 @@ +// +// 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.Metrics +{ + internal readonly struct Exemplar : IExemplar + where T : struct + { + private static readonly KeyValuePair[] EmptyTag = new KeyValuePair[0]; + private static readonly byte[] EmptyId = new byte[0]; + + private readonly T value; + + internal Exemplar(DateTimeOffset timestamp, T value, byte[] spanId, byte[] traceId, KeyValuePair[] filteredTags) + { + this.Timestamp = timestamp; + this.FilteredTags = filteredTags; + this.SpanId = spanId; + this.TraceId = traceId; + this.value = value; + } + + internal Exemplar(DateTimeOffset timestamp, T value) + : this(timestamp, value, Exemplar.EmptyId, Exemplar.EmptyId, Exemplar.EmptyTag) + { + } + + public DateTimeOffset Timestamp { get; } + + public readonly KeyValuePair[] FilteredTags { get; } + + public readonly byte[] SpanId { get; } + + public readonly byte[] TraceId { get; } + + public object Value => (object)this.value; + } +} diff --git a/src/OpenTelemetry/Metrics/IDataPoint.cs b/src/OpenTelemetry/Metrics/DataPoint/IDataPoint.cs similarity index 100% rename from src/OpenTelemetry/Metrics/IDataPoint.cs rename to src/OpenTelemetry/Metrics/DataPoint/IDataPoint.cs diff --git a/src/OpenTelemetry/Metrics/Aggregator.cs b/src/OpenTelemetry/Metrics/DataPoint/IExemplar.cs similarity index 66% rename from src/OpenTelemetry/Metrics/Aggregator.cs rename to src/OpenTelemetry/Metrics/DataPoint/IExemplar.cs index 471bb67bc22..3c0bf730f79 100644 --- a/src/OpenTelemetry/Metrics/Aggregator.cs +++ b/src/OpenTelemetry/Metrics/DataPoint/IExemplar.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,20 +16,19 @@ using System; using System.Collections.Generic; -using System.Linq; namespace OpenTelemetry.Metrics { - internal abstract class Aggregator + internal interface IExemplar { - internal virtual void Update(DateTimeOffset dt, T value) - where T : struct - { - } + DateTimeOffset Timestamp { get; } - internal virtual IEnumerable Collect() - { - return Enumerable.Empty(); - } + KeyValuePair[] FilteredTags { get; } + + byte[] SpanId { get; } + + byte[] TraceId { get; } + + object Value { get; } } } diff --git a/src/OpenTelemetry/Metrics/InstrumentState.cs b/src/OpenTelemetry/Metrics/InstrumentState.cs index 997faa38649..7a3722f5998 100644 --- a/src/OpenTelemetry/Metrics/InstrumentState.cs +++ b/src/OpenTelemetry/Metrics/InstrumentState.cs @@ -28,6 +28,7 @@ internal class InstrumentState internal InstrumentState(MeterProviderSdk sdk, Instrument instrument) { this.store = new AggregatorStore(sdk, instrument); + sdk.AggregatorStores.TryAdd(this.store, true); } internal void Update(DateTimeOffset dt, T value, ReadOnlySpan> tags) diff --git a/src/OpenTelemetry/Metrics/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry/Metrics/MeterProviderBuilderExtensions.cs index 3eaea341b47..a964d100990 100644 --- a/src/OpenTelemetry/Metrics/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry/Metrics/MeterProviderBuilderExtensions.cs @@ -22,48 +22,48 @@ namespace OpenTelemetry.Metrics public static class MeterProviderBuilderExtensions { /// - /// Sets observation period. + /// Sets default collection period. /// /// . /// Perion in milliseconds. /// . - public static MeterProviderBuilder SetObservationPeriod(this MeterProviderBuilder meterProviderBuilder, int periodMilliseconds) + public static MeterProviderBuilder SetDefaultCollectionPeriod(this MeterProviderBuilder meterProviderBuilder, int periodMilliseconds) { if (meterProviderBuilder is MeterProviderBuilderSdk meterProviderBuilderSdk) { - return meterProviderBuilderSdk.SetObservationPeriod(periodMilliseconds); + return meterProviderBuilderSdk.SetDefaultCollectionPeriod(periodMilliseconds); } return meterProviderBuilder; } /// - /// Sets collection period. + /// Add measurement processor. /// /// . - /// Perion in milliseconds. + /// Measurement Processors. /// . - public static MeterProviderBuilder SetCollectionPeriod(this MeterProviderBuilder meterProviderBuilder, int periodMilliseconds) + public static MeterProviderBuilder AddProcessor(this MeterProviderBuilder meterProviderBuilder, MeasurementProcessor processor) { if (meterProviderBuilder is MeterProviderBuilderSdk meterProviderBuilderSdk) { - return meterProviderBuilderSdk.SetCollectionPeriod(periodMilliseconds); + return meterProviderBuilderSdk.AddMeasurementProcessor(processor); } return meterProviderBuilder; } /// - /// Add measurement processor. + /// Add export processor. /// /// . /// Measurement Processors. /// . - public static MeterProviderBuilder AddProcessor(this MeterProviderBuilder meterProviderBuilder, MeasurementProcessor processor) + public static MeterProviderBuilder AddExportProcessor(this MeterProviderBuilder meterProviderBuilder, MetricProcessor processor) { if (meterProviderBuilder is MeterProviderBuilderSdk meterProviderBuilderSdk) { - return meterProviderBuilderSdk.AddMeasurementProcessor(processor); + return meterProviderBuilderSdk.AddExporter(processor); } return meterProviderBuilder; @@ -74,12 +74,13 @@ public static MeterProviderBuilder AddProcessor(this MeterProviderBuilder meterP /// /// . /// Measurement Processors. + /// Period in milliseconds between Collections. /// . - public static MeterProviderBuilder AddExportProcessor(this MeterProviderBuilder meterProviderBuilder, MetricProcessor processor) + public static MeterProviderBuilder AddExportProcessor(this MeterProviderBuilder meterProviderBuilder, MetricProcessor processor, int collectionPeriodMilliseconds) { if (meterProviderBuilder is MeterProviderBuilderSdk meterProviderBuilderSdk) { - return meterProviderBuilderSdk.AddExporter(processor); + return meterProviderBuilderSdk.AddExporter(processor, collectionPeriodMilliseconds); } return meterProviderBuilder; diff --git a/src/OpenTelemetry/Metrics/MeterProviderBuilderSdk.cs b/src/OpenTelemetry/Metrics/MeterProviderBuilderSdk.cs index 289b1f45a55..1801805d0d5 100644 --- a/src/OpenTelemetry/Metrics/MeterProviderBuilderSdk.cs +++ b/src/OpenTelemetry/Metrics/MeterProviderBuilderSdk.cs @@ -22,8 +22,7 @@ namespace OpenTelemetry.Metrics internal class MeterProviderBuilderSdk : MeterProviderBuilder { private readonly List meterSources = new List(); - private int observationPeriodMilliseconds = 1000; - private int collectionPeriodMilliseconds = 1000; + private int defaultCollectionPeriodMilliseconds = 1000; internal MeterProviderBuilderSdk() { @@ -31,7 +30,7 @@ internal MeterProviderBuilderSdk() internal List MeasurementProcessors { get; } = new List(); - internal List ExportProcessors { get; } = new List(); + internal List> ExportProcessors { get; } = new List>(); public override MeterProviderBuilder AddSource(params string[] names) { @@ -53,37 +52,34 @@ public override MeterProviderBuilder AddSource(params string[] names) return this; } - internal MeterProviderBuilderSdk SetObservationPeriod(int periodMilliseconds) + internal MeterProviderBuilderSdk SetDefaultCollectionPeriod(int periodMilliseconds) { - this.observationPeriodMilliseconds = periodMilliseconds; + this.defaultCollectionPeriodMilliseconds = periodMilliseconds; return this; } - internal MeterProviderBuilderSdk SetCollectionPeriod(int periodMilliseconds) + internal MeterProviderBuilderSdk AddMeasurementProcessor(MeasurementProcessor processor) { - this.collectionPeriodMilliseconds = periodMilliseconds; + this.MeasurementProcessors.Add(processor); return this; } - internal MeterProviderBuilderSdk AddMeasurementProcessor(MeasurementProcessor processor) + internal MeterProviderBuilderSdk AddExporter(MetricProcessor processor) { - this.MeasurementProcessors.Add(processor); + this.ExportProcessors.Add(new KeyValuePair(processor, this.defaultCollectionPeriodMilliseconds)); return this; } - internal MeterProviderBuilderSdk AddExporter(MetricProcessor processor) + internal MeterProviderBuilderSdk AddExporter(MetricProcessor processor, int periodMilliseconds) { - this.ExportProcessors.Add(processor); + this.ExportProcessors.Add(new KeyValuePair(processor, periodMilliseconds)); return this; } internal MeterProvider Build() { - // TODO: Need to review using a struct for BuildOptions return new MeterProviderSdk( this.meterSources, - this.observationPeriodMilliseconds, - this.collectionPeriodMilliseconds, this.MeasurementProcessors.ToArray(), this.ExportProcessors.ToArray()); } diff --git a/src/OpenTelemetry/Metrics/MeterProviderSdk.cs b/src/OpenTelemetry/Metrics/MeterProviderSdk.cs index ad546cf01c0..59e7ccc87d0 100644 --- a/src/OpenTelemetry/Metrics/MeterProviderSdk.cs +++ b/src/OpenTelemetry/Metrics/MeterProviderSdk.cs @@ -15,8 +15,10 @@ // using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.Metrics; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -26,12 +28,13 @@ namespace OpenTelemetry.Metrics public class MeterProviderSdk : MeterProvider { + internal readonly ConcurrentDictionary AggregatorStores = new ConcurrentDictionary(); + private static int lastTick = -1; private static DateTimeOffset lastTimestamp = DateTimeOffset.MinValue; private readonly CancellationTokenSource cts = new CancellationTokenSource(); - private readonly Task observerTask; - private readonly Task collectorTask; + private readonly List collectorTasks = new List(); private readonly MeterListener listener; private readonly object lockInstrumentStates = new object(); @@ -39,20 +42,13 @@ public class MeterProviderSdk internal MeterProviderSdk( IEnumerable meterSources, - int observationPeriodMilliseconds, - int collectionPeriodMilliseconds, MeasurementProcessor[] measurementProcessors, - MetricProcessor[] metricExportProcessors) + KeyValuePair[] metricExportProcessors) { - this.ObservationPeriodMilliseconds = observationPeriodMilliseconds; - this.CollectionPeriodMilliseconds = collectionPeriodMilliseconds; - // Setup our Processors this.MeasurementProcessors.AddRange(measurementProcessors); - this.AggregateProcessors.Add(new AggregateProcessor()); - this.ExportProcessors.AddRange(metricExportProcessors); // Setup Listener @@ -95,21 +91,20 @@ internal MeterProviderSdk( // Start our long running Task var token = this.cts.Token; - this.observerTask = Task.Run(async () => await this.ObserverTask(token)); - this.collectorTask = Task.Run(async () => await this.CollectorTask(token)); - } - internal int ObservationPeriodMilliseconds { get; } = 1000; - - internal int CollectionPeriodMilliseconds { get; } = 1000; + // Group Export processors by their collectionPeriod. + var groups = this.ExportProcessors.GroupBy(k => k.Value, v => v.Key); + foreach (var group in groups) + { + this.collectorTasks.Add(Task.Run(async () => await this.CollectorTask(token, group.Key, group.ToArray()))); + } + } internal List MeasurementProcessors { get; } = new List(); - internal List AggregateProcessors { get; } = new List(); - internal List MetricProcessors { get; } = new List(); - internal List ExportProcessors { get; } = new List(); + internal List> ExportProcessors { get; } = new List>(); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static DateTimeOffset GetDateTimeOffset() @@ -155,18 +150,12 @@ internal void MeasurementRecorded(Instrument instrument, T value, ReadOnlySpa var val = value; // Run Pre Aggregator Processors - foreach (var processor in this.MeasurementProcessors) { processor.OnEnd(measurementItem, ref dt, ref val, ref tags); } - // Run Aggregator Processors - - foreach (var processor in this.AggregateProcessors) - { - processor.OnEnd(measurementItem, ref dt, ref val, ref tags); - } + instrumentState.Update(dt, val, tags); } protected override void Dispose(bool disposing) @@ -175,52 +164,38 @@ protected override void Dispose(bool disposing) this.cts.Cancel(); - this.observerTask.Wait(); - - this.collectorTask.Wait(); - - this.Collect(); - } - - private async Task ObserverTask(CancellationToken token) - { - while (!token.IsCancellationRequested) + foreach (var collectorTask in this.collectorTasks) { - try - { - await Task.Delay(this.ObservationPeriodMilliseconds, token); - } - catch (TaskCanceledException) - { - } - - this.listener.RecordObservableInstruments(); + collectorTask.Wait(); } } - private async Task CollectorTask(CancellationToken token) + private async Task CollectorTask(CancellationToken token, int collectionPeriodMilliseconds, MetricProcessor[] processors) { while (!token.IsCancellationRequested) { try { - await Task.Delay(this.CollectionPeriodMilliseconds, token); + await Task.Delay(collectionPeriodMilliseconds, token); } catch (TaskCanceledException) { } - this.Collect(); + this.Collect(collectionPeriodMilliseconds, processors); } } - private void Collect() + private void Collect(int collectionPeriodMilliseconds, MetricProcessor[] processors) { + // Record all observable instruments + this.listener.RecordObservableInstruments(); + var metricItem = new MetricItem(); - foreach (var processor in this.AggregateProcessors) + foreach (var kv in this.AggregatorStores) { - var metrics = processor.Collect(); + var metrics = kv.Key.Collect(collectionPeriodMilliseconds); metricItem.Metrics.AddRange(metrics); } @@ -229,7 +204,7 @@ private void Collect() processor.OnEnd(metricItem); } - foreach (var processor in this.ExportProcessors) + foreach (var processor in processors) { processor.OnEnd(metricItem); } diff --git a/src/OpenTelemetry/Metrics/MetricAggregators/GaugeMetricAggregator.cs b/src/OpenTelemetry/Metrics/MetricAggregators/GaugeMetricAggregator.cs new file mode 100644 index 00000000000..8bf0fbfb582 --- /dev/null +++ b/src/OpenTelemetry/Metrics/MetricAggregators/GaugeMetricAggregator.cs @@ -0,0 +1,112 @@ +// +// 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.Metrics +{ + internal class GaugeMetricAggregator : IGaugeMetric, IAggregator + { + private readonly object lockUpdate = new object(); + private Type valueType; + private long longValue; + private double doubleValue; + + internal GaugeMetricAggregator(string name, DateTimeOffset startTimeExclusive, KeyValuePair[] attributes) + { + this.Name = name; + this.StartTimeExclusive = startTimeExclusive; + this.EndTimeInclusive = startTimeExclusive; + this.Attributes = attributes; + } + + public string Name { get; private set; } + + public DateTimeOffset StartTimeExclusive { get; private set; } + + public DateTimeOffset EndTimeInclusive { get; private set; } + + public KeyValuePair[] Attributes { get; private set; } + + public IEnumerable Exemplars { get; private set; } = new List(); + + public IDataPoint LastValue + { + get + { + if (this.valueType == typeof(long)) + { + return DataPoint.CreateDataPoint(this.EndTimeInclusive, this.longValue, this.Attributes); + } + else if (this.valueType == typeof(double)) + { + return DataPoint.CreateDataPoint(this.EndTimeInclusive, this.doubleValue, this.Attributes); + } + else + { + throw new Exception("Unsupported Type"); + } + } + } + + public void Update(DateTimeOffset dt, T value) + where T : struct + { + lock (this.lockUpdate) + { + this.EndTimeInclusive = dt; + if (typeof(T) == typeof(int)) + { + // Promote to Long + this.valueType = typeof(long); + this.longValue = (int)(object)value; + } + else if (typeof(T) == typeof(long)) + { + this.valueType = typeof(T); + this.longValue = (long)(object)value; + } + else if (typeof(T) == typeof(double)) + { + this.valueType = typeof(T); + this.doubleValue = (double)(object)value; + } + } + } + + public IMetric Collect(DateTimeOffset dt) + { + var cloneItem = new GaugeMetricAggregator(this.Name, this.StartTimeExclusive, this.Attributes); + + lock (this.lockUpdate) + { + cloneItem.Exemplars = this.Exemplars; + cloneItem.EndTimeInclusive = dt; + cloneItem.valueType = this.valueType; + cloneItem.longValue = this.longValue; + cloneItem.doubleValue = this.doubleValue; + } + + return cloneItem; + } + + public string ToDisplayString() + { + return $"Last={this.LastValue.Value}"; + } + } +} diff --git a/src/OpenTelemetry/Metrics/MetricAggregators/HistogramBucket.cs b/src/OpenTelemetry/Metrics/MetricAggregators/HistogramBucket.cs new file mode 100644 index 00000000000..4c972d7e6ea --- /dev/null +++ b/src/OpenTelemetry/Metrics/MetricAggregators/HistogramBucket.cs @@ -0,0 +1,25 @@ +// +// 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 +{ + internal struct HistogramBucket + { + internal double LowBoundary; + internal double HighBoundary; + internal long Count; + } +} diff --git a/src/OpenTelemetry/Metrics/MetricAggregators/HistogramMetricAggregator.cs b/src/OpenTelemetry/Metrics/MetricAggregators/HistogramMetricAggregator.cs new file mode 100644 index 00000000000..0d819d32a8e --- /dev/null +++ b/src/OpenTelemetry/Metrics/MetricAggregators/HistogramMetricAggregator.cs @@ -0,0 +1,97 @@ +// +// 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.Metrics +{ + internal class HistogramMetricAggregator : IHistogramMetric, IAggregator + { + private readonly object lockUpdate = new object(); + private List buckets = new List(); + + internal HistogramMetricAggregator(string name, DateTimeOffset startTimeExclusive, KeyValuePair[] attributes, bool isDelta) + { + this.Name = name; + this.StartTimeExclusive = startTimeExclusive; + this.EndTimeInclusive = startTimeExclusive; + this.Attributes = attributes; + this.IsDeltaTemporality = isDelta; + } + + public string Name { get; private set; } + + public DateTimeOffset StartTimeExclusive { get; private set; } + + public DateTimeOffset EndTimeInclusive { get; private set; } + + public KeyValuePair[] Attributes { get; private set; } + + public bool IsDeltaTemporality { get; } + + public IEnumerable Exemplars { get; private set; } = new List(); + + public long PopulationCount { get; private set; } + + public double PopulationSum { get; private set; } + + public IEnumerable Buckets => this.buckets; + + public void Update(DateTimeOffset dt, T value) + where T : struct + { + // TODO: Implement Histogram! + + lock (this.lockUpdate) + { + this.EndTimeInclusive = dt; + this.PopulationCount++; + } + } + + public IMetric Collect(DateTimeOffset dt) + { + if (this.PopulationCount == 0) + { + // TODO: Output stale markers + return null; + } + + var cloneItem = new HistogramMetricAggregator(this.Name, this.StartTimeExclusive, this.Attributes, this.IsDeltaTemporality); + + lock (this.lockUpdate) + { + cloneItem.Exemplars = this.Exemplars; + cloneItem.EndTimeInclusive = dt; + cloneItem.PopulationCount = this.PopulationCount; + cloneItem.PopulationSum = this.PopulationSum; + cloneItem.buckets = this.buckets; + + this.StartTimeExclusive = dt; + this.PopulationCount = 0; + this.PopulationSum = 0; + } + + return cloneItem; + } + + public string ToDisplayString() + { + return $"Count={this.PopulationCount},Sum={this.PopulationSum}"; + } + } +} diff --git a/src/OpenTelemetry/Metrics/MetricAggregators/IAggregator.cs b/src/OpenTelemetry/Metrics/MetricAggregators/IAggregator.cs new file mode 100644 index 00000000000..21330af0316 --- /dev/null +++ b/src/OpenTelemetry/Metrics/MetricAggregators/IAggregator.cs @@ -0,0 +1,28 @@ +// +// 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.Metrics +{ + internal interface IAggregator + { + void Update(DateTimeOffset dt, T value) + where T : struct; + + IMetric Collect(DateTimeOffset dt); + } +} diff --git a/src/OpenTelemetry/Metrics/MetricAggregators/IGaugeMetric.cs b/src/OpenTelemetry/Metrics/MetricAggregators/IGaugeMetric.cs new file mode 100644 index 00000000000..aaaeefaffbc --- /dev/null +++ b/src/OpenTelemetry/Metrics/MetricAggregators/IGaugeMetric.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.Collections.Generic; + +namespace OpenTelemetry.Metrics +{ + internal interface IGaugeMetric : IMetric + { + IEnumerable Exemplars { get; } + + IDataPoint LastValue { get; } + } +} diff --git a/src/OpenTelemetry/Metrics/MetricAggregators/IHistogramMetric.cs b/src/OpenTelemetry/Metrics/MetricAggregators/IHistogramMetric.cs new file mode 100644 index 00000000000..ce205229513 --- /dev/null +++ b/src/OpenTelemetry/Metrics/MetricAggregators/IHistogramMetric.cs @@ -0,0 +1,33 @@ +// +// 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; + +namespace OpenTelemetry.Metrics +{ + internal interface IHistogramMetric : IMetric + { + bool IsDeltaTemporality { get; } + + IEnumerable Exemplars { get; } + + long PopulationCount { get; } + + double PopulationSum { get; } + + IEnumerable Buckets { get; } + } +} diff --git a/src/OpenTelemetry/Metrics/Metric.cs b/src/OpenTelemetry/Metrics/MetricAggregators/IMetric.cs similarity index 64% rename from src/OpenTelemetry/Metrics/Metric.cs rename to src/OpenTelemetry/Metrics/MetricAggregators/IMetric.cs index c3b89808386..e8d7d56dc18 100644 --- a/src/OpenTelemetry/Metrics/Metric.cs +++ b/src/OpenTelemetry/Metrics/MetricAggregators/IMetric.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,21 +16,19 @@ using System; using System.Collections.Generic; -using System.Diagnostics.Metrics; namespace OpenTelemetry.Metrics { - // TODO: Need to determine what a Metric actually contains - - internal struct Metric + internal interface IMetric { - internal readonly string Name; - internal IDataPoint Point; + string Name { get; } + + DateTimeOffset StartTimeExclusive { get; } + + DateTimeOffset EndTimeInclusive { get; } + + KeyValuePair[] Attributes { get; } - internal Metric(string name, IDataPoint point) - { - this.Name = name; - this.Point = point; - } + string ToDisplayString(); } } diff --git a/src/OpenTelemetry/Metrics/MetricAggregators/ISumMetric.cs b/src/OpenTelemetry/Metrics/MetricAggregators/ISumMetric.cs new file mode 100644 index 00000000000..bf6514f4c5e --- /dev/null +++ b/src/OpenTelemetry/Metrics/MetricAggregators/ISumMetric.cs @@ -0,0 +1,31 @@ +// +// 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; + +namespace OpenTelemetry.Metrics +{ + internal interface ISumMetric : IMetric + { + bool IsDeltaTemporality { get; } + + bool IsMonotonic { get; } + + IEnumerable Exemplars { get; } + + object Sum { get; } + } +} diff --git a/src/OpenTelemetry/Metrics/MetricAggregators/ISummaryMetric.cs b/src/OpenTelemetry/Metrics/MetricAggregators/ISummaryMetric.cs new file mode 100644 index 00000000000..f36f23171be --- /dev/null +++ b/src/OpenTelemetry/Metrics/MetricAggregators/ISummaryMetric.cs @@ -0,0 +1,29 @@ +// +// 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; + +namespace OpenTelemetry.Metrics +{ + internal interface ISummaryMetric : IMetric + { + long PopulationCount { get; } + + double PopulationSum { get; } + + IEnumerable Quantiles { get; } + } +} diff --git a/src/OpenTelemetry/Metrics/MetricAggregators/SumMetricAggregator.cs b/src/OpenTelemetry/Metrics/MetricAggregators/SumMetricAggregator.cs new file mode 100644 index 00000000000..de2f0ca24f9 --- /dev/null +++ b/src/OpenTelemetry/Metrics/MetricAggregators/SumMetricAggregator.cs @@ -0,0 +1,187 @@ +// +// 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.Metrics +{ + internal class SumMetricAggregator : ISumMetric, IAggregator + { + private readonly object lockUpdate = new object(); + private Type valueType; + private long sumPos = 0; + private double dsumPos = 0; + private long countPos = 0; + private long sumNeg = 0; + private double dsumNeg = 0; + private long countNeg = 0; + + internal SumMetricAggregator(string name, DateTimeOffset startTimeExclusive, KeyValuePair[] attributes, bool isDelta, bool isMonotonic) + { + this.Name = name; + this.StartTimeExclusive = startTimeExclusive; + this.EndTimeInclusive = startTimeExclusive; + this.Attributes = attributes; + this.IsDeltaTemporality = isDelta; + this.IsMonotonic = isMonotonic; + } + + public string Name { get; private set; } + + public DateTimeOffset StartTimeExclusive { get; private set; } + + public DateTimeOffset EndTimeInclusive { get; private set; } + + public KeyValuePair[] Attributes { get; private set; } + + public bool IsDeltaTemporality { get; } + + public bool IsMonotonic { get; } + + public IEnumerable Exemplars { get; private set; } = new List(); + + public object Sum + { + get + { + if (this.valueType == typeof(long)) + { + if (this.IsMonotonic) + { + return this.sumPos + (long)this.dsumPos; + } + else + { + return this.sumPos + (long)this.dsumPos + this.sumNeg + (long)this.dsumNeg; + } + } + else if (this.valueType == typeof(double)) + { + if (this.IsMonotonic) + { + return this.dsumPos + (double)this.sumPos; + } + else + { + return this.dsumPos + (double)this.sumPos + this.dsumNeg + (double)this.sumNeg; + } + } + + throw new Exception("Unsupported Type"); + } + } + + public void Update(DateTimeOffset dt, T value) + where T : struct + { + lock (this.lockUpdate) + { + this.EndTimeInclusive = dt; + + if (typeof(T) == typeof(int)) + { + // Promote to Long + this.valueType = typeof(long); + var val = (long)(int)(object)value; + + if (val >= 0) + { + this.sumPos += val; + this.countPos++; + } + else + { + this.sumNeg += val; + this.countNeg++; + } + } + else if (typeof(T) == typeof(long)) + { + this.valueType = typeof(T); + var val = (long)(object)value; + + if (val >= 0) + { + this.sumPos += val; + this.countPos++; + } + else + { + this.sumNeg += val; + this.countNeg++; + } + } + else if (typeof(T) == typeof(double)) + { + this.valueType = typeof(T); + var val = (double)(object)value; + + if (val >= 0) + { + this.dsumPos += val; + this.countPos++; + } + else + { + this.dsumNeg += val; + this.countNeg++; + } + } + else + { + throw new Exception("Unsupported Type"); + } + } + } + + public IMetric Collect(DateTimeOffset dt) + { + var cloneItem = new SumMetricAggregator(this.Name, this.StartTimeExclusive, this.Attributes, this.IsDeltaTemporality, this.IsMonotonic); + + lock (this.lockUpdate) + { + cloneItem.Exemplars = this.Exemplars; + cloneItem.EndTimeInclusive = dt; + cloneItem.valueType = this.valueType; + cloneItem.countPos = this.countPos; + cloneItem.sumPos = this.sumPos; + cloneItem.dsumPos = this.dsumPos; + cloneItem.countNeg = this.countNeg; + cloneItem.sumNeg = this.sumNeg; + cloneItem.dsumNeg = this.dsumNeg; + + if (this.IsDeltaTemporality) + { + this.StartTimeExclusive = dt; + this.countPos = 0; + this.sumPos = 0; + this.dsumPos = 0; + this.countNeg = 0; + this.sumNeg = 0; + this.dsumNeg = 0; + } + } + + return cloneItem; + } + + public string ToDisplayString() + { + return $"Delta={this.IsDeltaTemporality},Count={this.countPos},Sum={this.Sum}"; + } + } +} diff --git a/src/OpenTelemetry/Metrics/MetricAggregators/SummaryMetricAggregator.cs b/src/OpenTelemetry/Metrics/MetricAggregators/SummaryMetricAggregator.cs new file mode 100644 index 00000000000..d003e0ad8d0 --- /dev/null +++ b/src/OpenTelemetry/Metrics/MetricAggregators/SummaryMetricAggregator.cs @@ -0,0 +1,122 @@ +// +// 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.Metrics +{ + internal class SummaryMetricAggregator : ISummaryMetric, IAggregator + { + private readonly object lockUpdate = new object(); + + private List quantiles = new List(); + + internal SummaryMetricAggregator(string name, DateTimeOffset startTimeExclusive, KeyValuePair[] attributes, bool isMonotonic) + { + this.Name = name; + this.StartTimeExclusive = startTimeExclusive; + this.EndTimeInclusive = startTimeExclusive; + this.Attributes = attributes; + this.IsMonotonic = isMonotonic; + } + + public string Name { get; private set; } + + public DateTimeOffset StartTimeExclusive { get; private set; } + + public DateTimeOffset EndTimeInclusive { get; private set; } + + public KeyValuePair[] Attributes { get; private set; } + + public bool IsMonotonic { get; } + + public long PopulationCount { get; private set; } + + public double PopulationSum { get; private set; } + + public IEnumerable Quantiles => this.quantiles; + + public void Update(DateTimeOffset dt, T value) + where T : struct + { + // TODO: Implement Summary! + + lock (this.lockUpdate) + { + this.EndTimeInclusive = dt; + + if (typeof(T) == typeof(int)) + { + var val = (int)(object)value; + if (val > 0 || !this.IsMonotonic) + { + this.PopulationSum += (double)val; + this.PopulationCount++; + } + } + else if (typeof(T) == typeof(long)) + { + var val = (long)(object)value; + if (val > 0 || !this.IsMonotonic) + { + this.PopulationSum += (double)val; + this.PopulationCount++; + } + } + else if (typeof(T) == typeof(double)) + { + var val = (double)(object)value; + if (val > 0 || !this.IsMonotonic) + { + this.PopulationSum += (double)val; + this.PopulationCount++; + } + } + } + } + + public IMetric Collect(DateTimeOffset dt) + { + if (this.PopulationCount == 0) + { + // TODO: Output stale markers + return null; + } + + var cloneItem = new SummaryMetricAggregator(this.Name, this.StartTimeExclusive, this.Attributes, this.IsMonotonic); + + lock (this.lockUpdate) + { + cloneItem.EndTimeInclusive = dt; + cloneItem.PopulationCount = this.PopulationCount; + cloneItem.PopulationSum = this.PopulationSum; + cloneItem.quantiles = this.quantiles; + + this.StartTimeExclusive = dt; + this.PopulationCount = 0; + this.PopulationSum = 0; + } + + return cloneItem; + } + + public string ToDisplayString() + { + return $"Count={this.PopulationCount},Sum={this.PopulationSum}"; + } + } +} diff --git a/src/OpenTelemetry/Metrics/MetricAggregators/ValueAtQuantile.cs b/src/OpenTelemetry/Metrics/MetricAggregators/ValueAtQuantile.cs new file mode 100644 index 00000000000..7b757cbd2df --- /dev/null +++ b/src/OpenTelemetry/Metrics/MetricAggregators/ValueAtQuantile.cs @@ -0,0 +1,24 @@ +// +// 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 +{ + internal struct ValueAtQuantile + { + internal double Quantile; + internal double Value; + } +} diff --git a/src/OpenTelemetry/Metrics/Processors/AggregateProcessor.cs b/src/OpenTelemetry/Metrics/Processors/AggregateProcessor.cs deleted file mode 100644 index 9e905e2aaab..00000000000 --- a/src/OpenTelemetry/Metrics/Processors/AggregateProcessor.cs +++ /dev/null @@ -1,51 +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.Concurrent; -using System.Collections.Generic; -using System.Diagnostics.Metrics; - -namespace OpenTelemetry.Metrics -{ - internal class AggregateProcessor : MeasurementProcessor - { - internal ConcurrentDictionary AggregatorStores { get; } = new ConcurrentDictionary(); - - internal override void OnEnd(MeasurementItem measurementItem, ref DateTimeOffset dt, ref T value, ref ReadOnlySpan> tags) - where T : struct - { - measurementItem.State.Update(dt, value, tags); - } - - internal void Register(AggregatorStore store) - { - this.AggregatorStores.TryAdd(store, true); - } - - internal IEnumerable Collect() - { - var metrics = new List(); - - foreach (var kv in this.AggregatorStores) - { - metrics.AddRange(kv.Key.Collect()); - } - - return metrics.ToArray(); - } - } -} diff --git a/src/OpenTelemetry/Metrics/Processors/MetricConsoleExporter.cs b/src/OpenTelemetry/Metrics/Processors/MetricConsoleExporter.cs index 4555b396c93..fe718669850 100644 --- a/src/OpenTelemetry/Metrics/Processors/MetricConsoleExporter.cs +++ b/src/OpenTelemetry/Metrics/Processors/MetricConsoleExporter.cs @@ -21,13 +21,26 @@ namespace OpenTelemetry.Metrics { public class MetricConsoleExporter : MetricProcessor { + private string name; + + public MetricConsoleExporter(string name) + { + this.name = name; + } + public override void OnEnd(MetricItem data) { foreach (var metric in data.Metrics) { - var tags = metric.Point?.Tags.ToArray().Select(k => $"{k.Key}={k.Value?.ToString()}"); - var msg = $"{metric.Name}[{string.Join(";", tags)}] = {metric.Point?.Value.ToString()}"; - Console.WriteLine($"Export: {msg}"); + var tags = metric.Attributes.ToArray().Select(k => $"{k.Key}={k.Value?.ToString()}"); + + var kind = metric.GetType().Name; + var value = metric.ToDisplayString(); + + string time = $"{metric.StartTimeExclusive.ToLocalTime().ToString("HH:mm:ss.fff")} {metric.EndTimeInclusive.ToLocalTime().ToString("HH:mm:ss.fff")}"; + + var msg = $"Export[{this.name}] {time} {metric.Name} [{string.Join(";", tags)}] {kind} {value}"; + Console.WriteLine(msg); } } } diff --git a/src/OpenTelemetry/Metrics/Processors/MetricItem.cs b/src/OpenTelemetry/Metrics/Processors/MetricItem.cs index c317321ee60..1f06d33e0de 100644 --- a/src/OpenTelemetry/Metrics/Processors/MetricItem.cs +++ b/src/OpenTelemetry/Metrics/Processors/MetricItem.cs @@ -14,16 +14,13 @@ // limitations under the License. // -using System; -using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics.Metrics; namespace OpenTelemetry.Metrics { public class MetricItem { - internal List Metrics = new List(); + internal List Metrics = new List(); internal MetricItem() { diff --git a/test/Benchmarks/Metrics/MetricsBenchmarks.cs b/test/Benchmarks/Metrics/MetricsBenchmarks.cs index 634b62bddb1..94f57056bdd 100644 --- a/test/Benchmarks/Metrics/MetricsBenchmarks.cs +++ b/test/Benchmarks/Metrics/MetricsBenchmarks.cs @@ -27,16 +27,17 @@ [Host] : .NET Core 3.1.13 (CoreCLR 4.700.21.11102, CoreFX 4.700.21.11602), X64 RyuJIT DefaultJob : .NET Core 3.1.13 (CoreCLR 4.700.21.11102, CoreFX 4.700.21.11602), X64 RyuJIT -| Method | WithSDK | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | -|-------------------------- |-------- |-----------:|-----------:|-----------:|-------:|------:|------:|----------:| -| CounterHotPath | False | 13.291 ns | 0.0867 ns | 0.0769 ns | - | - | - | - | -| CounterWith1LabelsHotPath | False | 8.777 ns | 0.1995 ns | 0.2523 ns | - | - | - | - | -| CounterWith3LabelsHotPath | False | 22.326 ns | 0.1740 ns | 0.1453 ns | - | - | - | - | -| CounterWith5LabelsHotPath | False | 31.854 ns | 0.5290 ns | 0.4949 ns | 0.0249 | - | - | 104 B | -| CounterHotPath | True | 111.240 ns | 2.0913 ns | 1.7464 ns | - | - | - | - | -| CounterWith1LabelsHotPath | True | 170.970 ns | 3.4123 ns | 3.1919 ns | - | - | - | - | -| CounterWith3LabelsHotPath | True | 478.776 ns | 2.8576 ns | 2.3862 ns | - | - | - | - | -| CounterWith5LabelsHotPath | True | 614.775 ns | 11.6086 ns | 12.4211 ns | 0.0553 | - | - | 232 B | + +| Method | WithSDK | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | +|-------------------------- |-------- |-----------:|-----------:|----------:|-------:|------:|------:|----------:| +| CounterHotPath | False | 15.126 ns | 0.3228 ns | 0.3965 ns | - | - | - | - | +| CounterWith1LabelsHotPath | False | 9.766 ns | 0.2268 ns | 0.3530 ns | - | - | - | - | +| CounterWith3LabelsHotPath | False | 25.240 ns | 0.2876 ns | 0.2690 ns | - | - | - | - | +| CounterWith5LabelsHotPath | False | 37.929 ns | 0.7512 ns | 0.5865 ns | 0.0249 | - | - | 104 B | +| CounterHotPath | True | 44.790 ns | 0.9101 ns | 1.3621 ns | - | - | - | - | +| CounterWith1LabelsHotPath | True | 115.023 ns | 2.1001 ns | 1.9644 ns | - | - | - | - | +| CounterWith3LabelsHotPath | True | 436.527 ns | 6.5121 ns | 5.7728 ns | - | - | - | - | +| CounterWith5LabelsHotPath | True | 586.498 ns | 11.4783 ns | 9.5849 ns | 0.0553 | - | - | 232 B | BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19043 @@ -44,16 +45,17 @@ [Host] : .NET Framework 4.8 (4.8.4360.0), X64 RyuJIT DefaultJob : .NET Framework 4.8 (4.8.4360.0), X64 RyuJIT + | Method | WithSDK | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | |-------------------------- |-------- |------------:|----------:|----------:|-------:|------:|------:|----------:| -| CounterHotPath | False | 21.66 ns | 0.304 ns | 0.270 ns | - | - | - | - | -| CounterWith1LabelsHotPath | False | 26.03 ns | 0.154 ns | 0.136 ns | - | - | - | - | -| CounterWith3LabelsHotPath | False | 42.22 ns | 0.252 ns | 0.223 ns | - | - | - | - | -| CounterWith5LabelsHotPath | False | 46.35 ns | 0.890 ns | 0.953 ns | 0.0249 | - | - | 104 B | -| CounterHotPath | True | 143.95 ns | 1.215 ns | 1.014 ns | - | - | - | - | -| CounterWith1LabelsHotPath | True | 224.92 ns | 4.491 ns | 8.760 ns | - | - | - | - | -| CounterWith3LabelsHotPath | True | 905.84 ns | 6.651 ns | 5.193 ns | - | - | - | - | -| CounterWith5LabelsHotPath | True | 1,898.68 ns | 35.805 ns | 36.770 ns | 0.0553 | - | - | 233 B | +| CounterHotPath | False | 23.53 ns | 0.480 ns | 0.401 ns | - | - | - | - | +| CounterWith1LabelsHotPath | False | 28.70 ns | 0.592 ns | 0.770 ns | - | - | - | - | +| CounterWith3LabelsHotPath | False | 46.27 ns | 0.942 ns | 1.157 ns | - | - | - | - | +| CounterWith5LabelsHotPath | False | 51.66 ns | 1.060 ns | 1.857 ns | 0.0249 | - | - | 104 B | +| CounterHotPath | True | 70.44 ns | 1.029 ns | 0.912 ns | - | - | - | - | +| CounterWith1LabelsHotPath | True | 151.92 ns | 3.067 ns | 3.651 ns | - | - | - | - | +| CounterWith3LabelsHotPath | True | 876.20 ns | 15.920 ns | 14.892 ns | - | - | - | - | +| CounterWith5LabelsHotPath | True | 1,973.64 ns | 38.393 ns | 45.705 ns | 0.0534 | - | - | 233 B | */ namespace Benchmarks.Metrics @@ -81,8 +83,7 @@ public void Setup() { this.provider = Sdk.CreateMeterProviderBuilder() .AddSource("TestMeter") // All instruments from this meter are enabled. - .SetObservationPeriod(10000) - .SetCollectionPeriod(10000) + .SetDefaultCollectionPeriod(10000) // .AddExportProcessor(new MetricConsoleExporter()) .Build(); diff --git a/test/OpenTelemetry.Tests/Metrics/MetricAPITest.cs b/test/OpenTelemetry.Tests/Metrics/MetricAPITest.cs index 080e9a922fc..f9c0e1c6992 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricAPITest.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricAPITest.cs @@ -30,17 +30,34 @@ public void SimpleTest() { using var provider = Sdk.CreateMeterProviderBuilder() .AddSource("BasicAllTest") - .SetObservationPeriod(300) - .SetCollectionPeriod(1000) + .SetDefaultCollectionPeriod(1000) .AddProcessor(new TagEnrichmentProcessor("newAttrib", "newAttribValue")) - .AddExportProcessor(new MetricConsoleExporter()) + .AddExportProcessor(new MetricConsoleExporter("Test1")) + .AddExportProcessor(new MetricConsoleExporter("Test2"), 2000) .Build(); using var meter = new Meter("BasicAllTest", "0.0.1"); - var counter = meter.CreateCounter("counter1"); + var counter = meter.CreateCounter("counter"); - var observableCounter = meter.CreateObservableGauge("MemoryUsage", () => + var histogram = meter.CreateHistogram("histogram"); + + var observableCounter = meter.CreateObservableCounter("asynccounter", () => + { + return new List>() + { + new Measurement( + 30, + new KeyValuePair("attrb1", "value1")), + + new Measurement( + 40, + new KeyValuePair("label1", "value1"), + new KeyValuePair("label2", "value2")), + }; + }); + + var observableGauge = meter.CreateObservableGauge("asyncgauge", () => { return new List>() { @@ -66,6 +83,17 @@ public void SimpleTest() new KeyValuePair("label1", "value1"), new KeyValuePair("label2", "value2")); + histogram.Record(10); + + histogram.Record( + 100, + new KeyValuePair("label1", "value1")); + + histogram.Record( + 200, + new KeyValuePair("label1", "value1"), + new KeyValuePair("label2", "value2")); + Task.Delay(3000).Wait(); } }