From 23d03e7923959dd9e5495fbcf2a17b087289bbde Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Thu, 13 Jul 2023 15:17:00 -0700 Subject: [PATCH] Fix standard metric for updated semantic conv --- .../src/Internals/AzMonNewListExtensions.cs | 16 ++ .../StandardMetricsExtractionProcessor.cs | 27 +++- .../StandardMetricTests.cs | 152 ++++++++++++++++++ 3 files changed, 190 insertions(+), 5 deletions(-) diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/AzMonNewListExtensions.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/AzMonNewListExtensions.cs index feb3f3a96add8..b6e8477ac4bf7 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/AzMonNewListExtensions.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/AzMonNewListExtensions.cs @@ -132,6 +132,22 @@ internal static (string? MessagingUrl, string? SourceOrTarget) GetMessagingUrlAn return null; } + /// + /// Gets dependency target from activity tag objects. + /// + internal static string? GetNewSchemaDependencyTarget(this AzMonList tagObjects, OperationType type) + { + switch (type) + { + case OperationType.Http: + return tagObjects.GetNewSchemaHttpDependencyTarget(); + case OperationType.Db: + return tagObjects.GetDbDependencyTargetAndName().DbTarget; + default: + return null; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsDefaultPort(int port) { 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 7f8168568a81e..9267a96a630f8 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/StandardMetricsExtractionProcessor.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/StandardMetricsExtractionProcessor.cs @@ -47,7 +47,7 @@ public override void OnEnd(Activity activity) if (_requestDuration.Enabled) { activity.SetTag("_MS.ProcessedByMetricExtractors", "(Name: X,Ver:'1.1')"); - ReportRequestDurationMetric(activity, SemanticConventions.AttributeHttpStatusCode); + ReportRequestDurationMetric(activity); } } if (activity.Kind == ActivityKind.Client || activity.Kind == ActivityKind.Internal) @@ -62,12 +62,12 @@ public override void OnEnd(Activity activity) // TODO: other activity kinds } - private void ReportRequestDurationMetric(Activity activity, string statusCodeAttribute) + private void ReportRequestDurationMetric(Activity activity) { string? statusCodeAttributeValue = null; foreach (ref readonly var tag in activity.EnumerateTagObjects()) { - if (tag.Key == statusCodeAttribute) + if (tag.Key == SemanticConventions.AttributeHttpResponseStatusCode || tag.Key == SemanticConventions.AttributeHttpStatusCode) { statusCodeAttributeValue = tag.Value?.ToString(); break; @@ -90,9 +90,24 @@ private void ReportDependencyDurationMetric(Activity activity) { var activityTagsProcessor = TraceHelper.EnumerateActivityTags(activity); - var dependencyTarget = activityTagsProcessor.MappedTags.GetDependencyTarget(activityTagsProcessor.activityType); + string? dependencyTarget; + string? statusCode; - var statusCode = AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeHttpStatusCode)?.ToString(); + if (activityTagsProcessor.activityType.HasFlag(OperationType.V2)) + { + // Reverting it for dependency type checks below + activityTagsProcessor.activityType &= ~OperationType.V2; + + dependencyTarget = activityTagsProcessor.MappedTags.GetNewSchemaDependencyTarget(activityTagsProcessor.activityType); + + statusCode = AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeHttpResponseStatusCode)?.ToString(); + } + else + { + dependencyTarget = activityTagsProcessor.MappedTags.GetDependencyTarget(activityTagsProcessor.activityType); + + statusCode = AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeHttpStatusCode)?.ToString(); + } var dependencyType = activityTagsProcessor.MappedTags.GetDependencyType(activityTagsProcessor.activityType); @@ -108,6 +123,8 @@ private void ReportDependencyDurationMetric(Activity activity) // Report metric _dependencyDuration.Record(activity.Duration.TotalMilliseconds, tags); + + activityTagsProcessor.Return(); } protected override void Dispose(bool disposing) 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 3776b0db23fe1..273c1507e0688 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 @@ -68,6 +68,55 @@ public void ValidateRequestDurationMetric() Assert.Equal(StandardMetricConstants.RequestDurationMetricIdValue, metricId); } + [Fact] + public void ValidateRequestDurationMetricNew() + { + var activitySource = new ActivitySource(nameof(StandardMetricTests.ValidateRequestDurationMetric)); + var traceTelemetryItems = new List(); + var metricTelemetryItems = new List(); + + var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems))); + + var traceServiceName = new KeyValuePair("service.name", "trace.service"); + var resourceAttributes = new KeyValuePair[] { traceServiceName }; + + using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .SetSampler(new AlwaysOnSampler()) + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddAttributes(resourceAttributes)) + .AddSource(nameof(StandardMetricTests.ValidateRequestDurationMetric)) + .AddProcessor(standardMetricCustomProcessor) + .AddProcessor(new BatchActivityExportProcessor(new AzureMonitorTraceExporter(new MockTransmitter(traceTelemetryItems)))) + .Build(); + + using (var activity = activitySource.StartActivity("Test", ActivityKind.Server)) + { + activity?.SetTag(SemanticConventions.AttributeHttpResponseStatusCode, 200); + } + + tracerProvider?.ForceFlush(); + + WaitForActivityExport(traceTelemetryItems); + + standardMetricCustomProcessor._meterProvider?.ForceFlush(); + + Assert.Single(metricTelemetryItems); + + var metricTelemetry = metricTelemetryItems.Last()!; + Assert.Equal("MetricData", metricTelemetry.Data.BaseType); + var metricData = (MetricsData)metricTelemetry.Data.BaseData; + Assert.True(metricData.Properties.TryGetValue(StandardMetricConstants.RequestSuccessKey, out var isSuccess)); + Assert.Equal("True", isSuccess); + Assert.True(metricData.Properties.TryGetValue(StandardMetricConstants.RequestResultCodeKey, out var resultCode)); + Assert.Equal("200", resultCode); + Assert.True(metricData.Properties.TryGetValue(StandardMetricConstants.IsAutoCollectedKey, out var isAutoCollectedFlag)); + Assert.Equal("True", isAutoCollectedFlag); + Assert.True(metricData.Properties.TryGetValue(StandardMetricConstants.CloudRoleInstanceKey, out _)); + Assert.True(metricData.Properties.TryGetValue(StandardMetricConstants.CloudRoleNameKey, out var cloudRoleName)); + Assert.Equal("trace.service", cloudRoleName); + Assert.True(metricData.Properties.TryGetValue(StandardMetricConstants.MetricIdKey, out var metricId)); + Assert.Equal(StandardMetricConstants.RequestDurationMetricIdValue, metricId); + } + [Fact] public void ValidateDependencyDurationMetric() { @@ -123,6 +172,61 @@ public void ValidateDependencyDurationMetric() Assert.Equal("www.foo.com", dependencyTarget); } + [Fact] + public void ValidateDependencyDurationMetricNew() + { + var activitySource = new ActivitySource(nameof(StandardMetricTests.ValidateDependencyDurationMetric)); + var traceTelemetryItems = new List(); + var metricTelemetryItems = new List(); + + var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems))); + + var traceServiceName = new KeyValuePair("service.name", "trace.service"); + var resourceAttributes = new KeyValuePair[] { traceServiceName }; + + using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .SetSampler(new AlwaysOnSampler()) + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddAttributes(resourceAttributes)) + .AddSource(nameof(StandardMetricTests.ValidateDependencyDurationMetric)) + .AddProcessor(standardMetricCustomProcessor) + .AddProcessor(new BatchActivityExportProcessor(new AzureMonitorTraceExporter(new MockTransmitter(traceTelemetryItems)))) + .Build(); + + using (var activity = activitySource.StartActivity("Test", ActivityKind.Client)) + { + activity?.SetTag(SemanticConventions.AttributeHttpResponseStatusCode, 200); + activity?.SetTag(SemanticConventions.AttributeHttpRequestMethod, "Get"); + activity?.SetTag(SemanticConventions.AttributeServerAddress, "foo.com"); + } + + tracerProvider?.ForceFlush(); + + WaitForActivityExport(traceTelemetryItems); + + standardMetricCustomProcessor._meterProvider?.ForceFlush(); + + Assert.Single(metricTelemetryItems); + + var metricTelemetry = metricTelemetryItems.Last()!; + Assert.Equal("MetricData", metricTelemetry.Data.BaseType); + var metricData = (MetricsData)metricTelemetry.Data.BaseData; + Assert.True(metricData.Properties.TryGetValue(StandardMetricConstants.DependencySuccessKey, out var isSuccess)); + Assert.Equal("True", isSuccess); + Assert.True(metricData.Properties.TryGetValue(StandardMetricConstants.DependencyResultCodeKey, out var resultCode)); + Assert.Equal("200", resultCode); + Assert.True(metricData.Properties.TryGetValue(StandardMetricConstants.IsAutoCollectedKey, out var isAutoCollectedFlag)); + Assert.Equal("True", isAutoCollectedFlag); + Assert.True(metricData.Properties.TryGetValue(StandardMetricConstants.CloudRoleInstanceKey, out _)); + Assert.True(metricData.Properties.TryGetValue(StandardMetricConstants.CloudRoleNameKey, out var cloudRoleName)); + Assert.Equal("trace.service", cloudRoleName); + Assert.True(metricData.Properties.TryGetValue(StandardMetricConstants.MetricIdKey, out var metricId)); + Assert.Equal(StandardMetricConstants.DependencyDurationMetricIdValue, metricId); + Assert.True(metricData.Properties.TryGetValue(StandardMetricConstants.DependencyTypeKey, out var dependencyType)); + Assert.Equal("Http", dependencyType); + Assert.True(metricData.Properties.TryGetValue(StandardMetricConstants.DependencyTargetKey, out var dependencyTarget)); + Assert.Equal("foo.com", dependencyTarget); + } + [Theory] [InlineData(ActivityKind.Server)] [InlineData(ActivityKind.Client)] @@ -171,6 +275,54 @@ public void ValidateNullStatusCode(ActivityKind kind) } } + [Theory] + [InlineData(ActivityKind.Server)] + [InlineData(ActivityKind.Client)] + public void ValidateNullStatusCodeNew(ActivityKind kind) + { + var activitySource = new ActivitySource(nameof(StandardMetricTests.ValidateNullStatusCode)); + var traceTelemetryItems = new List(); + var metricTelemetryItems = new List(); + + var standardMetricCustomProcessor = new StandardMetricsExtractionProcessor(new AzureMonitorMetricExporter(new MockTransmitter(metricTelemetryItems))); + + using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .SetSampler(new AlwaysOnSampler()) + .AddSource(nameof(StandardMetricTests.ValidateNullStatusCode)) + .AddProcessor(standardMetricCustomProcessor) + .AddProcessor(new BatchActivityExportProcessor(new AzureMonitorTraceExporter(new MockTransmitter(traceTelemetryItems)))) + .Build(); + + using (var activity = activitySource.StartActivity("Test", kind)) + { + activity?.SetTag(SemanticConventions.AttributeHttpRequestMethod, "Get"); + activity?.SetTag(SemanticConventions.AttributeUrlFull, "https://www.foo.com"); + } + + tracerProvider?.ForceFlush(); + + WaitForActivityExport(traceTelemetryItems); + + standardMetricCustomProcessor._meterProvider?.ForceFlush(); + + // Standard Metrics + Resource Metrics. + Assert.Single(metricTelemetryItems); + var metricTelemetry = metricTelemetryItems.Last()!; + Assert.Equal("MetricData", metricTelemetry.Data.BaseType); + var metricData = (MetricsData)metricTelemetry.Data.BaseData; + + if (kind == ActivityKind.Client) + { + Assert.True(metricData.Properties.TryGetValue(StandardMetricConstants.DependencyResultCodeKey, out var resultCode)); + Assert.Equal("0", resultCode); + } + else + { + Assert.True(metricData.Properties.TryGetValue(StandardMetricConstants.RequestResultCodeKey, out var resultCode)); + Assert.Equal("0", resultCode); + } + } + private void WaitForActivityExport(List traceTelemetryItems) { var result = SpinWait.SpinUntil(