diff --git a/examples/Console/Program.cs b/examples/Console/Program.cs
index 3de41f1d9d1..324c54ddb01 100644
--- a/examples/Console/Program.cs
+++ b/examples/Console/Program.cs
@@ -34,7 +34,7 @@ public class Program
/// dotnet run -p Examples.Console.csproj prometheus -i 15 -p 9184 -d 2
/// dotnet run -p Examples.Console.csproj otlp -e "http://localhost:4317"
/// dotnet run -p Examples.Console.csproj zpages
- /// dotnet run -p Examples.Console.csproj metrics -p 100
+ /// dotnet run -p Examples.Console.csproj metrics -p 100 -e 500
///
/// The above must be run from the project root folder
/// (eg: C:\repos\opentelemetry-dotnet\examples\Console\).
@@ -47,7 +47,7 @@ public static void Main(string[] args)
(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.ObservationPeriodMilliseconds),
+ (MetricsOptions options) => TestMetrics.Run(options.ObservationPeriodMilliseconds, options.CollectionPeriodMilliseconds),
(GrpcNetClientOptions options) => TestGrpcNetClient.Run(),
(HttpClientOptions options) => TestHttpClient.Run(),
(RedisOptions options) => TestRedis.Run(options.Uri),
@@ -100,6 +100,9 @@ internal class MetricsOptions
{
[Option('p', "observationPeriodMilliseconds", Default = 100, HelpText = "Observation period.", Required = false)]
public int ObservationPeriodMilliseconds { get; set; }
+
+ [Option('c', "collectionPeriodMilliseconds", Default = 500, HelpText = "Collection period.", Required = false)]
+ public int CollectionPeriodMilliseconds { get; set; }
}
[Verb("grpc", HelpText = "Specify the options required to test Grpc.Net.Client")]
diff --git a/examples/Console/TestMetrics.cs b/examples/Console/TestMetrics.cs
index c8fb0f719d4..29e0e49392a 100644
--- a/examples/Console/TestMetrics.cs
+++ b/examples/Console/TestMetrics.cs
@@ -25,11 +25,14 @@ namespace Examples.Console
{
internal class TestMetrics
{
- internal static object Run(int observationInterval)
+ internal static object Run(int observationInterval, int collectionInterval)
{
using var provider = Sdk.CreateMeterProviderBuilder()
.AddSource("TestMeter") // All instruments from this meter are enabled.
.SetObservationPeriod(observationInterval)
+ .SetCollectionPeriod(collectionInterval)
+ .AddProcessor(new TagEnrichmentProcessor())
+ .AddExportProcessor(new MetricConsoleExporter())
.Build();
using var meter = new Meter("TestMeter", "0.0.1");
@@ -56,7 +59,7 @@ internal static object Run(int observationInterval)
};
});
- Task.Delay(50).Wait();
+ Task.Delay(5000).Wait();
System.Console.WriteLine("Press Enter key to exit.");
return null;
}
diff --git a/src/OpenTelemetry/Metrics/AggregateState.cs b/src/OpenTelemetry/Metrics/AggregateState.cs
new file mode 100644
index 00000000000..630906aa048
--- /dev/null
+++ b/src/OpenTelemetry/Metrics/AggregateState.cs
@@ -0,0 +1,39 @@
+//
+// Copyright The OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#nullable enable
+
+namespace OpenTelemetry.Metrics
+{
+ internal class AggregateState
+ {
+ internal long Count = 0;
+ internal long Sum = 0;
+
+ public virtual void Update(DataPoint? value)
+ {
+ long val = 0;
+
+ if (value is DataPoint idp)
+ {
+ val = idp.Value;
+ }
+
+ this.Count++;
+ this.Sum += val;
+ }
+ }
+}
diff --git a/src/OpenTelemetry/Metrics/DataPoint.cs b/src/OpenTelemetry/Metrics/DataPoint.cs
new file mode 100644
index 00000000000..2afcf366642
--- /dev/null
+++ b/src/OpenTelemetry/Metrics/DataPoint.cs
@@ -0,0 +1,51 @@
+//
+// 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;
+
+#nullable enable
+
+namespace OpenTelemetry.Metrics
+{
+ public abstract class DataPoint
+ {
+ private KeyValuePair[] tags;
+
+ public DataPoint(ReadOnlySpan> tags)
+ {
+ this.tags = tags.ToArray();
+ }
+
+ public ReadOnlySpan> Tags
+ {
+ get
+ {
+ return new ReadOnlySpan>(this.tags);
+ }
+ }
+
+ public void SetTags(KeyValuePair[] tags)
+ {
+ this.tags = tags;
+ }
+
+ public virtual string ValueAsString()
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/OpenTelemetry/Metrics/DataPoint{T}.cs b/src/OpenTelemetry/Metrics/DataPoint{T}.cs
new file mode 100644
index 00000000000..1df87b8a104
--- /dev/null
+++ b/src/OpenTelemetry/Metrics/DataPoint{T}.cs
@@ -0,0 +1,46 @@
+//
+// Copyright The OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+using System;
+using System.Collections.Generic;
+
+#nullable enable
+
+namespace OpenTelemetry.Metrics
+{
+ internal class DataPoint : DataPoint
+ where T : unmanaged
+ {
+ internal readonly T Value;
+
+ public DataPoint(T value, params KeyValuePair[] tags)
+ : base(new ReadOnlySpan>(tags))
+ {
+ this.Value = value;
+ }
+
+ public DataPoint(T value, ReadOnlySpan> tags)
+ : base(tags)
+ {
+ this.Value = value;
+ }
+
+ public override string ValueAsString()
+ {
+ return this.Value.ToString();
+ }
+ }
+}
diff --git a/src/OpenTelemetry/Metrics/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry/Metrics/MeterProviderBuilderExtensions.cs
index 05ce3051bcf..3eaea341b47 100644
--- a/src/OpenTelemetry/Metrics/MeterProviderBuilderExtensions.cs
+++ b/src/OpenTelemetry/Metrics/MeterProviderBuilderExtensions.cs
@@ -34,7 +34,55 @@ public static MeterProviderBuilder SetObservationPeriod(this MeterProviderBuilde
return meterProviderBuilderSdk.SetObservationPeriod(periodMilliseconds);
}
- return null;
+ return meterProviderBuilder;
+ }
+
+ ///
+ /// Sets collection period.
+ ///
+ /// .
+ /// Perion in milliseconds.
+ /// .
+ public static MeterProviderBuilder SetCollectionPeriod(this MeterProviderBuilder meterProviderBuilder, int periodMilliseconds)
+ {
+ if (meterProviderBuilder is MeterProviderBuilderSdk meterProviderBuilderSdk)
+ {
+ return meterProviderBuilderSdk.SetCollectionPeriod(periodMilliseconds);
+ }
+
+ return meterProviderBuilder;
+ }
+
+ ///
+ /// Add measurement processor.
+ ///
+ /// .
+ /// Measurement Processors.
+ /// .
+ public static MeterProviderBuilder AddProcessor(this MeterProviderBuilder meterProviderBuilder, MeasurementProcessor processor)
+ {
+ if (meterProviderBuilder is MeterProviderBuilderSdk meterProviderBuilderSdk)
+ {
+ return meterProviderBuilderSdk.AddMeasurementProcessor(processor);
+ }
+
+ return meterProviderBuilder;
+ }
+
+ ///
+ /// Add export processor.
+ ///
+ /// .
+ /// Measurement Processors.
+ /// .
+ public static MeterProviderBuilder AddExportProcessor(this MeterProviderBuilder meterProviderBuilder, MetricProcessor processor)
+ {
+ if (meterProviderBuilder is MeterProviderBuilderSdk meterProviderBuilderSdk)
+ {
+ return meterProviderBuilderSdk.AddExporter(processor);
+ }
+
+ return meterProviderBuilder;
}
///
diff --git a/src/OpenTelemetry/Metrics/MeterProviderBuilderSdk.cs b/src/OpenTelemetry/Metrics/MeterProviderBuilderSdk.cs
index 3321f808103..289b1f45a55 100644
--- a/src/OpenTelemetry/Metrics/MeterProviderBuilderSdk.cs
+++ b/src/OpenTelemetry/Metrics/MeterProviderBuilderSdk.cs
@@ -22,12 +22,17 @@ namespace OpenTelemetry.Metrics
internal class MeterProviderBuilderSdk : MeterProviderBuilder
{
private readonly List meterSources = new List();
- private int observationPeriodMilliseconds;
+ private int observationPeriodMilliseconds = 1000;
+ private int collectionPeriodMilliseconds = 1000;
internal MeterProviderBuilderSdk()
{
}
+ internal List MeasurementProcessors { get; } = new List();
+
+ internal List ExportProcessors { get; } = new List();
+
public override MeterProviderBuilder AddSource(params string[] names)
{
if (names == null)
@@ -54,9 +59,33 @@ internal MeterProviderBuilderSdk SetObservationPeriod(int periodMilliseconds)
return this;
}
+ internal MeterProviderBuilderSdk SetCollectionPeriod(int periodMilliseconds)
+ {
+ this.collectionPeriodMilliseconds = periodMilliseconds;
+ return this;
+ }
+
+ internal MeterProviderBuilderSdk AddMeasurementProcessor(MeasurementProcessor processor)
+ {
+ this.MeasurementProcessors.Add(processor);
+ return this;
+ }
+
+ internal MeterProviderBuilderSdk AddExporter(MetricProcessor processor)
+ {
+ this.ExportProcessors.Add(processor);
+ return this;
+ }
+
internal MeterProvider Build()
{
- return new MeterProviderSdk(this.meterSources, this.observationPeriodMilliseconds);
+ // 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 8d21028a40d..b4da20b3048 100644
--- a/src/OpenTelemetry/Metrics/MeterProviderSdk.cs
+++ b/src/OpenTelemetry/Metrics/MeterProviderSdk.cs
@@ -15,7 +15,6 @@
//
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.Threading;
@@ -28,14 +27,30 @@ namespace OpenTelemetry.Metrics
public class MeterProviderSdk
: MeterProvider
{
- private ConcurrentDictionary meters;
- private MeterListener listener;
- private CancellationTokenSource cts;
- private Task observerTask;
+ private readonly CancellationTokenSource cts = new CancellationTokenSource();
+ private readonly Task observerTask;
+ private readonly Task collectorTask;
+ private readonly MeterListener listener;
- internal MeterProviderSdk(IEnumerable meterSources, int observationPeriodMilliseconds)
+ internal MeterProviderSdk(
+ IEnumerable meterSources,
+ int observationPeriodMilliseconds,
+ int collectionPeriodMilliseconds,
+ MeasurementProcessor[] measurementProcessors,
+ MetricProcessor[] metricExportProcessors)
{
- this.meters = new ConcurrentDictionary();
+ this.ObservationPeriodMilliseconds = observationPeriodMilliseconds;
+ this.CollectionPeriodMilliseconds = collectionPeriodMilliseconds;
+
+ // Setup our Processors
+
+ this.MeasurementProcessors.AddRange(measurementProcessors);
+
+ this.AggregateProcessors.Add(new AggregateProcessor());
+
+ this.ExportProcessors.AddRange(metricExportProcessors);
+
+ // Setup Listener
var meterSourcesToSubscribe = new Dictionary(StringComparer.OrdinalIgnoreCase);
foreach (var name in meterSources)
@@ -65,40 +80,112 @@ internal MeterProviderSdk(IEnumerable meterSources, int observationPerio
this.listener.Start();
- this.cts = new CancellationTokenSource();
+ // Start our long running Task
var token = this.cts.Token;
- this.observerTask = Task.Run(async () =>
- {
- while (!token.IsCancellationRequested)
- {
- try
- {
- await Task.Delay(observationPeriodMilliseconds, token);
- }
- catch (TaskCanceledException)
- {
- }
-
- this.listener.RecordObservableInstruments();
- }
- });
+ 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;
+
+ 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 void MeasurementsCompleted(Instrument instrument, object? state)
{
Console.WriteLine($"Instrument {instrument.Meter.Name}:{instrument.Name} completed.");
}
- internal void MeasurementRecorded(Instrument instrument, T value, ReadOnlySpan> attribs, object? state)
+ internal void MeasurementRecorded(Instrument instrument, T value, ReadOnlySpan> tags, object? state)
+ where T : unmanaged
{
- Console.WriteLine($"Instrument {instrument.Meter.Name}:{instrument.Name} recorded {value}.");
+ // Run Pre Aggregator Processors
+
+ var measurmentContext = new MeasurementItem(instrument, new DataPoint(value, tags));
+
+ foreach (var processor in this.MeasurementProcessors)
+ {
+ processor.OnEnd(measurmentContext);
+ }
+
+ // Run Aggregator Processors
+
+ foreach (var processor in this.AggregateProcessors)
+ {
+ processor.OnEnd(measurmentContext);
+ }
}
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)
+ {
+ try
+ {
+ await Task.Delay(this.ObservationPeriodMilliseconds, token);
+ }
+ catch (TaskCanceledException)
+ {
+ }
+
+ this.listener.RecordObservableInstruments();
+ }
+ }
+
+ private async Task CollectorTask(CancellationToken token)
+ {
+ while (!token.IsCancellationRequested)
+ {
+ try
+ {
+ await Task.Delay(this.CollectionPeriodMilliseconds, token);
+ }
+ catch (TaskCanceledException)
+ {
+ }
+
+ this.Collect();
+ }
+ }
+
+ private void Collect()
+ {
+ var metricItem = new MetricItem();
+
+ foreach (var processor in this.AggregateProcessors)
+ {
+ var metric = processor.Collect();
+ metricItem.Metrics.Add(metric);
+ }
+
+ foreach (var processor in this.MetricProcessors)
+ {
+ processor.OnEnd(metricItem);
+ }
+
+ foreach (var processor in this.ExportProcessors)
+ {
+ processor.OnEnd(metricItem);
+ }
}
}
}
diff --git a/src/OpenTelemetry/Metrics/Processors/AggregateProcessor.cs b/src/OpenTelemetry/Metrics/Processors/AggregateProcessor.cs
new file mode 100644
index 00000000000..7824755acef
--- /dev/null
+++ b/src/OpenTelemetry/Metrics/Processors/AggregateProcessor.cs
@@ -0,0 +1,40 @@
+//
+// 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.Concurrent;
+using System.Diagnostics.Metrics;
+using System.Threading;
+
+#nullable enable
+
+namespace OpenTelemetry.Metrics
+{
+ internal class AggregateProcessor : MeasurementProcessor
+ {
+ private ConcurrentDictionary states = new ConcurrentDictionary();
+
+ public override void OnEnd(MeasurementItem data)
+ {
+ var state = this.states.GetOrAdd(data.Instrument, (k) => new AggregateState());
+ state.Update(data.Point);
+ }
+
+ public ConcurrentDictionary Collect()
+ {
+ return Interlocked.Exchange(ref this.states, new ConcurrentDictionary());
+ }
+ }
+}
diff --git a/src/OpenTelemetry/Metrics/Processors/MeasurementItem.cs b/src/OpenTelemetry/Metrics/Processors/MeasurementItem.cs
new file mode 100644
index 00000000000..ac8a3e0b7b2
--- /dev/null
+++ b/src/OpenTelemetry/Metrics/Processors/MeasurementItem.cs
@@ -0,0 +1,34 @@
+//
+// 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.Diagnostics.Metrics;
+
+#nullable enable
+
+namespace OpenTelemetry.Metrics
+{
+ public class MeasurementItem
+ {
+ internal readonly Instrument Instrument;
+ internal DataPoint? Point;
+
+ public MeasurementItem(Instrument instrument, DataPoint point)
+ {
+ this.Instrument = instrument;
+ this.Point = point;
+ }
+ }
+}
diff --git a/src/OpenTelemetry/Metrics/Processors/MeasurementProcessor.cs b/src/OpenTelemetry/Metrics/Processors/MeasurementProcessor.cs
new file mode 100644
index 00000000000..e2bbadfe29a
--- /dev/null
+++ b/src/OpenTelemetry/Metrics/Processors/MeasurementProcessor.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.
+//
+
+#nullable enable
+
+namespace OpenTelemetry.Metrics
+{
+ public abstract class MeasurementProcessor : BaseProcessor
+ {
+ }
+}
diff --git a/src/OpenTelemetry/Metrics/Processors/MetricConsoleExporter.cs b/src/OpenTelemetry/Metrics/Processors/MetricConsoleExporter.cs
new file mode 100644
index 00000000000..05c15f75b54
--- /dev/null
+++ b/src/OpenTelemetry/Metrics/Processors/MetricConsoleExporter.cs
@@ -0,0 +1,40 @@
+//
+// 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.Diagnostics.Metrics;
+using System.Threading;
+
+#nullable enable
+
+namespace OpenTelemetry.Metrics
+{
+ public class MetricConsoleExporter : MetricProcessor
+ {
+ public override void OnEnd(MetricItem data)
+ {
+ foreach (var exports in data.Metrics)
+ {
+ foreach (var item in exports)
+ {
+ var msg = $"{item.Key.Meter.Name}:{item.Key.Name} = count:{item.Value.Count}, sum:{item.Value.Sum}";
+ Console.WriteLine($"Export: {msg}");
+ }
+ }
+ }
+ }
+}
diff --git a/src/OpenTelemetry/Metrics/Processors/MetricItem.cs b/src/OpenTelemetry/Metrics/Processors/MetricItem.cs
new file mode 100644
index 00000000000..471bf5e3369
--- /dev/null
+++ b/src/OpenTelemetry/Metrics/Processors/MetricItem.cs
@@ -0,0 +1,34 @@
+//
+// 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;
+
+#nullable enable
+
+namespace OpenTelemetry.Metrics
+{
+ public class MetricItem
+ {
+ internal List> Metrics = new List>();
+
+ public MetricItem()
+ {
+ }
+ }
+}
diff --git a/src/OpenTelemetry/Metrics/Processors/MetricProcessor.cs b/src/OpenTelemetry/Metrics/Processors/MetricProcessor.cs
new file mode 100644
index 00000000000..a4a76d834ec
--- /dev/null
+++ b/src/OpenTelemetry/Metrics/Processors/MetricProcessor.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.
+//
+
+#nullable enable
+
+namespace OpenTelemetry.Metrics
+{
+ public abstract class MetricProcessor : BaseProcessor
+ {
+ }
+}
diff --git a/src/OpenTelemetry/Metrics/Processors/TagEnrichmentProcessor.cs b/src/OpenTelemetry/Metrics/Processors/TagEnrichmentProcessor.cs
new file mode 100644
index 00000000000..4fef0b8c0be
--- /dev/null
+++ b/src/OpenTelemetry/Metrics/Processors/TagEnrichmentProcessor.cs
@@ -0,0 +1,38 @@
+//
+// 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;
+
+#nullable enable
+
+namespace OpenTelemetry.Metrics
+{
+ public class TagEnrichmentProcessor : MeasurementProcessor
+ {
+ public override void OnEnd(MeasurementItem data)
+ {
+ var oldArray = data.Point!.Tags.ToArray();
+ int len = oldArray.Length;
+
+ var newArray = new KeyValuePair[len + 1];
+ oldArray.CopyTo(newArray, 0);
+
+ newArray[len] = new KeyValuePair("newLabel", "newValue");
+
+ data.Point.SetTags(newArray);
+ }
+ }
+}
diff --git a/src/System.Diagnostics.Metrics.Temp/Counter.cs b/src/System.Diagnostics.Metrics.Temp/Counter.cs
index 9e611cc6348..7e02531fd74 100644
--- a/src/System.Diagnostics.Metrics.Temp/Counter.cs
+++ b/src/System.Diagnostics.Metrics.Temp/Counter.cs
@@ -26,10 +26,10 @@ namespace System.Diagnostics.Metrics
///
/// TBD.
public sealed class Counter : Instrument
- where T : unmanaged
+ where T : struct
{
- internal Counter(Meter meter, string name, string? description, string? unit)
- : base(meter, name, description, unit)
+ internal Counter(Meter meter, string name, string? unit, string? description)
+ : base(meter, name, unit, description)
{
this.Publish();
}
@@ -37,62 +37,62 @@ internal Counter(Meter meter, string name, string? description, string? unit)
///
/// TBD.
///
- public void Add(T measurement)
+ public void Add(T delta)
{
- this.RecordMeasurement(measurement);
+ this.RecordMeasurement(delta);
}
///
/// TBD.
///
public void Add(
- T measurement,
+ T delta,
KeyValuePair tag1)
{
- this.RecordMeasurement(measurement, tag1);
+ this.RecordMeasurement(delta, tag1);
}
///
/// TBD.
///
public void Add(
- T measurement,
+ T delta,
KeyValuePair tag1,
KeyValuePair tag2)
{
- this.RecordMeasurement(measurement, tag1, tag2);
+ this.RecordMeasurement(delta, tag1, tag2);
}
///
/// TBD.
///
public void Add(
- T measurement,
+ T delta,
KeyValuePair tag1,
KeyValuePair tag2,
KeyValuePair tag3)
{
- this.RecordMeasurement(measurement, tag1, tag2, tag3);
+ this.RecordMeasurement(delta, tag1, tag2, tag3);
}
///
/// TBD.
///
public void Add(
- T measurement,
+ T delta,
ReadOnlySpan> tags)
{
- this.RecordMeasurement(measurement, tags);
+ this.RecordMeasurement(delta, tags);
}
///
/// TBD.
///
public void Add(
- T measurement,
+ T delta,
params KeyValuePair[] tags)
{
- this.RecordMeasurement(measurement, tags);
+ this.RecordMeasurement(delta, tags);
}
}
}
diff --git a/src/System.Diagnostics.Metrics.Temp/Histogram.cs b/src/System.Diagnostics.Metrics.Temp/Histogram.cs
index daac125a833..a68104edc09 100644
--- a/src/System.Diagnostics.Metrics.Temp/Histogram.cs
+++ b/src/System.Diagnostics.Metrics.Temp/Histogram.cs
@@ -27,10 +27,10 @@ namespace System.Diagnostics.Metrics
///
/// TBD.
public sealed class Histogram : Instrument
- where T : unmanaged
+ where T : struct
{
- internal Histogram(Meter meter, string name, string? description, string? unit)
- : base(meter, name, description, unit)
+ internal Histogram(Meter meter, string name, string? unit, string? description)
+ : base(meter, name, unit, description)
{
this.Publish();
}
@@ -38,62 +38,62 @@ internal Histogram(Meter meter, string name, string? description, string? unit)
///
/// TBD.
///
- public void Record(T measurement)
+ public void Record(T value)
{
- this.RecordMeasurement(measurement);
+ this.RecordMeasurement(value);
}
///
/// TBD.
///
public void Record(
- T measurement,
+ T value,
KeyValuePair tag1)
{
- this.RecordMeasurement(measurement, tag1);
+ this.RecordMeasurement(value, tag1);
}
///
/// TBD.
///
public void Record(
- T measurement,
+ T value,
KeyValuePair tag1,
KeyValuePair tag2)
{
- this.RecordMeasurement(measurement, tag1, tag2);
+ this.RecordMeasurement(value, tag1, tag2);
}
///
/// TBD.
///
public void Record(
- T measurement,
+ T value,
KeyValuePair tag1,
KeyValuePair tag2,
KeyValuePair tag3)
{
- this.RecordMeasurement(measurement, tag1, tag2, tag3);
+ this.RecordMeasurement(value, tag1, tag2, tag3);
}
///
/// TBD.
///
public void Record(
- T measurement,
+ T value,
ReadOnlySpan> tags)
{
- this.RecordMeasurement(measurement, tags);
+ this.RecordMeasurement(value, tags);
}
///
/// TBD.
///
public void Record(
- T measurement,
+ T value,
params KeyValuePair[] tags)
{
- this.RecordMeasurement(measurement, tags);
+ this.RecordMeasurement(value, tags);
}
}
}
diff --git a/src/System.Diagnostics.Metrics.Temp/Instrument.cs b/src/System.Diagnostics.Metrics.Temp/Instrument.cs
index 1662bd02fba..5e3b5c531e9 100644
--- a/src/System.Diagnostics.Metrics.Temp/Instrument.cs
+++ b/src/System.Diagnostics.Metrics.Temp/Instrument.cs
@@ -34,12 +34,12 @@ public abstract class Instrument
/// Initializes a new instance of the class.
/// Protected constructor to initialize the common instrument properties.
///
- protected Instrument(Meter meter, string name, string? description, string? unit)
+ protected Instrument(Meter meter, string name, string? unit, string? description)
{
this.Meter = meter;
this.Name = name;
- this.Description = description;
this.Unit = unit;
+ this.Description = description;
}
///
diff --git a/src/System.Diagnostics.Metrics.Temp/InstrumentT.cs b/src/System.Diagnostics.Metrics.Temp/InstrumentT.cs
index 48a7aa66fc2..6976a91e3dd 100644
--- a/src/System.Diagnostics.Metrics.Temp/InstrumentT.cs
+++ b/src/System.Diagnostics.Metrics.Temp/InstrumentT.cs
@@ -17,6 +17,7 @@
using System.Collections.Generic;
#nullable enable
+#pragma warning disable SA1649
namespace System.Diagnostics.Metrics
{
@@ -26,14 +27,14 @@ namespace System.Diagnostics.Metrics
///
/// TBD.
public abstract class Instrument : Instrument
- where T : unmanaged
+ where T : struct
{
///
/// Initializes a new instance of the class.
/// Protected constructor to create the instrument with the common properties.
///
- protected Instrument(Meter meter, string name, string? description, string? unit)
- : base(meter, name, description, unit)
+ protected Instrument(Meter meter, string name, string? unit, string? description)
+ : base(meter, name, unit, description)
{
}
diff --git a/src/System.Diagnostics.Metrics.Temp/Measurement.cs b/src/System.Diagnostics.Metrics.Temp/Measurement.cs
index 1d04c6bd3b8..27a97efdcfe 100644
--- a/src/System.Diagnostics.Metrics.Temp/Measurement.cs
+++ b/src/System.Diagnostics.Metrics.Temp/Measurement.cs
@@ -27,7 +27,7 @@ namespace System.Diagnostics.Metrics
///
/// TBD.
public struct Measurement
- where T : unmanaged
+ where T : struct
{
///
/// Initializes a new instance of the struct.
diff --git a/src/System.Diagnostics.Metrics.Temp/Meter.cs b/src/System.Diagnostics.Metrics.Temp/Meter.cs
index 31229425446..a5c8b821fac 100644
--- a/src/System.Diagnostics.Metrics.Temp/Meter.cs
+++ b/src/System.Diagnostics.Metrics.Temp/Meter.cs
@@ -61,11 +61,11 @@ public Meter(string name, string? version)
/// TBD.
public Counter CreateCounter(
string name,
- string? description = null,
- string? unit = null)
- where T : unmanaged
+ string? unit = null,
+ string? description = null)
+ where T : struct
{
- return new Counter(this, name, description, unit);
+ return new Counter(this, name, unit, description);
}
///
@@ -74,11 +74,11 @@ public Counter CreateCounter(
/// TBD.
public Histogram CreateHistogram(
string name,
- string? description = null,
- string? unit = null)
- where T : unmanaged
+ string? unit = null,
+ string? description = null)
+ where T : struct
{
- return new Histogram(this, name, description, unit);
+ return new Histogram(this, name, unit, description);
}
///
@@ -88,9 +88,9 @@ public Histogram CreateHistogram(
public ObservableCounter CreateObservableCounter(
string name,
Func observeValue,
- string? description = null,
- string? unit = null)
- where T : unmanaged
+ string? unit = null,
+ string? description = null)
+ where T : struct
{
return new ObservableCounter(
this,
@@ -99,8 +99,8 @@ public ObservableCounter CreateObservableCounter(
{
new Measurement(observeValue()),
},
- description,
- unit);
+ unit,
+ description);
}
///
@@ -110,9 +110,9 @@ public ObservableCounter CreateObservableCounter(
public ObservableCounter CreateObservableCounter(
string name,
Func> observeValue,
- string? description = null,
- string? unit = null)
- where T : unmanaged
+ string? unit = null,
+ string? description = null)
+ where T : struct
{
return new ObservableCounter(
this,
@@ -121,8 +121,8 @@ public ObservableCounter CreateObservableCounter(
{
observeValue(),
},
- description,
- unit);
+ unit,
+ description);
}
///
@@ -132,11 +132,11 @@ public ObservableCounter CreateObservableCounter(
public ObservableCounter CreateObservableCounter(
string name,
Func>> observeValues,
- string? description = null,
- string? unit = null)
- where T : unmanaged
+ string? unit = null,
+ string? description = null)
+ where T : struct
{
- return new ObservableCounter(this, name, observeValues, description, unit);
+ return new ObservableCounter(this, name, observeValues, unit, description);
}
///
@@ -146,9 +146,9 @@ public ObservableCounter CreateObservableCounter(
public ObservableGauge CreateObservableGauge(
string name,
Func observeValue,
- string? description = null,
- string? unit = null)
- where T : unmanaged
+ string? unit = null,
+ string? description = null)
+ where T : struct
{
return new ObservableGauge(
this,
@@ -157,8 +157,8 @@ public ObservableGauge CreateObservableGauge(
{
new Measurement(observeValue()),
},
- description,
- unit);
+ unit,
+ description);
}
///
@@ -168,9 +168,9 @@ public ObservableGauge CreateObservableGauge(
public ObservableGauge CreateObservableGauge(
string name,
Func> observeValue,
- string? description = null,
- string? unit = null)
- where T : unmanaged
+ string? unit = null,
+ string? description = null)
+ where T : struct
{
return new ObservableGauge(
this,
@@ -179,8 +179,8 @@ public ObservableGauge CreateObservableGauge(
{
observeValue(),
},
- description,
- unit);
+ unit,
+ description);
}
///
@@ -190,11 +190,11 @@ public ObservableGauge CreateObservableGauge(
public ObservableGauge CreateObservableGauge(
string name,
Func>> observeValues,
- string? description = null,
- string? unit = null)
- where T : unmanaged
+ string? unit = null,
+ string? description = null)
+ where T : struct
{
- return new ObservableGauge(this, name, observeValues, description, unit);
+ return new ObservableGauge(this, name, observeValues, unit, description);
}
///
@@ -204,9 +204,9 @@ public ObservableGauge CreateObservableGauge(
public ObservableUpDownCounter CreateObservableUpDownCounter(
string name,
Func observeValue,
- string? description = null,
- string? unit = null)
- where T : unmanaged
+ string? unit = null,
+ string? description = null)
+ where T : struct
{
return new ObservableUpDownCounter(
this,
@@ -215,8 +215,8 @@ public ObservableUpDownCounter CreateObservableUpDownCounter(
{
new Measurement(observeValue()),
},
- description,
- unit);
+ unit,
+ description);
}
///
@@ -226,9 +226,9 @@ public ObservableUpDownCounter CreateObservableUpDownCounter(
public ObservableUpDownCounter CreateObservableUpDownCounter(
string name,
Func> observeValue,
- string? description = null,
- string? unit = null)
- where T : unmanaged
+ string? unit = null,
+ string? description = null)
+ where T : struct
{
return new ObservableUpDownCounter(
this,
@@ -237,8 +237,8 @@ public ObservableUpDownCounter CreateObservableUpDownCounter(
{
observeValue(),
},
- description,
- unit);
+ unit,
+ description);
}
///
@@ -248,11 +248,11 @@ public ObservableUpDownCounter CreateObservableUpDownCounter(
public ObservableUpDownCounter CreateObservableUpDownCounter(
string name,
Func>> observeValues,
- string? description = null,
- string? unit = null)
- where T : unmanaged
+ string? unit = null,
+ string? description = null)
+ where T : struct
{
- return new ObservableUpDownCounter(this, name, observeValues, description, unit);
+ return new ObservableUpDownCounter(this, name, observeValues, unit, description);
}
///
diff --git a/src/System.Diagnostics.Metrics.Temp/MeterListener.cs b/src/System.Diagnostics.Metrics.Temp/MeterListener.cs
index 5d4efc1c10a..b33a54c1c91 100644
--- a/src/System.Diagnostics.Metrics.Temp/MeterListener.cs
+++ b/src/System.Diagnostics.Metrics.Temp/MeterListener.cs
@@ -31,7 +31,8 @@ public delegate void MeasurementCallback(
Instrument instrument,
T measurement,
ReadOnlySpan> tags,
- object? state);
+ object? state)
+ where T : struct;
///
/// The listener class can be used to listen to observable and non-observable instrument
@@ -94,6 +95,7 @@ public void EnableMeasurementEvents(Instrument instrument, object? state = null)
///
/// TBD.
public void SetMeasurementEventCallback(MeasurementCallback? measurementCallback)
+ where T : struct
{
this.Callbacks.TryAdd(typeof(T), (object?)measurementCallback);
}
@@ -105,13 +107,6 @@ public void Start()
{
}
- ///
- /// TBD.
- ///
- public void Stop()
- {
- }
-
///
/// Call all Observable instruments to get the recorded measurements reported to the
/// callbacks enabled by SetMeasurementEventCallback_T.
diff --git a/src/System.Diagnostics.Metrics.Temp/ObservableCounter.cs b/src/System.Diagnostics.Metrics.Temp/ObservableCounter.cs
index 29af86729c2..19c4bf806f0 100644
--- a/src/System.Diagnostics.Metrics.Temp/ObservableCounter.cs
+++ b/src/System.Diagnostics.Metrics.Temp/ObservableCounter.cs
@@ -27,12 +27,12 @@ namespace System.Diagnostics.Metrics
///
/// TBD.
public sealed class ObservableCounter : ObservableInstrument
- where T : unmanaged
+ where T : struct
{
private Func>> observeValues;
- internal ObservableCounter(Meter meter, string name, Func>> observeValues, string? description, string? unit)
- : base(meter, name, description, unit)
+ internal ObservableCounter(Meter meter, string name, Func>> observeValues, string? unit, string? description)
+ : base(meter, name, unit, description)
{
this.observeValues = observeValues;
this.Publish();
diff --git a/src/System.Diagnostics.Metrics.Temp/ObservableGauge.cs b/src/System.Diagnostics.Metrics.Temp/ObservableGauge.cs
index 6418655622e..94eca5c393e 100644
--- a/src/System.Diagnostics.Metrics.Temp/ObservableGauge.cs
+++ b/src/System.Diagnostics.Metrics.Temp/ObservableGauge.cs
@@ -27,12 +27,12 @@ namespace System.Diagnostics.Metrics
///
/// TBD.
public sealed class ObservableGauge : ObservableInstrument
- where T : unmanaged
+ where T : struct
{
private Func>> observeValues;
- internal ObservableGauge(Meter meter, string name, Func>> observeValues, string? description, string? unit)
- : base(meter, name, description, unit)
+ internal ObservableGauge(Meter meter, string name, Func>> observeValues, string? unit, string? description)
+ : base(meter, name, unit, description)
{
this.observeValues = observeValues;
this.Publish();
diff --git a/src/System.Diagnostics.Metrics.Temp/ObservableInstrument.cs b/src/System.Diagnostics.Metrics.Temp/ObservableInstrument.cs
index c8070f6da22..5ff3908afcb 100644
--- a/src/System.Diagnostics.Metrics.Temp/ObservableInstrument.cs
+++ b/src/System.Diagnostics.Metrics.Temp/ObservableInstrument.cs
@@ -26,7 +26,7 @@ namespace System.Diagnostics.Metrics
///
/// TBD.
public abstract class ObservableInstrument : Instrument
- where T : unmanaged
+ where T : struct
{
///
/// Initializes a new instance of the class.
@@ -35,8 +35,8 @@ public abstract class ObservableInstrument : Instrument
protected ObservableInstrument(
Meter meter,
string name,
- string? description,
- string? unit)
+ string? unit,
+ string? description)
: base(meter, name, description, unit)
{
}
diff --git a/src/System.Diagnostics.Metrics.Temp/ObservableUpDownCounter.cs b/src/System.Diagnostics.Metrics.Temp/ObservableUpDownCounter.cs
index d0ac83931fa..bf74eef8f8e 100644
--- a/src/System.Diagnostics.Metrics.Temp/ObservableUpDownCounter.cs
+++ b/src/System.Diagnostics.Metrics.Temp/ObservableUpDownCounter.cs
@@ -27,12 +27,12 @@ namespace System.Diagnostics.Metrics
///
/// TBD.
public sealed class ObservableUpDownCounter : ObservableInstrument
- where T : unmanaged
+ where T : struct
{
private Func>> observeValues;
internal ObservableUpDownCounter(Meter meter, string name, Func>> observeValues, string? description, string? unit)
- : base(meter, name, description, unit)
+ : base(meter, name, unit, description)
{
this.observeValues = observeValues;
this.Publish();
diff --git a/test/OpenTelemetry.Tests/Metrics/MetricAPITest.cs b/test/OpenTelemetry.Tests/Metrics/MetricAPITest.cs
index 9f332557b73..da7250a3b28 100644
--- a/test/OpenTelemetry.Tests/Metrics/MetricAPITest.cs
+++ b/test/OpenTelemetry.Tests/Metrics/MetricAPITest.cs
@@ -16,6 +16,7 @@
using System.Collections.Generic;
using System.Diagnostics.Metrics;
+using System.Threading.Tasks;
using Xunit;
#nullable enable
@@ -29,7 +30,10 @@ public void SimpleTest()
{
using var provider = Sdk.CreateMeterProviderBuilder()
.AddSource("BasicAllTest")
- .SetObservationPeriod(1000)
+ .SetObservationPeriod(300)
+ .SetCollectionPeriod(1000)
+ .AddProcessor(new TagEnrichmentProcessor())
+ .AddExportProcessor(new MetricConsoleExporter())
.Build();
using var meter = new Meter("BasicAllTest", "0.0.1");
@@ -61,6 +65,8 @@ public void SimpleTest()
200,
new KeyValuePair("label1", "value1"),
new KeyValuePair("label2", "value2"));
+
+ Task.Delay(3000).Wait();
}
}
}