diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/AzureMonitorExporterExtensions.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/AzureMonitorExporterExtensions.cs index e18db3589942..827f54e19c29 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/AzureMonitorExporterExtensions.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/AzureMonitorExporterExtensions.cs @@ -92,7 +92,7 @@ public static TracerProviderBuilder AddAzureMonitorTraceExporter( builder.AddProcessor(new CompositeProcessor(new BaseProcessor[] { - new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(exporterOptions)), + new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(exporterOptions), new PerfCounterItemCounts()), new BatchActivityExportProcessor(new AzureMonitorTraceExporter(exporterOptions)) })); }); @@ -116,6 +116,7 @@ public static MeterProviderBuilder AddAzureMonitorMetricExporter( TokenCredential credential = null, string name = null) { + // TODO: determine if we want to add support for perf counters here if (builder == null) { throw new ArgumentNullException(nameof(builder)); @@ -199,6 +200,7 @@ public static OpenTelemetryLoggerOptions AddAzureMonitorLogExporter( options.Credential ??= credential; } + // TODO: determine if we want to add support for perf counters here return loggerOptions.AddProcessor(new BatchLogRecordExportProcessor(new AzureMonitorLogExporter(options))); } @@ -267,6 +269,7 @@ public static LoggerProviderBuilder AddAzureMonitorLogExporter( sp.EnsureNoUseAzureMonitorExporterRegistrations(); // TODO: Do we need provide an option to alter BatchExportLogRecordProcessorOptions? + // TODO: Do we need to support perf counters with this? return new BatchLogRecordExportProcessor(new AzureMonitorLogExporter(exporterOptions)); }); } diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/ExporterRegistrationHostedService.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/ExporterRegistrationHostedService.cs index 98aa268da732..892fa4b00bd4 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/ExporterRegistrationHostedService.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/ExporterRegistrationHostedService.cs @@ -29,7 +29,6 @@ namespace Azure.Monitor.OpenTelemetry.Exporter internal sealed class ExporterRegistrationHostedService : IHostedService { private readonly IServiceProvider _serviceProvider; - public ExporterRegistrationHostedService(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; @@ -49,6 +48,7 @@ public Task StopAsync(CancellationToken cancellationToken) private static void Initialize(IServiceProvider serviceProvider) { Debug.Assert(serviceProvider != null, "serviceProvider was null"); + PerfCounterItemCounts itemCounts = new PerfCounterItemCounts(); var tracerProvider = serviceProvider!.GetService(); if (tracerProvider != null) @@ -68,7 +68,7 @@ private static void Initialize(IServiceProvider serviceProvider) // TODO: Add Ai Sampler. tracerProvider.AddProcessor(new CompositeProcessor(new BaseProcessor[] { - new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(exporterOptions)), + new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(exporterOptions), itemCounts), new BatchActivityExportProcessor(new AzureMonitorTraceExporter(exporterOptions)) })); } @@ -89,13 +89,18 @@ private static void Initialize(IServiceProvider serviceProvider) loggerProvider.AddProcessor(new CompositeProcessor(new BaseProcessor[] { + new PerfCounterLogProcessor(itemCounts), new LiveMetricsLogProcessor(manager), new BatchLogRecordExportProcessor(exporter) })); } else { - loggerProvider.AddProcessor(new BatchLogRecordExportProcessor(exporter)); + loggerProvider.AddProcessor(new CompositeProcessor(new BaseProcessor[] + { + new PerfCounterLogProcessor(itemCounts), + new BatchLogRecordExportProcessor(exporter) + })); } } } diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/PerfCounterConstants.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/PerfCounterConstants.cs new file mode 100644 index 000000000000..0ab34e3c3075 --- /dev/null +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/PerfCounterConstants.cs @@ -0,0 +1,20 @@ +namespace Azure.Monitor.OpenTelemetry.Exporter.Internals +{ + internal static class PerfCounterConstants + { + internal const string PerfCounterMeterName = "PerfCounterMeter"; + internal const string ExceptionRateInstrumentName = "ExceptionRate"; + internal const string RequestRateInstrumentName = "RequestRate"; + internal const string ProcessCpuInstrumentName = "ProcessCpu"; + internal const string ProcessCpuNormalizedInstrumentName = "ProcessCpuNormalized"; + internal const string ProcessPrivateBytesInstrumentName = "ProcessPrivateBytes"; + + // breeze perf counter names + internal const string ExceptionRateMetricIdValue = "\.NET CLR Exceptions(??APP_CLR_PROC??)\# of Exceps Thrown / sec"; + internal const string RequestRateMetricIdValue = "\ASP.NET Applications(??APP_W3SVC_PROC??)\Requests/Sec"; + internal const string ProcessCpuMetricIdValue = "\Process(??APP_WIN32_PROC??)\% Processor Time"; + internal const string ProcessCpuNormalizedMetricIdValue = "\Process(??APP_WIN32_PROC??)\% Processor Time Normalized"; + internal const string ProcessPrivateBytesMetricIdValue = "\Process(??APP_WIN32_PROC??)\Private Bytes"; + + } +} diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/PerfCounterItemCounts.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/PerfCounterItemCounts.cs new file mode 100644 index 000000000000..a73cf1accd21 --- /dev/null +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/PerfCounterItemCounts.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading; + +namespace Azure.Monitor.OpenTelemetry.Exporter.Internals +{ + /// + /// Thread-safe counters for performance metrics. + /// + internal sealed class PerfCounterItemCounts + { + private long _totalRequestCount = 0; + private long _totalExceptionCount = 0; + + /// + /// Atomically increments the total request count. + /// + /// The new value after incrementing. + public long IncrementRequestCount() + { + return Interlocked.Increment(ref _totalRequestCount); + } + + /// + /// Atomically increments the total exception count. + /// + /// The new value after incrementing. + public long IncrementExceptionCount() + { + return Interlocked.Increment(ref _totalExceptionCount); + } + + /// + /// Atomically reads the current total request count. + /// + /// The current request count. + public long ReadRequestCount() + { + return Interlocked.Read(ref _totalRequestCount); + } + + /// + /// Atomically reads the current total exception count. + /// + /// The current exception count. + public long ReadExceptionCount() + { + return Interlocked.Read(ref _totalExceptionCount); + } + } +} diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/PerfCounterLogProcessor.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/PerfCounterLogProcessor.cs new file mode 100644 index 000000000000..0a8c00e96bf0 --- /dev/null +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/PerfCounterLogProcessor.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Diagnostics; +using OpenTelemetry; +using OpenTelemetry.Logs; + +namespace Azure.Monitor.OpenTelemetry.Exporter.Internals +{ + internal sealed class PerfCounterLogProcessor : BaseProcessor + { + private bool _disposed; + private readonly PerfCounterItemCounts _itemCounts; + + internal PerfCounterLogProcessor(PerfCounterItemCounts itemCounts) + { + _itemCounts = itemCounts; + } + + public override void OnEnd(LogRecord data) + { + if (data.Exception is not null) + { + _itemCounts.IncrementExceptionCount(); + } + } + + protected override void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + // cleanup if needed + } + _disposed = true; + } + base.Dispose(disposing); + } + } +} diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/PerformanceCounter.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/PerformanceCounter.cs new file mode 100644 index 000000000000..9bdde461ade9 --- /dev/null +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/PerformanceCounter.cs @@ -0,0 +1,181 @@ +using OpenTelemetry.Metrics; +using System.Diagnostics.Metrics; + +namespace Azure.Monitor.OpenTelemetry.Exporter.Internals +{ + internal sealed class PerformanceCounter : IDisposable + { + private readonly MeterProvider _meterProvider; + private readonly Meter _perfCounterMeter; + + // Gauge instruments + private readonly ObservableGauge _exceptionRateGauge; + private readonly ObservableGauge _requestRateGauge; + private readonly ObservableGauge _processCpuGauge; + private readonly ObservableGauge _processCpuNormalizedGauge; + private readonly ObservableGauge _processPrivateBytesGauge; + private readonly Process _process = Process.GetCurrentProcess(); + + // state for cpu counter calculations + private DateTimeOffset _cachedCollectedTime = DateTimeOffset.MinValue; + private long _cachedCollectedValueTicks = 0; + private double _currentCpuPercentage = 0; + private readonly int _processorCount = Environment.ProcessorCount; + + // state for telemetry item related counts + private readonly PerfCounterItemCounts _itemCounts; + private long _lastExceptionRateCount = 0; + private DateTimeOffset _lastExceptionRateTime = DateTimeOffset.UtcNow; + private long _lastRequestRateCount = 0; + private DateTimeOffset _lastRequestRateTime = DateTimeOffset.UtcNow; + + private bool _disposed; + + public PerformanceCounter(MeterProvider meterProvider, PerfCounterItemCounts itemCounts) + { + _meterProvider = meterProvider; + _perfCounterMeter = new Meter(PerfCounterConstants.PerfCounterMeterName); + _itemCounts = itemCounts; + + // Create observable gauges for perf counter + _exceptionRateGauge = _perfCounterMeter.CreateObservableGauge( + PerfCounterConstants.ExceptionRateInstrumentName, + () => GetExceptionRate(), + description: "Exception rate gauge (ex/sec)"); + + _requestRateGauge = _perfCounterMeter.CreateObservableGauge( + PerfCounterConstants.RequestRateInstrumentName, + () => GetRequestRate(), + description: "Request rate gauge (req/sec)"); + + _processCpuGauge = _perfCounterMeter.CreateObservableGauge( + PerfCounterConstants.ProcessCpuInstrumentName, + () => GetProcessCpu(), + description: "Process CPU gauge (percent)"); + + _processCpuNormalizedGauge = _perfCounterMeter.CreateObservableGauge( + PerfCounterConstants.ProcessCpuNormalizedInstrumentName, + () => GetProcessCpuNormalized(), + description: "Process CPU normalized gauge (percent)"); + + _processPrivateBytesGauge = _perfCounterMeter.CreateObservableGauge( + PerfCounterConstants.ProcessPrivateBytesInstrumentName, + () => GetProcessPrivateBytes(), + description: "Process private bytes gauge"); + } + + // Placeholder methods for gauge callbacks + private double GetExceptionRate() + { + var currentTime = DateTimeOffset.UtcNow; + var totalExceptions = _itemCounts.ReadExceptionCount(); + var intervalData = totalExceptions - _lastExceptionRateCount; + var elapsedSeconds = (currentTime - _lastExceptionRateTime).TotalSeconds; + + double dataPerSec = 0.0; + if (elapsedSeconds > 0) + { + dataPerSec = intervalData / elapsedSeconds; + } + + _lastExceptionRateCount = totalExceptions; + _lastExceptionRateTime = currentTime; + + return dataPerSec; + } + private double GetRequestRate() + { + var currentTime = DateTimeOffset.UtcNow; + var totalRequests = _itemCounts.ReadRequestCount(); + var intervalRequests = totalRequests - _lastRequestRateCount; + var elapsedSec = (currentTime - _lastRequestRateTime).TotalSeconds; + + double requestsPerSec = 0.0; + if (elapsedSec > 0) + { + requestsPerSec = intervalRequests / elapsedSec; + } + + _lastRequestRateCount = totalRequests; + _lastRequestRateTime = currentTime; + + return requestsPerSec; + } + private double GetProcessCpu() + { + UpdateCpuValuesIfNeeded(); + return _currentCpuPercentage; + } + private double GetProcessCpuNormalized() + { + UpdateCpuValuesIfNeeded(); + return _currentCpuPercentage / _processorCount; + } + private double GetProcessPrivateBytes() + { + return _process.PrivateMemorySize64; + } + + private void UpdateCpuValuesIfNeeded() + { + var now = DateTimeOffset.UtcNow; + if ((now - _cachedCollectedTime).TotalSeconds > 59) // the default interval is set to a minute + { + _process.Refresh(); + if (TryCalculateProcessCpu(out var cpuValue)) + { + _currentCpuPercentage = cpuValue; + } + } + } + + private bool TryCalculateProcessCpu(out double calculatedValue) + { + var previousCollectedValue = _cachedCollectedValueTicks; + var previousCollectedTime = _cachedCollectedTime; + + var recentCollectedValue = _cachedCollectedValueTicks = _process.TotalProcessorTime.Ticks; + var recentCollectedTime = _cachedCollectedTime = DateTimeOffset.UtcNow; + + double calculatedValue; + + if (previousCollectedTime == DateTimeOffset.MinValue) + { + Debug.WriteLine($"{nameof(TryCalculateProcessCpu)} DateTimeOffset.MinValue"); + calculatedValue = default; + return false; + } + + var period = recentCollectedTime.Ticks - previousCollectedTime.Ticks; + if (period < 0) + { + Debug.WriteLine($"{nameof(TryCalculateProcessCpu)} period less than zero"); + calculatedValue = default; + return false; + } + + var diff = recentCollectedValue - previousCollectedValue; + if (diff < 0) + { + Debug.WriteLine($"{nameof(TryCalculateProcessCpu)} diff less than zero"); + calculatedValue = default; + return false; + } + + period = period != 0 ? period : 1; + calculatedValue = diff * 100.0 / period; + return true; + } + + public void Dispose() + { + if (_disposed) return; + try + { + _perfCounterMeter?.Dispose(); // Do NOT dispose _meterProvider (shared elsewhere) + } + catch { } + _disposed = true; + } + } +} diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/StandardMetricsExtractionProcessor.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/StandardMetricsExtractionProcessor.cs index 8866e1202695..67965846319d 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/StandardMetricsExtractionProcessor.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/StandardMetricsExtractionProcessor.cs @@ -19,25 +19,36 @@ internal sealed class StandardMetricsExtractionProcessor : BaseProcessor _requestDuration; private readonly Histogram _dependencyDuration; + private readonly PerformanceCounter _performanceCounter; + private readonly PerfCounterItemCounts _itemCounts; internal static readonly IReadOnlyDictionary s_standardMetricNameMapping = new Dictionary() { [StandardMetricConstants.RequestDurationInstrumentName] = StandardMetricConstants.RequestDurationMetricIdValue, [StandardMetricConstants.DependencyDurationInstrumentName] = StandardMetricConstants.DependencyDurationMetricIdValue, + [PerfCounterConstants.ExceptionRateInstrumentName] = PerfCounterConstants.ExceptionRateMetricIdValue, + [PerfCounterConstants.RequestRateInstrumentName] = PerfCounterConstants.RequestRateMetricIdValue, + [PerfCounterConstants.ProcessCpuInstrumentName] = PerfCounterConstants.ProcessCpuMetricIdValue, + [PerfCounterConstants.ProcessCpuNormalizedInstrumentName] = PerfCounterConstants.ProcessCpuNormalizedMetricIdValue, + [PerfCounterConstants.ProcessPrivateBytesInstrumentName] = PerfCounterConstants.ProcessPrivateBytesMetricIdValue, }; internal AzureMonitorResource? StandardMetricResource => _resource ??= ParentProvider?.GetResource().CreateAzureMonitorResource(); - internal StandardMetricsExtractionProcessor(AzureMonitorMetricExporter metricExporter) + internal StandardMetricsExtractionProcessor(AzureMonitorMetricExporter metricExporter, PerfCounterItemCounts itemCounts) { _meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(StandardMetricConstants.StandardMetricMeterName) + .AddMeter(PerfCounterConstants.PerfCounterMeterName) .AddReader(new PeriodicExportingMetricReader(metricExporter) { TemporalityPreference = MetricReaderTemporalityPreference.Delta }) .Build(); _meter = new Meter(StandardMetricConstants.StandardMetricMeterName); + _itemCounts = itemCounts; + _performanceCounter = new PerformanceCounter(_meterProvider, _itemCounts); _requestDuration = _meter.CreateHistogram(StandardMetricConstants.RequestDurationInstrumentName); _dependencyDuration = _meter.CreateHistogram(StandardMetricConstants.DependencyDurationInstrumentName); + } public override void OnEnd(Activity activity) @@ -58,6 +69,16 @@ public override void OnEnd(Activity activity) ReportDependencyDurationMetric(activity); } } + if (activity.Events != null) + { + foreach (ref readonly var @event in activity.EnumerateEvents()) + { + if (@event.Name == SemanticConventions.AttributeExceptionEventName) + { + _itemCounts.IncrementExceptionCount(); + } + } + } } private void ReportRequestDurationMetric(Activity activity) @@ -82,6 +103,7 @@ private void ReportRequestDurationMetric(Activity activity) // Report metric _requestDuration.Record(activity.Duration.TotalMilliseconds, tags); + _itemCounts.IncrementRequestCount(); } private void ReportDependencyDurationMetric(Activity activity) @@ -143,6 +165,7 @@ protected override void Dispose(bool disposing) { _meterProvider?.Dispose(); _meter?.Dispose(); + _performanceCounter?.Dispose(); } catch (Exception) { diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/tests/Azure.Monitor.OpenTelemetry.Exporter.Tests/StandardMetricTests.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/tests/Azure.Monitor.OpenTelemetry.Exporter.Tests/StandardMetricTests.cs index 8bbb12c9d5cf..5d40785c9d43 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/tests/Azure.Monitor.OpenTelemetry.Exporter.Tests/StandardMetricTests.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/tests/Azure.Monitor.OpenTelemetry.Exporter.Tests/StandardMetricTests.cs @@ -26,7 +26,7 @@ public void ValidateRequestDurationMetric() var traceTelemetryItems = new List(); var metricTelemetryItems = new List(); - var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems))); + var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems)), new PerfCounterItemCounts()); var traceServiceName = new KeyValuePair("service.name", "trace.service"); var resourceAttributes = new KeyValuePair[] { traceServiceName }; @@ -75,7 +75,7 @@ public void ValidateRequestDurationMetricNew() var traceTelemetryItems = new List(); var metricTelemetryItems = new List(); - var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems))); + var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems)), new PerfCounterItemCounts()); var traceServiceName = new KeyValuePair("service.name", "trace.service"); var resourceAttributes = new KeyValuePair[] { traceServiceName }; @@ -124,7 +124,7 @@ public void ValidateRequestDurationMetricConsumerKind() var traceTelemetryItems = new List(); var metricTelemetryItems = new List(); - var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems))); + var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems)), new PerfCounterItemCounts()); var traceServiceName = new KeyValuePair("service.name", "trace.service"); var resourceAttributes = new KeyValuePair[] { traceServiceName }; @@ -178,7 +178,7 @@ public void ValidateDependencyDurationMetric(bool isAzureSDK) var traceTelemetryItems = new List(); var metricTelemetryItems = new List(); - var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems))); + var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems)), new PerfCounterItemCounts()); var traceServiceName = new KeyValuePair("service.name", "trace.service"); var resourceAttributes = new KeyValuePair[] { traceServiceName }; @@ -247,7 +247,7 @@ public void ValidateDependencyDurationMetricForProducerKind(bool isAzureSDKSpan) var traceTelemetryItems = new List(); var metricTelemetryItems = new List(); - var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems))); + var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems)), new PerfCounterItemCounts()); var traceServiceName = new KeyValuePair("service.name", "trace.service"); var resourceAttributes = new KeyValuePair[] { traceServiceName }; @@ -315,7 +315,7 @@ public void ValidateDependencyDurationMetricNew(bool isAzureSDK) var traceTelemetryItems = new List(); var metricTelemetryItems = new List(); - var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems))); + var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems)), new PerfCounterItemCounts()); var traceServiceName = new KeyValuePair("service.name", "trace.service"); var resourceAttributes = new KeyValuePair[] { traceServiceName }; @@ -384,7 +384,7 @@ public void ValidateNullStatusCode(ActivityKind kind) var traceTelemetryItems = new List(); var metricTelemetryItems = new List(); - var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems))); + var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems)), new PerfCounterItemCounts()); using var tracerProvider = Sdk.CreateTracerProviderBuilder() .SetSampler(new AlwaysOnSampler()) @@ -432,7 +432,7 @@ public void ValidateNullStatusCodeNew(ActivityKind kind) var traceTelemetryItems = new List(); var metricTelemetryItems = new List(); - var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems))); + var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems)), new PerfCounterItemCounts()); using var tracerProvider = Sdk.CreateTracerProviderBuilder() .SetSampler(new AlwaysOnSampler())