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(); } } }