diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/CHANGELOG.md b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/CHANGELOG.md index ad520cffeae6..9351eb7c2f83 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/CHANGELOG.md +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/CHANGELOG.md @@ -18,8 +18,14 @@ * Fixed a bug where LiveMetrics displays "UNKNOWN_INSTANCE" and "UNKNOWN_NAME" for "server name" and "role name" respectively. ([#45433](https://github.com/Azure/azure-sdk-for-net/pull/45433)) +* Fixed a bug in LiveMetrics that counted all manually created Dependencies as failures. + ([#45103](https://github.com/Azure/azure-sdk-for-net/pull/45103)) + ### Other Changes +* Updated field mappings for telemetry sent to LiveMetrics. + ([#45103](https://github.com/Azure/azure-sdk-for-net/pull/45103)) + * Updated log collection to default to Warning level and above for Azure SDKs via `Microsoft.Extensions.Logging`. For more information, refer to [Logging with the Azure SDK for diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/LiveMetrics/DataCollection/DocumentHelper.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/LiveMetrics/DataCollection/DocumentHelper.cs index c4727d0dbc4b..a083e3ede32b 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/LiveMetrics/DataCollection/DocumentHelper.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/LiveMetrics/DataCollection/DocumentHelper.cs @@ -108,8 +108,13 @@ internal static RemoteDependency ConvertToDependencyDocument(Activity activity) RemoteDependency remoteDependencyDocument = new() { DocumentType = DocumentType.RemoteDependency, + Duration = activity.Duration < SchemaConstants.RemoteDependencyData_Duration_LessThanDays + ? activity.Duration.ToString("c", CultureInfo.InvariantCulture) + : SchemaConstants.Duration_MaxValue, + // The following "EXTENSION" properties are used to calculate metrics. These are not serialized. Extension_Duration = activity.Duration.TotalMilliseconds, + Extension_IsSuccess = activity.Status != ActivityStatusCode.Error, }; var liveMetricsTagsProcessor = new LiveMetricsTagsProcessor(); @@ -119,41 +124,41 @@ internal static RemoteDependency ConvertToDependencyDocument(Activity activity) { case OperationType.Http: remoteDependencyDocument.Name = activity.DisplayName; - remoteDependencyDocument.CommandName = AzMonList.GetTagValue(ref liveMetricsTagsProcessor.Tags, SemanticConventions.AttributeUrlFull)?.ToString(); + + var httpUrl = AzMonList.GetTagValue(ref liveMetricsTagsProcessor.Tags, SemanticConventions.AttributeUrlFull)?.ToString(); + remoteDependencyDocument.CommandName = httpUrl; + var httpResponseStatusCode = AzMonList.GetTagValue(ref liveMetricsTagsProcessor.Tags, SemanticConventions.AttributeHttpResponseStatusCode)?.ToString(); - remoteDependencyDocument.ResultCode = httpResponseStatusCode; - remoteDependencyDocument.Duration = activity.Duration < SchemaConstants.RequestData_Duration_LessThanDays - ? activity.Duration.ToString("c", CultureInfo.InvariantCulture) - : SchemaConstants.Duration_MaxValue; + remoteDependencyDocument.ResultCode = httpResponseStatusCode ?? "0"; // The following "EXTENSION" properties are used to calculate metrics. These are not serialized. remoteDependencyDocument.Extension_IsSuccess = IsHttpSuccess(activity, httpResponseStatusCode); break; case OperationType.Db: - // Note: The Exception details are recorded in Activity.Events only if the configuration has opt-ed into this (SqlClientInstrumentationOptions.RecordException). - - var (_, dbTarget) = liveMetricsTagsProcessor.Tags.GetDbDependencyTargetAndName(); + remoteDependencyDocument.Name = activity.DisplayName; - remoteDependencyDocument.Name = dbTarget; remoteDependencyDocument.CommandName = AzMonList.GetTagValue(ref liveMetricsTagsProcessor.Tags, SemanticConventions.AttributeDbStatement)?.ToString(); - remoteDependencyDocument.Duration = activity.Duration.ToString("c", CultureInfo.InvariantCulture); // TODO: remoteDependencyDocumentIngress.ResultCode = ""; // AI SDK reads a Number property from Connection or Command objects. // As of Feb 2024, OpenTelemetry doesn't record this. This may change in the future when the semantic convention stabalizes. - // The following "EXTENSION" properties are used to calculate metrics. These are not serialized. - remoteDependencyDocument.Extension_IsSuccess = activity.Status != ActivityStatusCode.Error; - break; - case OperationType.Rpc: - // TODO RPC break; case OperationType.Messaging: - // TODO MESSAGING + remoteDependencyDocument.Name = activity.DisplayName; + + var (messagingUrl, _) = liveMetricsTagsProcessor.Tags.GetMessagingUrlAndSourceOrTarget(activity.Kind); + remoteDependencyDocument.CommandName = messagingUrl; + break; + case OperationType.Rpc: + // remoteDependencyDocument.Name = activity.DisplayName; + // remoteDependencyDocument.CommandName = AzMonList.GetTagValue(ref liveMetricsTagsProcessor.Tags, SemanticConventions.AttributeRpcService)?.ToString(); + // remoteDependencyDocument.ResultCode = AzMonList.GetTagValue(ref liveMetricsTagsProcessor.Tags, SemanticConventions.AttributeRpcStatus)?.ToString(); default: - // Unknown or Unexpected Dependency Type - remoteDependencyDocument.Name = liveMetricsTagsProcessor.ActivityType.ToString(); + // Unknown or Manual or Unexpected Dependency Type + remoteDependencyDocument.Name = activity.DisplayName; + remoteDependencyDocument.Properties.Add(new KeyValuePairString("ActivitySource", activity.Source.Name)); break; } diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/LiveMetrics/DocumentTests/HttpClientDependecyTests.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/LiveMetrics/DocumentTests/HttpClientDependecyTests.cs index 0a1f042a6a07..828d42055c46 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/LiveMetrics/DocumentTests/HttpClientDependecyTests.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/LiveMetrics/DocumentTests/HttpClientDependecyTests.cs @@ -43,7 +43,7 @@ public void VerifyHttpClientAttributes() ActivitySource.AddActivityListener(listener); // ACT - using var dependencyActivity = activitySource.StartActivity(name: "HelloWorld", kind: ActivityKind.Client); + using var dependencyActivity = activitySource.StartActivity(name: "TestActivityName", kind: ActivityKind.Client); Assert.NotNull(dependencyActivity); dependencyActivity.SetTag("http.request.method", "GET"); dependencyActivity.SetTag("url.full", "http://bing.com"); @@ -67,7 +67,7 @@ public void VerifyHttpClientAttributes() // ASSERT Assert.Equal("http://bing.com", dependencyDocument.CommandName); Assert.Equal(DocumentType.RemoteDependency, dependencyDocument.DocumentType); - Assert.Equal("HelloWorld", dependencyDocument.Name); + Assert.Equal("TestActivityName", dependencyDocument.Name); Assert.Equal("200", dependencyDocument.ResultCode); VerifyCustomProperties(dependencyDocument); diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/LiveMetrics/DocumentTests/ManualDependencyTests.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/LiveMetrics/DocumentTests/ManualDependencyTests.cs new file mode 100644 index 000000000000..5df314e5098f --- /dev/null +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/LiveMetrics/DocumentTests/ManualDependencyTests.cs @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Azure.Monitor.OpenTelemetry.AspNetCore.LiveMetrics.DataCollection; +using Azure.Monitor.OpenTelemetry.AspNetCore.Models; +using Microsoft.AspNetCore.Builder; +using OpenTelemetry; +using OpenTelemetry.Trace; +using Xunit; +using Xunit.Abstractions; + +namespace Azure.Monitor.OpenTelemetry.AspNetCore.Tests.LiveMetrics.DocumentTests +{ + public class ManualDependencyTests : DocumentTestBase + { + public ManualDependencyTests(ITestOutputHelper output) : base(output) + { + } + + [Theory] + [InlineData(ActivityStatusCode.Ok, true)] + [InlineData(ActivityStatusCode.Error, false)] + [InlineData(ActivityStatusCode.Unset, true)] + public void VerifyManualDependency(ActivityStatusCode activityStatusCode, bool expectedIsSuccess) + { + var exportedActivities = new List(); + + var testActivitySource = new ActivitySource("TestActivitySource"); + + // SETUP + using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddSource("TestActivitySource") + .AddInMemoryExporter(exportedActivities) + .Build(); + + // ACT + using (var activity = testActivitySource.StartActivity("TestActivityName", ActivityKind.Internal)) + { + activity?.SetStatus(activityStatusCode); + } + + tracerProvider.ForceFlush(); + WaitForActivityExport(exportedActivities); + + // Assert + var dependencyActivity = exportedActivities.Last(); + PrintActivity(dependencyActivity); + var dependencyDocument = DocumentHelper.ConvertToDependencyDocument(dependencyActivity); + + Assert.Null(dependencyDocument.CommandName); + Assert.Equal(DocumentType.RemoteDependency, dependencyDocument.DocumentType); + Assert.Equal("TestActivityName", dependencyDocument.Name); + Assert.Equal("TestActivitySource", dependencyDocument.Properties.Single(x => x.Key == "ActivitySource").Value); + + //// The following "EXTENSION" properties are used to calculate metrics. These are not serialized. + Assert.Equal(dependencyActivity.Duration.TotalMilliseconds, dependencyDocument.Extension_Duration); + Assert.Equal(expectedIsSuccess, dependencyDocument.Extension_IsSuccess); + } + } +} diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/LiveMetrics/DocumentTests/SqlClientDependencyTests.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/LiveMetrics/DocumentTests/SqlClientDependencyTests.cs index 83201283846a..fa6a782ff2bb 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/LiveMetrics/DocumentTests/SqlClientDependencyTests.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/LiveMetrics/DocumentTests/SqlClientDependencyTests.cs @@ -46,7 +46,7 @@ public void VerifySqlClientAttributes() ActivitySource.AddActivityListener(listener); // ACT - using var dependencyActivity = activitySource.StartActivity(name: "HelloWorld", kind: ActivityKind.Client); + using var dependencyActivity = activitySource.StartActivity(name: "TestActivityName", kind: ActivityKind.Client); Assert.NotNull(dependencyActivity); dependencyActivity.SetTag("db.system", "mssql"); dependencyActivity.SetTag("db.name", "MyDatabase"); @@ -71,7 +71,7 @@ public void VerifySqlClientAttributes() Assert.Equal("select * from sys.databases", dependencyDocument.CommandName); Assert.Equal(DocumentType.RemoteDependency, dependencyDocument.DocumentType); Assert.Equal(dependencyActivity.Duration.ToString("c"), dependencyDocument.Duration); - Assert.Equal("(localdb)\\MSSQLLocalDB | MyDatabase", dependencyDocument.Name); + Assert.Equal("TestActivityName", dependencyDocument.Name); VerifyCustomProperties(dependencyDocument); @@ -141,7 +141,7 @@ public void VerifySqlClientDependency( Assert.Equal(commandText, dependencyDocument.CommandName); Assert.Equal(DocumentType.RemoteDependency, dependencyDocument.DocumentType); Assert.Equal(dependencyActivity.Duration.ToString("c"), dependencyDocument.Duration); - Assert.Equal("(localdb)\\MSSQLLocalDB | MyDatabase", dependencyDocument.Name); + Assert.Equal("MyDatabase", dependencyDocument.Name); // The following "EXTENSION" properties are used to calculate metrics. These are not serialized. Assert.Equal(dependencyActivity.Duration.TotalMilliseconds, dependencyDocument.Extension_Duration); @@ -214,7 +214,7 @@ public void VerifySqlClientDependencyWithException( Assert.Equal(commandText, dependencyDocument.CommandName); Assert.Equal(DocumentType.RemoteDependency, dependencyDocument.DocumentType); Assert.Equal(dependencyActivity.Duration.ToString("c"), dependencyDocument.Duration); - Assert.Equal("(localdb)\\MSSQLLocalDB | MyDatabase", dependencyDocument.Name); + Assert.Equal("MyDatabase", dependencyDocument.Name); // The following "EXTENSION" properties are used to calculate metrics. These are not serialized. Assert.Equal(dependencyActivity.Duration.TotalMilliseconds, dependencyDocument.Extension_Duration);