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