diff --git a/docs/azure/includes/dotnet-all.md b/docs/azure/includes/dotnet-all.md index ae19ae34976dc..8d34456aaa3c6 100644 --- a/docs/azure/includes/dotnet-all.md +++ b/docs/azure/includes/dotnet-all.md @@ -145,7 +145,7 @@ | WebJobs Extensions - Web PubSub | NuGet [1.7.0](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.WebPubSub/1.7.0) | [docs](/dotnet/api/overview/azure/Microsoft.Azure.WebJobs.Extensions.WebPubSub-readme) | GitHub [1.7.0](https://github.com/Azure/azure-sdk-for-net/tree/Microsoft.Azure.WebJobs.Extensions.WebPubSub_1.7.0/sdk/webpubsub/Microsoft.Azure.WebJobs.Extensions.WebPubSub/) | | Functions extension for Blob Storage | NuGet [5.3.1](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.Storage.Blobs/5.3.1) | [docs](/dotnet/api/overview/azure/Microsoft.Azure.WebJobs.Extensions.Storage.Blobs-readme) | GitHub [5.3.1](https://github.com/Azure/azure-sdk-for-net/tree/Microsoft.Azure.WebJobs.Extensions.Storage.Blobs_5.3.1/sdk/storage/Microsoft.Azure.WebJobs.Extensions.Storage.Blobs/) | | Functions extension for Storage Queues | NuGet [5.3.1](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.Storage.Queues/5.3.1) | [docs](/dotnet/api/overview/azure/Microsoft.Azure.WebJobs.Extensions.Storage.Queues-readme) | GitHub [5.3.1](https://github.com/Azure/azure-sdk-for-net/tree/Microsoft.Azure.WebJobs.Extensions.Storage.Queues_5.3.1/sdk/storage/Microsoft.Azure.WebJobs.Extensions.Storage.Queues/) | -| unknown | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO/1.0.0-beta.1) | | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO_1.0.0-beta.1/sdk/webpubsub/Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO/) | +| unknown | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO_1.0.0-beta.1/sdk/webpubsub/Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO/) | | Resource Management - Advisor | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.Advisor/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.Advisor-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Advisor_1.0.0-beta.4/sdk/advisor/Azure.ResourceManager.Advisor/) | | Resource Management - Agrifood | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.AgFoodPlatform/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.AgFoodPlatform-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.AgFoodPlatform_1.0.0-beta.4/sdk/agrifood/Azure.ResourceManager.AgFoodPlatform/) | | Resource Management - Alerts Management | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.AlertsManagement/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.AlertsManagement-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.AlertsManagement_1.1.0/sdk/alertsmanagement/Azure.ResourceManager.AlertsManagement/) | @@ -174,7 +174,7 @@ | Resource Management - Chaos | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.Chaos/1.0.0)
NuGet [1.1.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.Chaos/1.1.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.Chaos-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Chaos_1.0.0/sdk/chaos/Azure.ResourceManager.Chaos/)
GitHub [1.1.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Chaos_1.1.0-beta.1/sdk/chaos/Azure.ResourceManager.Chaos/) | | Resource Management - Cognitive Services | NuGet [1.3.3](https://www.nuget.org/packages/Azure.ResourceManager.CognitiveServices/1.3.3) | [docs](/dotnet/api/overview/azure/ResourceManager.CognitiveServices-readme) | GitHub [1.3.3](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.CognitiveServices_1.3.3/sdk/cognitiveservices/Azure.ResourceManager.CognitiveServices/) | | Resource Management - Communication | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.Communication/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Communication-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Communication_1.2.0/sdk/communication/Azure.ResourceManager.Communication/) | -| Resource Management - Compute | NuGet [1.5.0](https://www.nuget.org/packages/Azure.ResourceManager.Compute/1.5.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Compute-readme) | GitHub [1.5.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Compute_1.5.0/sdk/compute/Azure.ResourceManager.Compute/) | +| Resource Management - Compute | NuGet [1.6.0](https://www.nuget.org/packages/Azure.ResourceManager.Compute/1.6.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Compute-readme) | GitHub [1.6.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Compute_1.6.0/sdk/compute/Azure.ResourceManager.Compute/) | | Resource Management - Confidential Ledger | NuGet [1.0.1](https://www.nuget.org/packages/Azure.ResourceManager.ConfidentialLedger/1.0.1)
NuGet [1.1.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.ConfidentialLedger/1.1.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.ConfidentialLedger-readme) | GitHub [1.0.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.ConfidentialLedger_1.0.1/sdk/confidentialledger/Azure.ResourceManager.ConfidentialLedger/)
GitHub [1.1.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.ConfidentialLedger_1.1.0-beta.4/sdk/confidentialledger/Azure.ResourceManager.ConfidentialLedger/) | | Resource Management - Confluent | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.Confluent/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Confluent-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Confluent_1.2.0/sdk/confluent/Azure.ResourceManager.Confluent/) | | Resource Management - Connected VMware vSphere | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.ConnectedVMwarevSphere/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.ConnectedVMwarevSphere-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.ConnectedVMwarevSphere_1.1.0/sdk/connectedvmwarevsphere/Azure.ResourceManager.ConnectedVMwarevSphere/) | @@ -449,7 +449,7 @@ | App Service - API Apps Service | NuGet [0.9.64](https://www.nuget.org/packages/Microsoft.Azure.AppService.ApiApps.Service/0.9.64) | | | | Code Analyzers for Durable Functions | NuGet [0.5.0](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.DurableTask.Analyzers/0.5.0) | | GitHub [0.5.0](https://github.com/Azure/azure-functions-durable-extension/tree/Analyzer-v0.3.0/src/WebJobs.Extensions.DurableTask.Analyzers) | | Cosmos DB - BulkExecutor | NuGet [2.5.1-preview](https://www.nuget.org/packages/Microsoft.Azure.CosmosDB.BulkExecutor/2.5.1-preview) | | GitHub [2.5.1-preview](https://github.com/Azure/azure-cosmosdb-bulkexecutor-dotnet-getting-started) | -| Cosmos DB - Direct | NuGet [3.35.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos.Direct/3.35.0) | | GitHub [3.35.0](https://github.com/Azure/azure-cosmos-dotnet-v3) | +| Cosmos DB - Direct | NuGet [3.35.1](https://www.nuget.org/packages/Microsoft.Azure.Cosmos.Direct/3.35.1) | | GitHub [3.35.1](https://github.com/Azure/azure-cosmos-dotnet-v3) | | Cosmos DB - Encryption | NuGet [2.0.3](https://www.nuget.org/packages/Microsoft.Azure.Cosmos.Encryption/2.0.3)
NuGet [2.1.0-preview4](https://www.nuget.org/packages/Microsoft.Azure.Cosmos.Encryption/2.1.0-preview4) | | GitHub [2.0.3](https://github.com/Azure/azure-cosmos-dotnet-v3/tree/releases/encryption/1.0.0-preview4/Microsoft.Azure.Cosmos.Encryption) | | Cosmos DB - Encryption | NuGet [1.0.0-preview07](https://www.nuget.org/packages/Microsoft.Azure.Cosmos.Encryption.Custom/1.0.0-preview07) | | | | Extensions - Caching Cosmos | NuGet [1.6.1](https://www.nuget.org/packages/Microsoft.Extensions.Caching.Cosmos/1.6.1) | | GitHub [1.6.1](https://github.com/Azure/Microsoft.Extensions.Caching.Cosmos/tree/v1.0.0-preview4) | @@ -528,12 +528,12 @@ | Microsoft.Azure.WebJobs.Extensions.Rpc | NuGet [3.0.41](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.Rpc/3.0.41) | | | | Microsoft.Azure.WebJobs.Rpc.Core | NuGet [3.0.41](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Rpc.Core/3.0.41) | | | | Service Bus - Message ID plugin | NuGet [2.0.0](https://www.nuget.org/packages/Microsoft.Azure.ServiceBus.MessageIdPlugin/2.0.0) | | | -| SignalR | NuGet [1.26.1](https://www.nuget.org/packages/Microsoft.Azure.SignalR/1.26.1)
NuGet [1.25.0-preview1-11147](https://www.nuget.org/packages/Microsoft.Azure.SignalR/1.25.0-preview1-11147) | | GitHub [1.26.1](https://github.com/Azure/azure-signalr/tree/v1.5.0/src/Microsoft.Azure.SignalR) | -| SignalR - ASP.NET | NuGet [1.26.1](https://www.nuget.org/packages/Microsoft.Azure.SignalR.AspNet/1.26.1) | | GitHub [1.26.1](https://github.com/Azure/azure-signalr/tree/v1.5.0/src/Microsoft.Azure.SignalR.AspNet) | +| SignalR | NuGet [1.27.0](https://www.nuget.org/packages/Microsoft.Azure.SignalR/1.27.0)
NuGet [1.25.0-preview1-11147](https://www.nuget.org/packages/Microsoft.Azure.SignalR/1.25.0-preview1-11147) | | GitHub [1.27.0](https://github.com/Azure/azure-signalr/tree/v1.5.0/src/Microsoft.Azure.SignalR) | +| SignalR - ASP.NET | NuGet [1.27.0](https://www.nuget.org/packages/Microsoft.Azure.SignalR.AspNet/1.27.0) | | GitHub [1.27.0](https://github.com/Azure/azure-signalr/tree/v1.5.0/src/Microsoft.Azure.SignalR.AspNet) | | SignalR - Benchmark | NuGet [1.0.0-preview1-10415](https://www.nuget.org/packages/Microsoft.Azure.SignalR.Benchmark/1.0.0-preview1-10415) | | GitHub [1.0.0-preview1-10415](https://github.com/azure/azure-signalr-bench) | -| SignalR - Protocols | NuGet [1.26.1](https://www.nuget.org/packages/Microsoft.Azure.SignalR.Protocols/1.26.1) | | GitHub [1.26.1](https://github.com/Azure/azure-signalr/tree/v1.5.0/src/Microsoft.Azure.SignalR.Protocols) | +| SignalR - Protocols | NuGet [1.27.0](https://www.nuget.org/packages/Microsoft.Azure.SignalR.Protocols/1.27.0) | | GitHub [1.27.0](https://github.com/Azure/azure-signalr/tree/v1.5.0/src/Microsoft.Azure.SignalR.Protocols) | | SignalR - Serverless Protocols | NuGet [1.10.0](https://www.nuget.org/packages/Microsoft.Azure.SignalR.Serverless.Protocols/1.10.0) | | GitHub [1.10.0](https://github.com/Azure/azure-functions-signalrservice-extension/tree/v1.2.0/src/Microsoft.Azure.SignalR.Serverless.Protocols) | -| SignalR Management | NuGet [1.26.1](https://www.nuget.org/packages/Microsoft.Azure.SignalR.Management/1.26.1) | | GitHub [1.26.1](https://github.com/Azure/azure-signalr/tree/v1.5.0/src/Microsoft.Azure.SignalR.Management) | +| SignalR Management | NuGet [1.27.0](https://www.nuget.org/packages/Microsoft.Azure.SignalR.Management/1.27.0) | | GitHub [1.27.0](https://github.com/Azure/azure-signalr/tree/v1.5.0/src/Microsoft.Azure.SignalR.Management) | | SQL Database Elastic Scale Client | NuGet [2.4.2](https://www.nuget.org/packages/Microsoft.Azure.SqlDatabase.ElasticScale.Client/2.4.2) | | GitHub [2.4.2](https://github.com/Azure/elastic-db-tools/tree/v2.3.0/Src/ElasticScale.Client) | | SQL Database Elastic Scale Service SplitMerge | NuGet [1.2.0](https://www.nuget.org/packages/Microsoft.Azure.SqlDatabase.ElasticScale.Service.SplitMerge/1.2.0) | | | | SQL Database Jobs | NuGet [0.8.3362.1](https://www.nuget.org/packages/Microsoft.Azure.SqlDatabase.Jobs/0.8.3362.1) | | | diff --git a/docs/azure/includes/dotnet-new.md b/docs/azure/includes/dotnet-new.md index 6b4f401ced3da..4b6aef58c3177 100644 --- a/docs/azure/includes/dotnet-new.md +++ b/docs/azure/includes/dotnet-new.md @@ -149,7 +149,7 @@ | WebJobs Extensions - Web PubSub | NuGet [1.7.0](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.WebPubSub/1.7.0) | [docs](/dotnet/api/overview/azure/Microsoft.Azure.WebJobs.Extensions.WebPubSub-readme) | GitHub [1.7.0](https://github.com/Azure/azure-sdk-for-net/tree/Microsoft.Azure.WebJobs.Extensions.WebPubSub_1.7.0/sdk/webpubsub/Microsoft.Azure.WebJobs.Extensions.WebPubSub/) | | Functions extension for Blob Storage | NuGet [5.3.1](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.Storage.Blobs/5.3.1) | [docs](/dotnet/api/overview/azure/Microsoft.Azure.WebJobs.Extensions.Storage.Blobs-readme) | GitHub [5.3.1](https://github.com/Azure/azure-sdk-for-net/tree/Microsoft.Azure.WebJobs.Extensions.Storage.Blobs_5.3.1/sdk/storage/Microsoft.Azure.WebJobs.Extensions.Storage.Blobs/) | | Functions extension for Storage Queues | NuGet [5.3.1](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.Storage.Queues/5.3.1) | [docs](/dotnet/api/overview/azure/Microsoft.Azure.WebJobs.Extensions.Storage.Queues-readme) | GitHub [5.3.1](https://github.com/Azure/azure-sdk-for-net/tree/Microsoft.Azure.WebJobs.Extensions.Storage.Queues_5.3.1/sdk/storage/Microsoft.Azure.WebJobs.Extensions.Storage.Queues/) | -| unknown | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO/1.0.0-beta.1) | | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO_1.0.0-beta.1/sdk/webpubsub/Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO/) | +| unknown | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO_1.0.0-beta.1/sdk/webpubsub/Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO/) | | Resource Management - Advisor | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.Advisor/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.Advisor-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Advisor_1.0.0-beta.4/sdk/advisor/Azure.ResourceManager.Advisor/) | | Resource Management - Agrifood | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.AgFoodPlatform/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.AgFoodPlatform-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.AgFoodPlatform_1.0.0-beta.4/sdk/agrifood/Azure.ResourceManager.AgFoodPlatform/) | | Resource Management - Alerts Management | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.AlertsManagement/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.AlertsManagement-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.AlertsManagement_1.1.0/sdk/alertsmanagement/Azure.ResourceManager.AlertsManagement/) | @@ -179,7 +179,7 @@ | Resource Management - Chaos | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.Chaos/1.0.0)
NuGet [1.1.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.Chaos/1.1.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.Chaos-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Chaos_1.0.0/sdk/chaos/Azure.ResourceManager.Chaos/)
GitHub [1.1.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Chaos_1.1.0-beta.1/sdk/chaos/Azure.ResourceManager.Chaos/) | | Resource Management - Cognitive Services | NuGet [1.3.3](https://www.nuget.org/packages/Azure.ResourceManager.CognitiveServices/1.3.3) | [docs](/dotnet/api/overview/azure/ResourceManager.CognitiveServices-readme) | GitHub [1.3.3](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.CognitiveServices_1.3.3/sdk/cognitiveservices/Azure.ResourceManager.CognitiveServices/) | | Resource Management - Communication | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.Communication/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Communication-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Communication_1.2.0/sdk/communication/Azure.ResourceManager.Communication/) | -| Resource Management - Compute | NuGet [1.5.0](https://www.nuget.org/packages/Azure.ResourceManager.Compute/1.5.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Compute-readme) | GitHub [1.5.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Compute_1.5.0/sdk/compute/Azure.ResourceManager.Compute/) | +| Resource Management - Compute | NuGet [1.6.0](https://www.nuget.org/packages/Azure.ResourceManager.Compute/1.6.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Compute-readme) | GitHub [1.6.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Compute_1.6.0/sdk/compute/Azure.ResourceManager.Compute/) | | Resource Management - Confidential Ledger | NuGet [1.0.1](https://www.nuget.org/packages/Azure.ResourceManager.ConfidentialLedger/1.0.1)
NuGet [1.1.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.ConfidentialLedger/1.1.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.ConfidentialLedger-readme) | GitHub [1.0.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.ConfidentialLedger_1.0.1/sdk/confidentialledger/Azure.ResourceManager.ConfidentialLedger/)
GitHub [1.1.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.ConfidentialLedger_1.1.0-beta.4/sdk/confidentialledger/Azure.ResourceManager.ConfidentialLedger/) | | Resource Management - Confluent | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.Confluent/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Confluent-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Confluent_1.2.0/sdk/confluent/Azure.ResourceManager.Confluent/) | | Resource Management - Connected VMware vSphere | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.ConnectedVMwarevSphere/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.ConnectedVMwarevSphere-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.ConnectedVMwarevSphere_1.1.0/sdk/connectedvmwarevsphere/Azure.ResourceManager.ConnectedVMwarevSphere/) | diff --git a/docs/azure/sdk/protocol-convenience-methods.md b/docs/azure/sdk/protocol-convenience-methods.md index 074435dad73ba..20c602459fac5 100644 --- a/docs/azure/sdk/protocol-convenience-methods.md +++ b/docs/azure/sdk/protocol-convenience-methods.md @@ -1,9 +1,9 @@ --- title: Understand Azure SDK client library method types -description: Learn about the key differences between Azure SDK client library protocol and convenience methods +description: Learn about the key differences between Azure SDK client library protocol and convenience methods. ms.topic: conceptual ms.custom: devx-track-dotnet, engagement-fy23, devx-track-arm-template -ms.date: 06/24/2024 +ms.date: 08/26/2024 --- # Azure SDK for .NET protocol and convenience methods overview @@ -24,19 +24,19 @@ Protocol and convenience methods implement slightly different patterns based on - [**Azure.Core**](/dotnet/api/overview/azure/core-readme) provides shared primitives, abstractions, and helpers for building modern Azure SDK client libraries. These libraries follow the [Azure SDK Design Guidelines for .NET](https://azure.github.io/azure-sdk/dotnet_introduction.html) and use package names and namespaces prefixed with *Azure*, such as [`Azure.Storage.Blobs`](/dotnet/api/overview/azure/storage.blobs-readme). -- [**System.ClientModel**](/dotnet/api/overview/azure/system.clientmodel-readme) is a core library that provides shared primitives, abstractions, and helpers for .NET service client libraries. The `System.ClientModel` library is a general purpose toolset designed to help build libraries for a variety of platforms and services, whereas the `Azure.Core` library is specifically designed for building Azure client libraries. +- [**System.ClientModel**](/dotnet/api/overview/azure/system.clientmodel-readme) is a core library that provides shared primitives, abstractions, and helpers for .NET service client libraries. The `System.ClientModel` library is a general purpose toolset designed to help build libraries for various platforms and services, whereas the `Azure.Core` library is specifically designed for building Azure client libraries. > [!NOTE] > The `Azure.Core` library itself also depends on `System.ClientModel` for various client building blocks. In the context of this article, the key differentiator for method patterns is whether a client library depends on `Azure.Core` or `System.ClientModel` directly, rather than through a transitive dependency. -The following table compares some of the request and response types used by protocol and convenience methods, based on whether the library depends on `Azure.Core` or `System.ClientModel`: +The following table compares some of the request and response types used by protocol and convenience methods, based on whether the library depends on `Azure.Core` or `System.ClientModel`. -|Request or response concern |Azure.Core | System.ClientModel | -|---------|---------|---------| -|Request body | [`RequestContent`](/dotnet/api/azure.core.requestcontent) | [`BinaryContent`](/dotnet/api/system.clientmodel.binarycontent) | -|Advanced options | [`RequestContext`](/dotnet/api/azure.requestcontext) | [`RequestOptions`](/dotnet/api/system.clientmodel.primitives.requestoptions) | -|Raw HTTP Response | [`Response`](/dotnet/api/azure.response) | [`PipelineResponse`](/dotnet/api/system.clientmodel.primitives.pipelineresponse) | -|Return type with output model | [`Response`](/dotnet/api/azure.response-1) | [`ClientResult`](/dotnet/api/system.clientmodel.clientresult-1) | +| Request or response concern | Azure.Core | System.ClientModel | +|-------------------------------|----------------------------------|-------------------------------------------------------| +| Request body | | | +| Advanced options | | | +| Raw HTTP Response | | | +| Return type with output model | | | The sections ahead provide implementation examples of these concepts. @@ -63,14 +63,14 @@ The preceding code demonstrates the following `Azure.Core` convenience method pa The following code uses a `ContentSafetyClient` to call the `AnalyzeText` protocol method: -:::code source="snippets/protocol-convenience-methods/AzureCoreProtocol/Program.cs" highlight="18-23"::: +:::code source="snippets/protocol-convenience-methods/AzureCoreProtocol/Program.cs" highlight="19-24"::: The preceding code demonstrates the following protocol method patterns: - Uses the `RequestContent` type to supply data for the request body. - Uses the `RequestContext` type to configure request options. - Returns data using the `Response` type. -- Reads `response.Content` to access the response data. +- Reads content from the response data into a [dynamic](../../csharp/advanced-topics/interop/using-type-dynamic.md) type using . > [!NOTE] > The preceding code configures the `ClientErrorBehaviors.NoThrow` for the `RequestOptions`. This option prevents non-success service responses status codes from throwing an exception, which means the app code should manually handle the response status code checks. @@ -79,13 +79,13 @@ The preceding code demonstrates the following protocol method patterns: ### Libraries that depend on System.ClientModel -Some client libraries that connect to non-Azure services use patterns similar to the libraries that depend on `Azure.Core`. For example, the [`OpenAI`](https://www.nuget.org/packages/OpenAI/2.0.0-beta.7) library provides a client that connects to the OpenAI services. These libraries are based on a library called `System.ClientModel` that has patterns similar to `Azure.Core`. +Some client libraries that connect to non-Azure services use patterns similar to the libraries that depend on `Azure.Core`. For example, the [`OpenAI`](https://www.nuget.org/packages/OpenAI) library provides a client that connects to the OpenAI services. These libraries are based on a library called `System.ClientModel` that has patterns similar to `Azure.Core`. ### [Convenience method](#tab/convenience-methods) Consider the following code that uses a `ChatClient` to call the `CompleteChat` convenience method: -:::code source="snippets/protocol-convenience-methods/SCMConvenience/Program.cs" highlight="10,11"::: +:::code source="snippets/protocol-convenience-methods/SCMConvenience/Program.cs" highlight="9"::: The preceding code demonstrates the following `System.ClientModel` convenience method patterns: @@ -112,7 +112,7 @@ The preceding code demonstrates the following `System.ClientModel` protocol meth ## Protocol and convenience method usage guidance -Although the Azure SDK for .NET client libraries provide the option to use either protocol or convenience methods, prioritize using convenience methods in most scenarios. Convenience methods are designed to improve the development experience and provide flexibility for authoring requests and handling responses. However, both method types can be used in your app as needed. Consider the following criteria when deciding which type of method to use: +Although the Azure SDK for .NET client libraries provide the option to use either protocol or convenience methods, prioritize using convenience methods in most scenarios. Convenience methods are designed to improve the development experience and provide flexibility for authoring requests and handling responses. However, both method types can be used in your app as needed. Consider the following criteria when deciding which type of method to use. Convenience methods: diff --git a/docs/azure/sdk/snippets/protocol-convenience-methods/AzureCoreConvenience/Program.cs b/docs/azure/sdk/snippets/protocol-convenience-methods/AzureCoreConvenience/Program.cs index 75b9a7e7e6c15..da8046f2a5e6e 100644 --- a/docs/azure/sdk/snippets/protocol-convenience-methods/AzureCoreConvenience/Program.cs +++ b/docs/azure/sdk/snippets/protocol-convenience-methods/AzureCoreConvenience/Program.cs @@ -2,15 +2,15 @@ using Azure.Identity; // Create the client -ContentSafetyClient safetyClient = new( +ContentSafetyClient client = new( new Uri("https://contentsafetyai.cognitiveservices.azure.com/"), new DefaultAzureCredential()); // Call the convenience method -AnalyzeTextResult result = safetyClient.AnalyzeText("What is Microsoft Azure?"); +AnalyzeTextResult result = client.AnalyzeText("What is Microsoft Azure?"); // Display the results foreach (TextCategoriesAnalysis item in result.CategoriesAnalysis) { Console.WriteLine($"{item.Category}: {item.Severity}"); -} \ No newline at end of file +} diff --git a/docs/azure/sdk/snippets/protocol-convenience-methods/AzureCoreProtocol/Program.cs b/docs/azure/sdk/snippets/protocol-convenience-methods/AzureCoreProtocol/Program.cs index d857985031f90..ea8b98343ada2 100644 --- a/docs/azure/sdk/snippets/protocol-convenience-methods/AzureCoreProtocol/Program.cs +++ b/docs/azure/sdk/snippets/protocol-convenience-methods/AzureCoreProtocol/Program.cs @@ -1,10 +1,11 @@ using Azure; using Azure.AI.ContentSafety; using Azure.Core; +using Azure.Core.Serialization; using Azure.Identity; // Create the client -ContentSafetyClient safetyClient = new( +ContentSafetyClient client = new( new Uri("https://contentsafetyai.cognitiveservices.azure.com/"), new DefaultAzureCredential()); @@ -15,19 +16,24 @@ }); // Call the protocol method -Response response = safetyClient.AnalyzeText( - prompt, - new RequestContext() +Response response = client.AnalyzeText( + prompt, + new RequestContext { - ErrorOptions = ErrorOptions.NoThrow + ErrorOptions = ErrorOptions.NoThrow, }); // Any non-200 response code from Azure AI Content Safety AnalyzeText REST API isn't considered a success response. // See REST API details at https://azure-ai-content-safety-api-docs.developer.azure-api.net/api-details#api=content-safety-service-2023-10-01&operation=TextOperations_AnalyzeText -if (response.Status != 200) +if (response.Status != 200) { - throw new RequestFailedException(response); + throw new RequestFailedException(response); } +dynamic content = response.Content.ToDynamicFromJson(JsonPropertyNames.CamelCase); + // Display the results -Console.WriteLine(response.Content); \ No newline at end of file +foreach (var item in content.CategoriesAnalysis) +{ + Console.WriteLine($"{item.Category}: {item.Severity}"); +} diff --git a/docs/azure/sdk/snippets/protocol-convenience-methods/SCMConvenience/Program.cs b/docs/azure/sdk/snippets/protocol-convenience-methods/SCMConvenience/Program.cs index 596a8c9d4b953..4954814051935 100644 --- a/docs/azure/sdk/snippets/protocol-convenience-methods/SCMConvenience/Program.cs +++ b/docs/azure/sdk/snippets/protocol-convenience-methods/SCMConvenience/Program.cs @@ -1,14 +1,12 @@ -using OpenAI; -using OpenAI.Chat; -using System.ClientModel; +using OpenAI.Chat; // Create the client -OpenAIClient client = new(""); -ChatClient chatClient = client.GetChatClient("gpt-4"); +ChatClient client = new( + model: "gpt-4o-mini", + credential: Environment.GetEnvironmentVariable("OPENAI_API_KEY")!); // Call the convenience method -ChatCompletion completion - = chatClient.CompleteChat("What is Microsoft Azure?"); +ChatCompletion completion = client.CompleteChat("What is Microsoft Azure?"); // Display the results -Console.WriteLine($"{completion.Role}: {completion.Content}"); \ No newline at end of file +Console.WriteLine($"[{completion.Role}]: {completion}"); diff --git a/docs/azure/sdk/snippets/protocol-convenience-methods/SCMConvenience/SCMConvenience.csproj b/docs/azure/sdk/snippets/protocol-convenience-methods/SCMConvenience/SCMConvenience.csproj index dfa0e53953394..941128e2f2587 100644 --- a/docs/azure/sdk/snippets/protocol-convenience-methods/SCMConvenience/SCMConvenience.csproj +++ b/docs/azure/sdk/snippets/protocol-convenience-methods/SCMConvenience/SCMConvenience.csproj @@ -8,7 +8,7 @@ - + diff --git a/docs/azure/sdk/snippets/protocol-convenience-methods/SCMProtocol/Program.cs b/docs/azure/sdk/snippets/protocol-convenience-methods/SCMProtocol/Program.cs index 88d89803729b3..4cb4dd0e8408b 100644 --- a/docs/azure/sdk/snippets/protocol-convenience-methods/SCMProtocol/Program.cs +++ b/docs/azure/sdk/snippets/protocol-convenience-methods/SCMProtocol/Program.cs @@ -1,21 +1,21 @@ -using OpenAI; -using OpenAI.Chat; +using OpenAI.Chat; using System.ClientModel; using System.ClientModel.Primitives; using System.Text.Json; // Create the client -OpenAIClient client = new(""); -ChatClient chatClient = client.GetChatClient("gpt-4"); +ChatClient client = new( + model: "gpt-4o-mini", + credential: Environment.GetEnvironmentVariable("OPENAI_API_KEY")!); // Create the request content BinaryData input = BinaryData.FromBytes(""" - { - "model": "gpt-4o", + { + "model": "gpt-4o-mini", "messages": [ { "role": "user", - "content": "What is Microsoft Azure?." + "content": "What is Microsoft Azure?" } ] } @@ -23,12 +23,12 @@ using BinaryContent content = BinaryContent.Create(input); // Call the protocol method -ClientResult result = chatClient.CompleteChat( - content, - new RequestOptions() - { - ErrorOptions = ClientErrorBehaviors.NoThrow - }); +ClientResult result = client.CompleteChat( + content, + new RequestOptions + { + ErrorOptions = ClientErrorBehaviors.NoThrow, + }); PipelineResponse response = result.GetRawResponse(); @@ -38,14 +38,12 @@ throw new ClientResultException(response); } -// Display the results BinaryData output = result.GetRawResponse().Content; - using JsonDocument outputAsJson = JsonDocument.Parse(output); -string message = outputAsJson.RootElement +JsonElement messageElement = outputAsJson.RootElement .GetProperty("choices"u8)[0] - .GetProperty("message"u8) - .GetProperty("content"u8) - .GetString(); + .GetProperty("message"u8); -Console.WriteLine(message); \ No newline at end of file +// Display the results +Console.WriteLine($@"[{messageElement.GetProperty("role"u8)}]: + {messageElement.GetProperty("content"u8)}"); diff --git a/docs/azure/sdk/snippets/protocol-convenience-methods/SCMProtocol/SCMProtocol.csproj b/docs/azure/sdk/snippets/protocol-convenience-methods/SCMProtocol/SCMProtocol.csproj index dfa0e53953394..941128e2f2587 100644 --- a/docs/azure/sdk/snippets/protocol-convenience-methods/SCMProtocol/SCMProtocol.csproj +++ b/docs/azure/sdk/snippets/protocol-convenience-methods/SCMProtocol/SCMProtocol.csproj @@ -8,7 +8,7 @@ - + diff --git a/docs/core/diagnostics/media/aspire-dashboard-auth.png b/docs/core/diagnostics/media/aspire-dashboard-auth.png new file mode 100644 index 0000000000000..7cc16f047b169 Binary files /dev/null and b/docs/core/diagnostics/media/aspire-dashboard-auth.png differ diff --git a/docs/core/diagnostics/media/aspire-dashboard-logs-thumb.png b/docs/core/diagnostics/media/aspire-dashboard-logs-thumb.png new file mode 100644 index 0000000000000..6b20aa86fce27 Binary files /dev/null and b/docs/core/diagnostics/media/aspire-dashboard-logs-thumb.png differ diff --git a/docs/core/diagnostics/media/aspire-dashboard-logs.png b/docs/core/diagnostics/media/aspire-dashboard-logs.png new file mode 100644 index 0000000000000..52d6f2267c6f3 Binary files /dev/null and b/docs/core/diagnostics/media/aspire-dashboard-logs.png differ diff --git a/docs/core/diagnostics/media/aspire-dashboard-metrics-thumb.png b/docs/core/diagnostics/media/aspire-dashboard-metrics-thumb.png new file mode 100644 index 0000000000000..86fba070fdb72 Binary files /dev/null and b/docs/core/diagnostics/media/aspire-dashboard-metrics-thumb.png differ diff --git a/docs/core/diagnostics/media/aspire-dashboard-metrics.png b/docs/core/diagnostics/media/aspire-dashboard-metrics.png new file mode 100644 index 0000000000000..88ab8b78014d2 Binary files /dev/null and b/docs/core/diagnostics/media/aspire-dashboard-metrics.png differ diff --git a/docs/core/diagnostics/media/aspire-dashboard-spans-thumb.png b/docs/core/diagnostics/media/aspire-dashboard-spans-thumb.png new file mode 100644 index 0000000000000..f8f937fdf7043 Binary files /dev/null and b/docs/core/diagnostics/media/aspire-dashboard-spans-thumb.png differ diff --git a/docs/core/diagnostics/media/aspire-dashboard-spans.png b/docs/core/diagnostics/media/aspire-dashboard-spans.png new file mode 100644 index 0000000000000..0fcad6015307c Binary files /dev/null and b/docs/core/diagnostics/media/aspire-dashboard-spans.png differ diff --git a/docs/core/diagnostics/media/aspire-dashboard-thumb.png b/docs/core/diagnostics/media/aspire-dashboard-thumb.png new file mode 100644 index 0000000000000..1dfb4b30779f2 Binary files /dev/null and b/docs/core/diagnostics/media/aspire-dashboard-thumb.png differ diff --git a/docs/core/diagnostics/media/aspire-dashboard-traces-thumb.png b/docs/core/diagnostics/media/aspire-dashboard-traces-thumb.png new file mode 100644 index 0000000000000..0b8e0b7aa036d Binary files /dev/null and b/docs/core/diagnostics/media/aspire-dashboard-traces-thumb.png differ diff --git a/docs/core/diagnostics/media/aspire-dashboard-traces.png b/docs/core/diagnostics/media/aspire-dashboard-traces.png new file mode 100644 index 0000000000000..b456479f5bdaf Binary files /dev/null and b/docs/core/diagnostics/media/aspire-dashboard-traces.png differ diff --git a/docs/core/diagnostics/media/aspire-dashboard.png b/docs/core/diagnostics/media/aspire-dashboard.png new file mode 100644 index 0000000000000..07d0417102889 Binary files /dev/null and b/docs/core/diagnostics/media/aspire-dashboard.png differ diff --git a/docs/core/diagnostics/observability-OTLP-example.md b/docs/core/diagnostics/observability-OTLP-example.md new file mode 100644 index 0000000000000..1612e22f37dfc --- /dev/null +++ b/docs/core/diagnostics/observability-OTLP-example.md @@ -0,0 +1,151 @@ +--- +title: "Example: Use OpenTelemetry with OTLP and the standalone Aspire Dashboard" +description: An introduction to observing .NET apps with OTLP and the standalone Aspire Dashboard +ms.date: 6/14/2023 +ms.topic: conceptual +--- + +# Example: Use OpenTelemetry with OTLP and the standalone Aspire Dashboard + +This is one of a series of examples to illustrate [.NET observability with OpenTelemetry](./observability-with-otel.md). + +In addition to being a standard part of .NET Aspire, the Aspire Dashboard is available as a [standalone docker container](/dotnet/aspire/fundamentals/dashboard/standalone?tabs=powershell), which provides an OTLP endpoint telemetry can be sent to, and it will visualize the logs, metrics and traces. Using the dashboard in this way has no dependency on .NET Aspire, it will visualize telemetry from any application sending it telemetry via OTLP. It works equally well for applications written in Java, GoLang, Python etc. provided that they can send their telemetry to an OTLP endpoint. + +Using the Aspire Dashboard has less configuration and setup steps than using Open Source solutions such as [Prometheus, Grafana and Jaeger](./observability-PrGrJa-example.md), but unlike those tools, the Aspire Dashboard is intended as a developer visualization tool, and not for production monitoring. + +## 1. Create the project + +Create a simple web API project by using the **ASP.NET Core Empty** template in Visual Studio or the following .NET CLI command: + +``` dotnetcli +dotnet new web +``` + +## 2. Add metrics and activity definitions + +The following code defines a new metric (`greetings.count`) for the number of times the API has been called, and a new activity source (`Otel.Example`). + +:::code language="csharp" source="snippets/OTLP-Example/csharp/Program.cs" id="Snippet_CustomMetrics"::: + +## 3. Create an API endpoint + +Insert the following between `builder.Build();` and `app.Run()` + +:::code language="csharp" source="snippets/OTLP-Example/csharp/Program.cs" id="Snippet_MapGet"::: + +Insert the following function at the bottom of the file: + +:::code language="csharp" source="snippets/OTLP-Example/csharp/Program.cs" id="Snippet_SendGreeting"::: + +> [!NOTE] +> The endpoint definition does not use anything specific to OpenTelemetry. It uses the .NET APIs for observability. + +## 4. Reference the OpenTelemetry packages + +Use the NuGet Package Manager or command line to add the following NuGet packages: + +``` xml + + + + + + +``` + +> [!NOTE] +> Use the latest versions, as the OTel APIs are constantly evolving. + +## 5. Configure OpenTelemetry with the correct providers + +Insert the following code before `builder.Build();`: + +:::code language="csharp" source="snippets/OTLP-Example/csharp/Program.cs" id="Snippet_OTEL"::: + +This code sets up OpenTelemetry with the different sources of telemetry: + +- It adds a OTel provider to ILogger to collect log records. +- It sets up metrics, registering instrumentation providers and Meters for ASP.NET and our custom Meter. +- It sets up tracing, registering instrumentation providers and our custom ActivitySource. + +It then registers the OTLP exporter using env vars for its configuration. + +## 6. Configure OTLP Environment variables + +The OTLP exporter can be configured via APIs in code, but its more common to configure it via environment variables. Add the following to _AppSettings.Development.json_ + +``` josn +"OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317", +"OTEL_SERVICE_NAME": "OTLP-Example" +``` + +You can add additional environment variables for the [.NET OTLP Exporter](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/src/OpenTelemetry.Exporter.OpenTelemetryProtocol#exporter-configuration) or common OTel variables such as `OTEL_RESOURCE_ATTRIBUTES` to define [resource attributes](https://opentelemetry.io/docs/concepts/resources/). + +> [!NOTE] +> A common gotcha is to mix up _AppSettings.json_ and _AppSettings.Development.json_, if the latter is present it will be used when you F5 from Visual Studio and any settings in _AppSettings.json_ will be ignored. + +## 7. Start the Aspire Dashboard container + +Use docker to download and run the dashboard container. + +``` powershell +docker run --rm -it ` +-p 18888:18888 ` +-p 4317:18889 ` +--name aspire-dashboard ` +mcr.microsoft.com/dotnet/aspire-dashboard:latest +``` + +Data displayed in the dashboard can be sensitive. By default, the dashboard is secured with authentication that requires a token to login. The token is displayed in the resulting output when running the container. + +[![Aspire Dashboard](./media/aspire-dashboard-auth.png)] + +Copy the url shown, and replace `0.0.0.0` with `localhost`, eg `http://localhost:18888/login?t=123456780abcdef123456780` and open that in your browser, or you can also paste the key after `/login?t=` when the login dialog is shown. The token will change each time you start the container. + +## 8. Run the project + +Run the project and then access the API with the browser or curl. + +``` dotnetcli +curl -k http://localhost:7275 +``` + +Each time you request the page, it will increment the count for the number of greetings that have been made. + +### 8.1 Log output + +The logging statements from the code are output using `ILogger`. By default, the [Console Provider](../extensions/logging.md?tabs=command-line#configure-logging) is enabled so that output is directed to the console. + +There are a couple of options for how logs can be egressed from .NET: + +- `stdout` and `stderr` output is redirected to log files by container systems such as [Kubernetes](https://kubernetes.io/docs/concepts/cluster-administration/logging/#how-nodes-handle-container-logs). +- Using logging libraries that will integrate with ILogger, these include [Serilog](https://serilog.net/) or [NLog](https://nlog-project.org/). +- Using logging providers for OTel such as OTLP. The logging section in the code from step 5 adds the OTel provider. + +The logs are shown in the dashboard as structured logs - any properties you set in the log message are extracted as fields in the log record. + +[![Logs in standalone dashboard](./media/aspire-dashboard-logs-thumb.png)](./media/aspire-dashboard-logs.png#lightbox) + +### 8.2 Viewing the metrics + +The Aspire dashboard shows metrics on a per resource basis (a resource being the OTel way of talking about sources of telemetry such as a process). When a resource is selected, the dashboard will enumerate each metric that has been sent to its OTLP endpoint by the resource. The list of metrics is dynamic, and will be updated as new metrics are received. + +[![Metrics in standalone dashboard](./media/aspire-dashboard-metrics-thumb.png)](./media/aspire-dashboard-metrics.png#lightbox) + +The view for the metrics will depend on the type of metric that is being used: + +- Counters will be shown directly. +- Histograms which track a value per request, such as a timespan or bytes sent per request, are collected into a series of buckets. The dashboard will graph the P50, P90 and P99 percentiles. Histogram results can include exemplars, which are individual datapoints together with the trace/spanId for that request. These will be shown as dots on the graph. Selecting one will navigate to the respective trace so you can see what happened to cause that value. This is useful for diagnosing outliers. +- Metrics can include dimensions, which are key/value pairs associated with individual values. The values are aggregated per dimension. Using the dropdowns in the view you can filter the results to look at specific dimensions, such as only `GET` requests, or those for a specific URL route in ASP.NET. + +### 8.3 Viewing the tracing + +The tracing view will show a list of traces - each trace is a set of activites that share the same traceId. Work is tracked with spans which represent a unit of work. Processing an ASP.NET request will create a span. Making an HttpClient request will be a span. By tracking the span's parent a heiarchy of spans can be visualized. By collecting spans from a each resource (process) we track the work that happens across a series of services. Http requests have a header which is used to pass the traceId and parent spanId to the next service. Each resource needs to collect telemetry and send it to the same collector. It will then aggregate and present a hierarchy of the spans. + +[![Traces in standalone dashboard](./media/aspire-dashboard-traces-thumb.png)](./media/aspire-dashboard-traces.png#lightbox) + +The dashboard will show a list of traces with summary information. Whenever spans with a new traceId are seen, they will get a row in the table. Clicking view will show all the spans in the trace. + +[![Spans in standalone dashboard](./media/aspire-dashboard-spans-thumb.png)](./media/aspire-dashboard-spans.png#lightbox) + +Selecting a span will show its details including any properties on the span, such as the `greeting` tag we set in [step 3](#3-create-an-api-endpoint). diff --git a/docs/core/diagnostics/observability-PrGrJa-example.md b/docs/core/diagnostics/observability-PrGrJa-example.md new file mode 100644 index 0000000000000..c7b8316894968 --- /dev/null +++ b/docs/core/diagnostics/observability-PrGrJa-example.md @@ -0,0 +1,309 @@ +--- +title: "Example: Use OpenTelemetry with Prometheus, Grafana, and Jaeger" +description: An walkthrough of how to use OpenTelemetry in .NET to export telemetry to Prometheus, Grafana, and Jaeger +ms.date: 6/14/2023 +ms.topic: conceptual +--- + +# Example: Use OpenTelemetry with Prometheus, Grafana, and Jaeger + +This example uses Prometheus for metrics collection, Grafana for creating a dashboard, and Jaeger to show distributed tracing. + +## 1. Create the project + +Create a simple web API project by using the **ASP.NET Core Empty** template in Visual Studio or the following .NET CLI command: + +``` dotnetcli +dotnet new web +``` + +## 2. Add metrics and activity definitions + +The following code defines a new metric (`greetings.count`) for the number of times the API has been called, and a new activity source (`OtPrGrYa.Example`). + +:::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_CustomMetrics"::: + +## 3. Create an API endpoint + +:::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_MapGet"::: + +:::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_SendGreeting"::: + +> [!NOTE] +> The API definition does not use anything specific to OpenTelemetry. It uses the .NET APIs for observability. + +## 4. Reference the OpenTelemetry packages + +Use the NuGet Package Manager or command line to add the following NuGet packages: + +``` xml + + + + + + + + + +``` + +> [!NOTE] +> Use the latest versions, as the OTel APIs are constantly evolving. + +## 5. Configure OpenTelemetry with the correct providers + +:::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_OTEL"::: + +This code uses ASP.NET Core instrumentation to get metrics and activities from ASP.NET Core. It also registers the `Metrics` and `ActivitySource` providers for metrics and tracing respectively. + +The code uses the Prometheus exporter for metrics, which uses ASP.NET Core to host the endpoint, so you also need to add: + +:::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_Prometheus"::: + +## 6. Run the project + +Run the project and then access the API with the browser or curl. + +``` dotnetcli +curl -k http://localhost:7275 +``` + +Each time you request the page, it will increment the count for the number of greetings that have been made. You can access the metrics endpoint using the same base url, with the path `/metrics`. + +### 6.1 Log output + +The logging statements from the code are output using `ILogger`. By default, the [Console Provider](../extensions/logging.md?tabs=command-line#configure-logging) is enabled so that output is directed to the console. + +There are a couple of options for how logs can be egressed from .NET: + +- `stdout` and `stderr` output is redirected to log files by container systems such as [Kubernetes](https://kubernetes.io/docs/concepts/cluster-administration/logging/#how-nodes-handle-container-logs). +- Using logging libraries that will integrate with ILogger, these include [Serilog](https://serilog.net/) or [NLog](https://nlog-project.org/). +- Using logging providers for OTel such as OTLP or the Azure Monitor exporter shown further below. + +### 6.2 Access the metrics + +You can access the metrics using the `/metrics` endpoint. + +``` dotnetcli +curl -k https://localhost:7275/ +Hello World! + +curl -k https://localhost:7275/metrics +# TYPE greetings_count counter +# HELP greetings_count Counts the number of greetings +greetings_count 1 1686894204856 + +# TYPE current_connections gauge +# HELP current_connections Number of connections that are currently active on the server. +current_connections{endpoint="127.0.0.1:7275"} 1 1686894204856 +current_connections{endpoint="[::1]:7275"} 0 1686894204856 +current_connections{endpoint="[::1]:5212"} 1 1686894204856 +... +``` + +The metrics output is a snapshot of the metrics at the time the endpoint is requested. The results are provided in [Prometheus exposition format](https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md), which is human readable but better understood by Prometheus. That topic is covered in the next stage. + +### 6.3 Access the tracing + +If you look at the console for the server, you'll see the output from the console trace exporter, which outputs the information in a human readable format. This should show two activities, one from your custom `ActivitySource`, and the other from ASP.NET Core: + +``` dotnetcli +Activity.TraceId: 2e00dd5e258d33fe691b965607b91d18 +Activity.SpanId: 3b7a891f55b97f1a +Activity.TraceFlags: Recorded +Activity.ParentSpanId: 645071fd0011faac +Activity.ActivitySourceName: OtPrGrYa.Example +Activity.DisplayName: GreeterActivity +Activity.Kind: Internal +Activity.StartTime: 2023-06-16T04:50:26.7675469Z +Activity.Duration: 00:00:00.0023974 +Activity.Tags: + greeting: Hello World! +Resource associated with Activity: + service.name: OTel-Prometheus-Grafana-Jaeger + service.instance.id: e1afb619-bc32-48d8-b71f-ee196dc2a76a + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: dotnet + telemetry.sdk.version: 1.5.0 + +Activity.TraceId: 2e00dd5e258d33fe691b965607b91d18 +Activity.SpanId: 645071fd0011faac +Activity.TraceFlags: Recorded +Activity.ActivitySourceName: Microsoft.AspNetCore +Activity.DisplayName: / +Activity.Kind: Server +Activity.StartTime: 2023-06-16T04:50:26.7672615Z +Activity.Duration: 00:00:00.0121259 +Activity.Tags: + net.host.name: localhost + net.host.port: 7275 + http.method: GET + http.scheme: https + http.target: / + http.url: https://localhost:7275/ + http.flavor: 1.1 + http.user_agent: curl/8.0.1 + http.status_code: 200 +Resource associated with Activity: + service.name: OTel-Prometheus-Grafana-Jaeger + service.instance.id: e1afb619-bc32-48d8-b71f-ee196dc2a76a + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: dotnet + telemetry.sdk.version: 1.5.0 +``` + +The first is the inner custom activity you created. The second is created by ASP.NET for the request and includes tags for the HTTP request properties. You will see that both have the same `TraceId`, which identifies a single transaction and in a distributed system can be used to correlate the traces from each service involved in a transaction. The IDs are transmitted as HTTP headers. ASP.NET Core assigns a `TraceId` if none is present when it receives a request. `HttpClient` includes the headers by default on outbound requests. Each activity has a `SpanId`, which is the combination of `TraceId` and `SpanId` that uniquely identify each activity. The `Greeter` activity is parented to the HTTP activity through its `ParentSpanId`, which maps to the `SpanId` of the HTTP activity. + +In a later stage, you'll feed this data into Jaeger to visualize the distributed traces. + +## 7. Collect metrics with Prometheus + +Prometheus is a metrics collection, aggregation, and time-series database system. You configure it with the metric endpoints for each service and it periodically scrapes the values and stores them in its time-series database. You can then analyze and process them as needed. + +The metrics data that's exposed in Prometheus format is a point-in-time snapshot of the process's metrics. Each time a request is made to the metrics endpoint, it will report the current values. While current values are interesting, they become more valuable when compared to historical values to see trends and detect if values are anomalous. Commonly, services have usage spikes based on the time of day or world events, such as a holiday shopping spree. By comparing the values against historical trends, you can detect if they are abnormal, or if a metric is slowly getting worse over time. + +The process doesn't store any history of these metric snapshots. Adding that capability to the process could be resource intensive. Also, in a distributed system you commonly have multiple instances of each node, so you want to be able to collect the metrics from all of them and then aggregate and compare with their historical values. + +### 7.1 Install and configure Prometheus + +Download Prometheus for your platform from [https://prometheus.io/download/](https://prometheus.io/download/) and extract the contents of the download. + +Look at the top of the output of your running server to get the port number for the **http** endpoint. For example: + +``` dotnetcli +info: Microsoft.Hosting.Lifetime[14] + Now listening on: https://localhost:7275 +info: Microsoft.Hosting.Lifetime[14] + Now listening on: http://localhost:5212 +``` + +Modify the Prometheus YAML configuration file to specify the port for your HTTP scraping endpoint and set a lower scraping interval. For example: + +``` yaml + scrape_configs: + # The job name is added as a label `job=` to any timeseries scraped from this config. + - job_name: "prometheus" + + # metrics_path defaults to '/metrics' + # scheme defaults to 'http'. + + scrape_interval: 1s # poll very quickly for a more responsive demo + static_configs: + - targets: ["localhost:5212"] +``` + +Start Prometheus, and look in the output for the port it's running on, typically 9090: + +``` dotnetcli +>prometheus.exe +... +ts=2023-06-16T05:29:02.789Z caller=web.go:562 level=info component=web msg="Start listening for connections" address=0.0.0.0:9090 +``` + +Open this URL in your browser. In the Prometheus UI you should now be able to query for your metrics. Use the highlighted button in the following image to open the metrics explorer, which shows all the available metrics. + +[![Prometheus Metrics Explorer](./media/prometheus-metrics-explorer.thumb.png)](./media/prometheus-metrics-explorer.png#lightbox) + +Select the `greetings_count` metric to see a graph of values. + +[![Graph of greetings_count](./media/prometheus-graph.thumb.png)](./media/prometheus-graph.png#lightbox) + +## 8. Use Grafana to create a metrics dashboard + +Grafana is a dashboarding product that can create dashboards and alerts based on Prometheus or other data sources. + +Download and install the OSS version of Grafana from [https://grafana.com/oss/grafana/](https://grafana.com/oss/grafana/) following the instructions for your platform. Once installed, Grafana is typically run on port 3000, so open `http://localhost:3000` in your browser. You will need to log in; the default username and password are both `admin`. + +From the hamburger menu choose connections, and then enter the text `prometheus` to select your endpoint type. Select **Create a Prometheus data source** to add a new data source. + +[![Grafana connection to prometheus](./media/grafana-connections.thumb.png)](./media/grafana-connections.png#lightbox) + +You need to set the following properties: + +- Prometheus server URL: `http://localhost:9090/` changing the port as applicable + +Select **Save & Test** to verify the configuration. + +Once you get a success message, you can configure a dashboard. Click the **building a dashboard** link shown in the popup for the success message. + +Select **Add a Visualization**, and then choose the Prometheus data source you just added as the data source. + +The dashboard panel designer should appear. In the lower half of the screen, you can define the query. + +[![Grafana query using greetings_count](./media/grafana-greetings-count-metric.thumb.png)](./media/grafana-greetings-count-metric.png#lightbox) + +Select the `greetings_count` metric, and then select **Run Queries** to see the results. + +With Grafana, you can design sophisticated dashboards that will track any number of metrics. + +Each metric in .NET can have additional dimensions, which are key-value pairs that can be used to partition the data. The ASP.NET metrics all feature a number of dimensions applicable to the counter. For example, the `current-requests` counter from `Microsoft.AspNetCore.Hosting` has the following dimensions: + +| Attribute | Type | Description | Examples | Presence | +|-----------|----------|----------------------------------------------------------|-----------------------|----------| +| `method` | `string` | HTTP request method. | `GET`; `POST`; `HEAD` | Always | +| `scheme` | `string` | The URI scheme identifying the used protocol. | `http`; `https` | Always | +| `host` | `string` | Name of the local HTTP server that received the request. | `localhost` | Always | +| `port` | `int` | Port of the local HTTP server that received the request. | `8080` | Added if not default (80 for http or 443 for https) | + +The graphs in Grafana are usually partitioned based on each unique combination of dimensions. The dimensions can be used in the Grafana queries to filter or aggregate the data. For example, if you graph `current_requests`, you'll see values partitioned based on each combination of dimensions. To filter based only on the host, add an operation of `Sum` and use `host` as the label value. + +[![Grafana current_requests by host](./media/grafana-request-count-by-host.thumb.png)](./media/grafana-request-count-by-host.png#lightbox) + +## 9. Distributed tracing with Jaeger + +In [step 6](#6-run-the-project), you saw that distributed tracing information was being exposed to the console. This information tracks units of work with activities. Some activities are created automatically by the platform, such as the one by ASP.NET to represent the handling of a request, and libraries and app code can also create activities. The greetings example has a `Greeter` activity. The activities are correlated using the `TraceId`, `SpanId`, and `ParentId` tags. + +Each process in a distributed system produces its own stream of activity information, and like metrics, you need a system to collect, store, and correlate the activities to be able to visualize the work done for each transaction. Jaeger is an open-source project to enable this collection and visualization. + +Download the latest binary distribution archive of Jaeger for your platform from [https://www.jaegertracing.io/download/](https://www.jaegertracing.io/download/). + +Then, extract the download to a local location that's easy to access. Run the *jaeger-all-in-one(.exe)* executable: + +``` dotnetcli +./jaeger-all-in-one --collector.otlp.enabled +``` + +Look through the console output to find the port where it's listening for OTLP traffic via gRPC. For example: + +``` json +{"level":"info","ts":1686963686.3854616,"caller":"otlpreceiver@v0.78.2/otlp.go:83","msg":"Starting GRPC server","endpoint":"0.0.0.0:4317"} +``` + +This output tells you it's listening on `0.0.0.0:4317`, so you can configure that port as the destination for your OTLP exporter. + +Open the `AppSettings.json` file for our project, and add the following line, changing the port if applicable. + +``` json +"OTLP_ENDPOINT_URL" : "http://localhost:4317/" +``` + +Restart the greeter process so that it can pick up the property change and start directing tracing information to Jaeger. + +Now, you should be able to see the Jaeger UI at `http://localhost:16686/` from a web browser. + +[![Jaeger query for traces](./media/jaeger-search-results.thumb.png)](./media/jaeger-search-results.png#lightbox) + +To see a list of traces, select `OTel-Prometheus-grafana-Jaeger` from the **Service** dropdown. Selecting a trace should show a gantt chart of the activities as part of that trace. Clicking on each of the operations shows more details about the activity. + +[![Jaeger Operation Details](./media/jaeger-activity-details.thumb.png)](./media/jaeger-activity-details.png#lightbox) + +In a distributed system, you want to send traces from all processes to the same Jaeger installation so that it can correlate the transactions across the system. + +You can make your app a little more interesting by having it make HTTP calls to itself. + +- Add an `HttpClient` factory to the application + + :::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_HttpClientFactory"::: + +- Add a new endpoint for making nested greeting calls + + :::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_MapNested"::: + +- Implement the endpoint so that it makes HTTP calls that can also be traced. In this case, it calls back to itself in an artificial loop (really only applicable to demo scenarios). + + :::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_SendNestedGreeting"::: + +This results in a more interesting graph with a pyramid shape for the requests, as each level waits for the response from the previous call. + +[![Jaeger nested dependency results](./media/jaeger-nested-activity-details.thumb.png)](./media/jaeger-nested-activity-details.png#lightbox) diff --git a/docs/core/diagnostics/observability-applicationinsights.md b/docs/core/diagnostics/observability-applicationinsights.md new file mode 100644 index 0000000000000..197392042da82 --- /dev/null +++ b/docs/core/diagnostics/observability-applicationinsights.md @@ -0,0 +1,78 @@ +--- +title: "Example: Use OpenTelemetry with Azure Monitor and Application Insights" +description: An walkthrough of how to use OpenTelemetry in .NET to export telemetry to Application Insights +ms.date: 8/21/2024 +ms.topic: conceptual +--- + +# Example: Use OpenTelemetry with Azure Monitor and Application Insights + +There are many commercial Application Performance Management (APM) systems available to choose from. In Azure, the primary application-monitoring product is [Application Insights](/azure/azure-monitor/app/app-insights-overview?tabs=net), which is a part of Azure Monitor. One of the advantages of an integrated APM product is that it can correlate the different observability data sources. Application Insights has rich views and analysis capabilities. + +## 1. Adding the Application Insights distro + +To make the ASP.NET experience with Azure Monitor easier, a wrapper package (called a Distro in OTel parlance) is provided that does most of the heavy lifting of configuring OpenTelemetry. + +This example is based off the [OTLP walkthrough](./observability-OTLP-example.md). Follow the steps 1-5 to create the application code using the OTLP exporter. In this example, we will extend the code to send data to Application Insights. + +Take the same project from [Step 5](./observability-OTLP-example.md#5-configure-opentelemetry-with-the-correct-providers) and add the following NuGet package: + +``` xml + + + +``` + +> [!NOTE] +> Replace the version with the latest available + +## 2. Setup the exporter + +Add the following OTel initialization code before `builder.Build();`: + +:::code language="csharp" source="snippets/OTLP-Example/csharp/Program.cs" id="Snippet_AzureMonitor"::: + +[`UseAzureMonitor()`](https://github.com/Azure/azure-sdk-for-net/blob/d51f02c6ef46f2c5d9b38a9d8974ed461cde9a81/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/OpenTelemetryBuilderExtensions.cs#L80) is the magic that will add the common instrumentation libraries and exporters for Application Insights. You just need to add your custom `Meter` and `ActivitySource` names to the registration. + +The same OTel initialization works for OTLP as for Application Insights, the difference is which exporters you selecect. You can use both in the same application, and select between them by defining the appropriate environment variables. + +## 3. Specify the connection string + +If you're not already an Azure customer, you can create a free account at [https://azure.microsoft.com/free/](https://azure.microsoft.com/free/). Log in to the Azure Portal, and either select an existing Application Insights resource or create a new one with [https://ms.portal.azure.com/#create/Microsoft.AppInsights](https://ms.portal.azure.com/#create/Microsoft.AppInsights). + +Application Insights identifies which instance to use to store and process data through an instrumentation key and connection string that are found at the top right side of the portal UI. + +[![Connection String in Azure Portal](./media/portal_ui.thumb.png)](./media/portal_ui.png#lightbox) + +If you're using Azure App Service, this connection string is automatically passed to the application as an environment variable. For other services or when running locally, you need to pass it using the `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable or in _appsettings.json_. For running locally, it's easiest to add the value to _appsettings.development.json_: + +```json +"AzureMonitor": { + "ConnectionString": "InstrumentationKey=12345678-abcd-abcd-abcd-12345678..." +} +``` + +> [!Note] +> Replace the value with the one from your instance. + +## 4. Examine your app in Application Insights + +When you run the application, telemetry will be sent to Application Insights. You should now get logs, metrics, and distributed traces for your application. Open the Application Insights resource in the Azure Portal. + +:::row::: + :::column span=""::: + **Logs** + +[![App Insights logs view](./media/azure-logs.thumb.png)](./media/azure-logs.png#lightbox) + :::column-end::: + :::column span=""::: + **Metrics** + +[![App Insights metrics view](./media/azure-metrics-graph.thumb.png)](./media/azure-metrics-graph.png#lightbox) + :::column-end::: + :::column span=""::: + **Distributed Tracing** + +[![App Insights transaction view](./media/azure-tracing.thumb.png)](./media/azure-tracing.png#lightbox) + :::column-end::: +:::row-end::: diff --git a/docs/core/diagnostics/observability-with-otel.md b/docs/core/diagnostics/observability-with-otel.md index 5ed4ba82b869d..89475d0dd2682 100644 --- a/docs/core/diagnostics/observability-with-otel.md +++ b/docs/core/diagnostics/observability-with-otel.md @@ -15,11 +15,11 @@ Observability in the context of a distributed system is the ability to monitor a Observability is commonly done using a combination of: -- Logs, which record individual operations, such as an incoming request, a failure in a specific component, or an order being placed. -- Metrics, which are measuring counters and gauges such as number of completed requests, active requests, widgets that have been sold; or a histogram of the request latency. -- Distributed tracing, which tracks requests and activities across components in a distributed system so that you can see where time is spent and track down specific failures. +- [Logs](../extensions/logging.md), which record individual operations, such as an incoming request, a failure in a specific component, or an order being placed. +- [Metrics](./metrics.md), which are measuring counters and gauges such as number of completed requests, active requests, widgets that have been sold; or a histogram of the request latency. +- [Distributed tracing](./distributed-tracing.md), which tracks requests and activities across components in a distributed system so that you can see where time is spent and track down specific failures. -Together, logs, metrics, and distributed tracing are known as the *three pillars of observability*. +Together, logs, metrics, and distributed tracing are sometimes known as the *three pillars of observability*. Each pillar might include telemetry data from: @@ -84,361 +84,53 @@ The following table describes the main packages. | [OpenTelemetry.Exporter.Prometheus.AspNetCore](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/README.md) | Exporter for Prometheus implemented using an ASP.NET Core endpoint | | [OpenTelemetry.Exporter.Zipkin](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Zipkin/README.md) | Exporter for Zipkin tracing | -## Example: Use Prometheus, Grafana, and Jaeger +## Examples -This example uses Prometheus for metrics collection, Grafana for creating a dashboard, and Jaeger to show distributed tracing. +This topic is continued with a couple of example walkthroughs for using OpenTelemetry in .NET: -### 1. Create the project +- [Example: Use OTLP and the standalone Aspire Dashboard](./observability-OTLP-example.md) +- [Example: Use OpenTelemetry with Azure Monitor and Application Insights](./observability-applicationinsights.md) +- [Example: Use OpenTelemetry with Prometheus, Grafana, and Jaeger](./observability-PrGrJa-example.md) -Create a simple web API project by using the **ASP.NET Core Empty** template in Visual Studio or the following .NET CLI command: +## OpenTelemetry in .NET Aspire -``` shell -dotnet new web -``` - -### 2. Add metrics and activity definitions - -The following code defines a new metric (`greetings.count`) for the number of times the API has been called, and a new activity source (`OtPrGrYa.Example`). - -:::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_CustomMetrics"::: - -### 3. Create an API endpoint - -:::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_MapGet"::: - -:::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_SendGreeting"::: - -> [!Note] -> The API definition does not use anything specific to OpenTelemetry. It uses the .NET APIs for observability. - -### 4. Reference the OpenTelemetry packages - -Use the NuGet Package Manager or command line to add the following NuGet packages: - -``` xml - - - - - - - - -``` - -> [!Note] -> Use the latest versions, as the OTel APIs are constantly evolving. - -### 5. Configure OpenTelemetry with the correct providers - -:::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_OTEL"::: - -This code uses ASP.NET Core instrumentation to get metrics and activities from ASP.NET Core. It also registers the `Metrics` and `ActivitySource` providers for metrics and tracing respectively. - -The code uses the Prometheus exporter for metrics, which uses ASP.NET Core to host the endpoint, so you also need to add: - -:::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_Prometheus"::: - -### 6. Run the project - -Run the project and then access the API with the browser or curl. - -``` shell -curl -k http://localhost:7275 -``` - -Each time you request the page, it will increment the count for the number of greetings that have been made. You can access the metrics endpoint using the same base url, with the path `/metrics`. - -#### 6.1 Log output - -The logging statements from the code are output using `ILogger`. By default, the [Console Provider](../extensions/logging.md?tabs=command-line#configure-logging) is enabled so that output is directed to the console. - -There are a couple of options for how logs can be egressed from .NET: - -- `stdout` and `stderr` output is redirected to log files by container systems such as [Kubernetes](https://kubernetes.io/docs/concepts/cluster-administration/logging/#how-nodes-handle-container-logs). -- Using logging libraries that will integrate with ILogger, these include [Serilog](https://serilog.net/) or [NLog](https://nlog-project.org/). -- Using logging providers for OTel such as OTLP or the Azure Monitor exporter shown further below. - -#### 6.2 Access the metrics - -You can access the metrics using the `/metrics` endpoint. - -``` shell -curl -k https://localhost:7275/ -Hello World! - -curl -k https://localhost:7275/metrics -# TYPE greetings_count counter -# HELP greetings_count Counts the number of greetings -greetings_count 1 1686894204856 - -# TYPE current_connections gauge -# HELP current_connections Number of connections that are currently active on the server. -current_connections{endpoint="127.0.0.1:7275"} 1 1686894204856 -current_connections{endpoint="[::1]:7275"} 0 1686894204856 -current_connections{endpoint="[::1]:5212"} 1 1686894204856 -... -``` - -The metrics output is a snapshot of the metrics at the time the endpoint is requested. The results are provided in [Prometheus exposition format](https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md), which is human readable but better understood by Prometheus. That topic is covered in the next stage. - -#### 6.3 Access the tracing - -If you look at the console for the server, you'll see the output from the console trace exporter, which outputs the information in a human readable format. This should show two activities, one from your custom `ActivitySource`, and the other from ASP.NET Core: - -``` shell -Activity.TraceId: 2e00dd5e258d33fe691b965607b91d18 -Activity.SpanId: 3b7a891f55b97f1a -Activity.TraceFlags: Recorded -Activity.ParentSpanId: 645071fd0011faac -Activity.ActivitySourceName: OtPrGrYa.Example -Activity.DisplayName: GreeterActivity -Activity.Kind: Internal -Activity.StartTime: 2023-06-16T04:50:26.7675469Z -Activity.Duration: 00:00:00.0023974 -Activity.Tags: - greeting: Hello World! -Resource associated with Activity: - service.name: OTel-Prometheus-Grafana-Jaeger - service.instance.id: e1afb619-bc32-48d8-b71f-ee196dc2a76a - telemetry.sdk.name: opentelemetry - telemetry.sdk.language: dotnet - telemetry.sdk.version: 1.5.0 - -Activity.TraceId: 2e00dd5e258d33fe691b965607b91d18 -Activity.SpanId: 645071fd0011faac -Activity.TraceFlags: Recorded -Activity.ActivitySourceName: Microsoft.AspNetCore -Activity.DisplayName: / -Activity.Kind: Server -Activity.StartTime: 2023-06-16T04:50:26.7672615Z -Activity.Duration: 00:00:00.0121259 -Activity.Tags: - net.host.name: localhost - net.host.port: 7275 - http.method: GET - http.scheme: https - http.target: / - http.url: https://localhost:7275/ - http.flavor: 1.1 - http.user_agent: curl/8.0.1 - http.status_code: 200 -Resource associated with Activity: - service.name: OTel-Prometheus-Grafana-Jaeger - service.instance.id: e1afb619-bc32-48d8-b71f-ee196dc2a76a - telemetry.sdk.name: opentelemetry - telemetry.sdk.language: dotnet - telemetry.sdk.version: 1.5.0 -``` - -The first is the inner custom activity you created. The second is created by ASP.NET for the request and includes tags for the HTTP request properties. You will see that both have the same `TraceId`, which identifies a single transaction and in a distributed system can be used to correlate the traces from each service involved in a transaction. The IDs are transmitted as HTTP headers. ASP.NET Core assigns a `TraceId` if none is present when it receives a request. `HttpClient` includes the headers by default on outbound requests. Each activity has a `SpanId`, which is the combination of `TraceId` and `SpanId` that uniquely identify each activity. The `Greeter` activity is parented to the HTTP activity through its `ParentSpanId`, which maps to the `SpanId` of the HTTP activity. - -In a later stage, you'll feed this data into Jaeger to visualize the distributed traces. - -### 7. Collect metrics with Prometheus - -Prometheus is a metrics collection, aggregation, and time-series database system. You configure it with the metric endpoints for each service and it periodically scrapes the values and stores them in its time-series database. You can then analyze and process them as needed. - -The metrics data that's exposed in Prometheus format is a point-in-time snapshot of the process's metrics. Each time a request is made to the metrics endpoint, it will report the current values. While current values are interesting, they become more valuable when compared to historical values to see trends and detect if values are anomalous. Commonly, services have usage spikes based on the time of day or world events, such a shopping spree on Black Friday. By comparing the values against historical trends, you can detect if they are abnormal, or if a metric is slowly getting worse over time. - -The process doesn't store any history of these metric snapshots. Adding that capability to the process could be resource intensive. Also, in a distributed system you commonly have multiple instances of each node, so you want to be able to collect the metrics from all of them and then aggregate and compare with their historical values. - -#### 7.1 Install and configure Prometheus - -Download Prometheus for your platform from [https://prometheus.io/download/](https://prometheus.io/download/) and extract the contents of the download. - -Look at the top of the output of your running server to get the port number for the **http** endpoint. For example: - -``` shell -info: Microsoft.Hosting.Lifetime[14] - Now listening on: https://localhost:7275 -info: Microsoft.Hosting.Lifetime[14] - Now listening on: http://localhost:5212 -``` - -Modify the Prometheus YAML configuration file to specify the port for your HTTP scraping endpoint and set a lower scraping interval. For example: - -``` yaml - scrape_configs: - # The job name is added as a label `job=` to any timeseries scraped from this config. - - job_name: "prometheus" - - # metrics_path defaults to '/metrics' - # scheme defaults to 'http'. - - scrape_interval: 1s # poll very quickly for a more responsive demo - static_configs: - - targets: ["localhost:5212"] -``` - -Start Prometheus, and look in the output for the port it's running on, typically 9090: - -``` shell ->prometheus.exe -... -ts=2023-06-16T05:29:02.789Z caller=web.go:562 level=info component=web msg="Start listening for connections" address=0.0.0.0:9090 -``` - -Open this URL in your browser. In the Prometheus UI you should now be able to query for your metrics. Use the highlighted button in the following image to open the metrics explorer, which shows all the available metrics. +[.NET Aspire](/dotnet/aspire/get-started/aspire-overview) is a set of extensions to .NET to make it easy to create and work with distributed applications. One of the benefits of using .NET Aspire is that telemetry is built in, using the OpenTelemetry libraries for .NET. The default project templates for .NET Aspire contain a `ServiceDefaults` project, part of which is to setup and configure OTel. The Service Defaults project is referenced and initialized by each service in a .NET Aspire solution. -[![Prometheus Metrics Explorer](./media/prometheus-metrics-explorer.thumb.png)](./media/prometheus-metrics-explorer.png#lightbox) +The Service Defaults project template includes the OTel SDK, ASP.NET, HttpClient and Runtime Instrumentation packages, and those are configured in the [`Extensions.cs`](https://github.com/dotnet/aspire/blob/main/src/Aspire.ProjectTemplates/templates/aspire-servicedefaults/Extensions.cs) file. For exporting telemetry .NET Aspire includes the OTLP exporter by default so that it can provide telemetry visualization using the Aspire Dashboard. -Select the `greetings_count` metric to see a graph of values. +The Aspire Dashboard is designed to bring telemetry observation to the local debug cycle, which enables developers to not only ensure that the applications are producing telemetry, but also use that telemetry to diagnose those applications locally. Being able to observe the calls between services is proving to be just as useful at debug time as in production. The .NET Aspire dashboard is launched automatically when you F5 the `AppHost` Project from Visual Studio or `dotnet run` the `AppHost` project. -[![Graph of greetings_count](./media/prometheus-graph.thumb.png)](./media/prometheus-graph.png#lightbox) +[![Aspire Dashboard](./media/aspire-dashboard-thumb.png)](./media/aspire-dashboard.png#lightbox) -### 8. Use Grafana to create a metrics dashboard +For more details on .NET Aspire see: -Grafana is a dashboarding product that can create dashboards and alerts based on Prometheus or other data sources. +- [Aspire Overview](/dotnet/aspire/get-started/aspire-overview) +- [Telemetry in Aspire](/dotnet/aspire/fundamentals/telemetry) +- [Aspire Dashboard](/dotnet/aspire/fundamentals/dashboard/explore) -Download and install the OSS version of Grafana from [https://grafana.com/oss/grafana/](https://grafana.com/oss/grafana/) following the instructions for your platform. Once installed, Grafana is typically run on port 3000, so open `http://localhost:3000` in your browser. You will need to log in; the default username and password are both `admin`. +### Reusing Service Defaults project without .NET Aspire Orchestration -From the hamburger menu choose connections, and then enter the text `prometheus` to select your endpoint type. Select **Create a Prometheus data source** to add a new data source. +Probably the easiest way to configure OTel for ASP.NET projects is to use the Aspire Service Defaults project, even if not using the rest of .NET Aspire such as the AppHost for orchestration. The Service Defaults project is available as a project template via Visual Studio or `dotnet new`. It configures OTel and sets up the OTLP exporter. You can then use the [OTel environment variables](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/src/OpenTelemetry.Exporter.OpenTelemetryProtocol#exporter-configuration) to configure the OTLP endpoint to send telemetry to, and provide the resource properties for the application. -[![Grafana connection to prometheus](./media/grafana-connections.thumb.png)](./media/grafana-connections.png#lightbox) +The steps to use _ServiceDefaults_ outside .NET Aspire are: -You need to set the following properties: +- Add the _ServiceDefaults_ project to the solution using Add New Project in Visual Studio, or use `dotnet new aspire-servicedefaults --output ServiceDefaults` +- Reference the _ServiceDefaults_ project from your ASP.NET application. In Visual Studio use "Add -> Project Reference" and select the _ServiceDefaults_ project" +- Call its OpenTelemetry setup function as part of your application builder initialization. -- Prometheus server URL: `http://localhost:9090/` changing the port as applicable +``` csharp +var builder = WebApplication.CreateBuilder(args); +builder.ConfigureOpenTelemetry(); -Select **Save & Test** to verify the configuration. +var app = builder.Build(); -Once you get a success message, you can configure a dashboard. Click the **building a dashboard** link shown in the popup for the success message. +app.MapGet("/", () => "Hello World!"); -Select **Add a Visualization**, and then choose the Prometheus data source you just added as the data source. - -The dashboard panel designer should appear. In the lower half of the screen, you can define the query. - -[![Grafana query using greetings_count](./media/grafana-greetings-count-metric.thumb.png)](./media/grafana-greetings-count-metric.png#lightbox) - -Select the `greetings_count` metric, and then select **Run Queries** to see the results. - -With Grafana, you can design sophisticated dashboards that will track any number of metrics. - -Each metric in .NET can have additional dimensions, which are key-value pairs that can be used to partition the data. The ASP.NET metrics all feature a number of dimensions applicable to the counter. For example, the `current-requests` counter from `Microsoft.AspNetCore.Hosting` has the following dimensions: - -| Attribute | Type | Description | Examples | Presence | -|-----------|----------|----------------------------------------------------------|-----------------------|----------| -| `method` | `string` | HTTP request method. | `GET`; `POST`; `HEAD` | Always | -| `scheme` | `string` | The URI scheme identifying the used protocol. | `http`; `https` | Always | -| `host` | `string` | Name of the local HTTP server that received the request. | `localhost` | Always | -| `port` | `int` | Port of the local HTTP server that received the request. | `8080` | Added if not default (80 for http or 443 for https) | - -The graphs in Grafana are usually partitioned based on each unique combination of dimensions. The dimensions can be used in the Grafana queries to filter or aggregate the data. For example, if you graph `current_requests`, you'll see values partitioned based on each combination of dimensions. To filter based only on the host, add an operation of `Sum` and use `host` as the label value. - -[![Grafana current_requests by host](./media/grafana-request-count-by-host.thumb.png)](./media/grafana-request-count-by-host.png#lightbox) - -### 9. Distributed tracing with Jaeger - -In [step 6](#6-run-the-project), you saw that distributed tracing information was being exposed to the console. This information tracks units of work with activities. Some activities are created automatically by the platform, such as the one by ASP.NET to represent the handling of a request, and libraries and app code can also create activities. The greetings example has a `Greeter` activity. The activities are correlated using the `TraceId`, `SpanId`, and `ParentId` tags. - -Each process in a distributed system produces its own stream of activity information, and like metrics, you need a system to collect, store, and correlate the activities to be able to visualize the work done for each transaction. Jaeger is an open-source project to enable this collection and visualization. - -Download the latest binary distribution archive of Jaeger for your platform from [https://www.jaegertracing.io/download/](https://www.jaegertracing.io/download/). - -Then, extract the download to a local location that's easy to access. Run the *jaeger-all-in-one(.exe)* executable: - -``` shell -./jaeger-all-in-one --collector.otlp.enabled +app.Run(); ``` -Look through the console output to find the port where it's listening for OTLP traffic via gRPC. For example: - -``` json -{"level":"info","ts":1686963686.3854616,"caller":"otlpreceiver@v0.78.2/otlp.go:83","msg":"Starting GRPC server","endpoint":"0.0.0.0:4317"} -``` - -This output tells you it's listening on `0.0.0.0:4317`, so you can configure that port as the destination for your OTLP exporter. - -Open the `AppSettings.json` file for our project, and add the following line, changing the port if applicable. - -``` json -"OTLP_ENDPOINT_URL" : "http://localhost:4317/" -``` - -Restart the greeter process so that it can pick up the property change and start directing tracing information to Jaeger. - -Now, you should be able to see the Jaeger UI at `http://localhost:16686/` from a web browser. - -[![Jaeger query for traces](./media/jaeger-search-results.thumb.png)](./media/jaeger-search-results.png#lightbox) - -To see a list of traces, select `OTel-Prometheus-grafana-Jaeger` from the **Service** dropdown. Selecting a trace should show a gant chart of the activities as part of that trace. Clicking on each of the operations shows more details about the activity. - -[![Jaeger Operation Details](./media/jaeger-activity-details.thumb.png)](./media/jaeger-activity-details.png#lightbox) - -In a distributed system, you want to send traces from all processes to the same Jaeger installation so that it can correlate the transactions across the system. - -You can make your app a little more interesting by having it make HTTP calls to itself. - -- Add an `HttpClient` factory to the application - - :::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_HttpClientFactory"::: - -- Add a new endpoint for making nested greeting calls - - :::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_MapNested"::: - -- Implement the endpoint so that it makes HTTP calls that can also be traced. In this case, it calls back to itself in an artificial loop (really only applicable to demo scenarios). - - :::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_SendNestedGreeting"::: - -This results in a more interesting graph with a pyramid shape for the requests, as each level waits for the response from the previous call. - -[![Jaeger nested dependency results](./media/jaeger-nested-activity-details.thumb.png)](./media/jaeger-nested-activity-details.png#lightbox) - -## Example: Use Azure Monitor and Application Insights - -In the previous example, you used separate open-source applications for metrics and tracing. There are many commercial APM systems available to choose from. In Azure, the primary application-monitoring product is [Application Insights](/azure/azure-monitor/app/app-insights-overview?tabs=net), which is part of Azure Monitor. - -One of the advantages of an integrated APM product is that it can correlate the different observability data sources. To make the ASP.NET experience with Azure Monitor easier, a wrapper package is provided that does most of the heavy lifting of configuring OpenTelemetry. - -Take the same project from [Step 5](#5-configure-opentelemetry-with-the-correct-providers) and replace the NuGet references with a single package: - -``` xml - - - -``` - -Then, replace the OTel initialization code with: - -:::code language="csharp" source="snippets/OTel-Prometheus-Grafana-Jaeger/csharp/Program.cs" id="Snippet_AzureMonitor"::: - -[`UseAzureMonitor()`](https://github.com/Azure/azure-sdk-for-net/blob/d51f02c6ef46f2c5d9b38a9d8974ed461cde9a81/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/OpenTelemetryBuilderExtensions.cs#L80) is the magic that will add the common instrumentation libraries and exporters for Application Insights. You just need to add your custom `Meter` and `ActivitySource` names to the registration. - -If you're not already an Azure customer, you can create a free account at [https://azure.microsoft.com/free/](https://azure.microsoft.com/free/). Log in to the Azure Portal, and either select an existing Application Insights resource or create a new one with [https://ms.portal.azure.com/#create/Microsoft.AppInsights](https://ms.portal.azure.com/#create/Microsoft.AppInsights). - -Application Insights identifies which instance to use to store and process data through an instrumentation key and connection string that are found at the top right side of the portal UI. - -[![Connection String in Azure Portal](./media/portal_ui.thumb.png)](./media/portal_ui.png#lightbox) - -If you're using Azure App Service, this connection string is automatically passed to the application as an environment variable. For other services or when running locally, you need to pass it using the `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable or in `appsettings.json`. For running locally, it's easiest to add the value to `appsettings.json`: - -```json -"AzureMonitor": { - "ConnectionString": "InstrumentationKey=12345678-abcd-abcd-abcd-12345678..." -} -``` - -> [!Note] -> Replace the value with the one from your instance. - -When you run the application, telemetry will be sent to Application Insights. You should now get logs, metrics, and distributed traces for your application. - -:::row::: - :::column span=""::: - **Logs** - -[![App Insights logs view](./media/azure-logs.thumb.png)](./media/azure-logs.png#lightbox) - :::column-end::: - :::column span=""::: - **Metrics** - -[![App Insights metrics view](./media/azure-metrics-graph.thumb.png)](./media/azure-metrics-graph.png#lightbox) - :::column-end::: - :::column span=""::: - **Distributed Tracing** +Service Defaults can setup the following additional functionality if required via `AddServiceDefaults()` or the specific functions: -[![App Insights transaction view](./media/azure-tracing.thumb.png)](./media/azure-tracing.png#lightbox) - :::column-end::: -:::row-end::: +- Health checks with `/health` and `/alive` endpoints +- Service discovery which will be a no-op without the rest of .NET Aspire +- Configuring resilience for HttpClient which will retry the request in the case of failures diff --git a/docs/core/diagnostics/snippets/Microsoft.Diagnostics.NETCore.Client/csharp/Microsoft.Diagnostics.NETCore.Client.Samples.csproj b/docs/core/diagnostics/snippets/Microsoft.Diagnostics.NETCore.Client/csharp/Microsoft.Diagnostics.NETCore.Client.Samples.csproj index 48cf2247783c2..0418ffd5c1455 100644 --- a/docs/core/diagnostics/snippets/Microsoft.Diagnostics.NETCore.Client/csharp/Microsoft.Diagnostics.NETCore.Client.Samples.csproj +++ b/docs/core/diagnostics/snippets/Microsoft.Diagnostics.NETCore.Client/csharp/Microsoft.Diagnostics.NETCore.Client.Samples.csproj @@ -7,7 +7,7 @@ - + diff --git a/docs/core/diagnostics/snippets/OTLP-Example/csharp/OTLP-Example.csproj b/docs/core/diagnostics/snippets/OTLP-Example/csharp/OTLP-Example.csproj new file mode 100644 index 0000000000000..06b767601408f --- /dev/null +++ b/docs/core/diagnostics/snippets/OTLP-Example/csharp/OTLP-Example.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + enable + OTLP_Example + + + + + + + + + + + + + + + diff --git a/docs/core/diagnostics/snippets/OTLP-Example/csharp/Program.cs b/docs/core/diagnostics/snippets/OTLP-Example/csharp/Program.cs new file mode 100644 index 0000000000000..b8b67efe33bbb --- /dev/null +++ b/docs/core/diagnostics/snippets/OTLP-Example/csharp/Program.cs @@ -0,0 +1,139 @@ +// +using System.Diagnostics; +using System.Diagnostics.Metrics; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; +using OpenTelemetry.Exporter; +using OpenTelemetry.Logs; +using OpenTelemetry; +using Azure.Monitor.OpenTelemetry.AspNetCore; + +var builder = WebApplication.CreateBuilder(args); + +// +// Custom metrics for the application +var greeterMeter = new Meter("OTel.Example", "1.0.0"); +var countGreetings = greeterMeter.CreateCounter("greetings.count", description: "Counts the number of greetings"); + +// Custom ActivitySource for the application +var greeterActivitySource = new ActivitySource("OTel.Example"); +// +// +builder.Services.AddHttpClient(); +// + +// +// Setup logging to be exported via OpenTelemetry +builder.Logging.AddOpenTelemetry(logging => +{ + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; +}); + +var otel = builder.Services.AddOpenTelemetry(); + +// Add Metrics for ASP.NET Core and our custom metrics and export via OTLP +otel.WithMetrics(metrics => +{ + // Metrics provider from OpenTelemetry + metrics.AddAspNetCoreInstrumentation(); + //Our custom metrics + metrics.AddMeter(greeterMeter.Name); + // Metrics provides by ASP.NET Core in .NET 8 + metrics.AddMeter("Microsoft.AspNetCore.Hosting"); + metrics.AddMeter("Microsoft.AspNetCore.Server.Kestrel"); +}); + +// Add Tracing for ASP.NET Core and our custom ActivitySource and export via OTLP +otel.WithTracing(tracing => +{ + tracing.AddAspNetCoreInstrumentation(); + tracing.AddHttpClientInstrumentation(); + tracing.AddSource(greeterActivitySource.Name); +}); + +// Export OpenTelemetry data via OTLP, using env vars for the configuration +var OtlpEndpoint = builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]; +if (OtlpEndpoint != null) +{ + otel.UseOtlpExporter(); +} +// + +// +if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) +{ + otel.UseAzureMonitor(); +} +// + + +var app = builder.Build(); + +// +app.MapGet("/", SendGreeting); +// + +// +app.MapGet("/NestedGreeting", SendNestedGreeting); +// + +app.Run(); + +// +async Task SendGreeting(ILogger logger) +{ + // Create a new Activity scoped to the method + using var activity = greeterActivitySource.StartActivity("GreeterActivity"); + + // Log a message + logger.LogInformation("Sending greeting"); + + // Increment the custom counter + countGreetings.Add(1); + + // Add a tag to the Activity + activity?.SetTag("greeting", "Hello World!"); + + return "Hello World!"; +} +// + +// +async Task SendNestedGreeting(int nestlevel, ILogger logger, HttpContext context, IHttpClientFactory clientFactory) +{ + // Create a new Activity scoped to the method + using var activity = greeterActivitySource.StartActivity("GreeterActivity"); + + if (nestlevel <= 5) + { + // Log a message + logger.LogInformation("Sending greeting, level {nestlevel}", nestlevel); + + // Increment the custom counter + countGreetings.Add(1); + + // Add a tag to the Activity + activity?.SetTag("nest-level", nestlevel); + + await context.Response.WriteAsync($"Nested Greeting, level: {nestlevel}\r\n"); + + if (nestlevel > 0) + { + var request = context.Request; + var url = new Uri($"{request.Scheme}://{request.Host}{request.Path}?nestlevel={nestlevel - 1}"); + + // Makes an http call passing the activity information as http headers + var nestedResult = await clientFactory.CreateClient().GetStringAsync(url); + await context.Response.WriteAsync(nestedResult); + } + } + else + { + // Log a message + logger.LogError("Greeting nest level {nestlevel} too high", nestlevel); + await context.Response.WriteAsync("Nest level too high, max is 5"); + } +} +// diff --git a/docs/core/diagnostics/snippets/OTLP-Example/csharp/Properties/launchSettings.json b/docs/core/diagnostics/snippets/OTLP-Example/csharp/Properties/launchSettings.json new file mode 100644 index 0000000000000..c7da9f2c0e414 --- /dev/null +++ b/docs/core/diagnostics/snippets/OTLP-Example/csharp/Properties/launchSettings.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:54006", + "sslPort": 44327 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5086", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7215;http://localhost:5086", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/docs/core/diagnostics/snippets/OTLP-Example/csharp/appsettings.Development.json b/docs/core/diagnostics/snippets/OTLP-Example/csharp/appsettings.Development.json new file mode 100644 index 0000000000000..7ce00d8d48dc0 --- /dev/null +++ b/docs/core/diagnostics/snippets/OTLP-Example/csharp/appsettings.Development.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317", + "OTEL_SERVICE_NAME": "OTLP-Example", + "OTEL_RESOURCE_ATTRIBUTES": "service.instance.id=local-test", + "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc", + "OTEL_METRIC_EXPORT_INTERVAL": 1000 +} diff --git a/docs/core/diagnostics/snippets/OTLP-Example/csharp/appsettings.json b/docs/core/diagnostics/snippets/OTLP-Example/csharp/appsettings.json new file mode 100644 index 0000000000000..602eff08761f4 --- /dev/null +++ b/docs/core/diagnostics/snippets/OTLP-Example/csharp/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" + +} diff --git a/docs/core/diagnostics/snippets/OTel-Prometheus-Grafana-Jaeger/csharp/OTel-Prometheus-Grafana-Jaeger.csproj b/docs/core/diagnostics/snippets/OTel-Prometheus-Grafana-Jaeger/csharp/OTel-Prometheus-Grafana-Jaeger.csproj index 91d5249fd4ec0..26c45c42559da 100644 --- a/docs/core/diagnostics/snippets/OTel-Prometheus-Grafana-Jaeger/csharp/OTel-Prometheus-Grafana-Jaeger.csproj +++ b/docs/core/diagnostics/snippets/OTel-Prometheus-Grafana-Jaeger/csharp/OTel-Prometheus-Grafana-Jaeger.csproj @@ -15,7 +15,7 @@ - + diff --git a/docs/framework/migration-guide/how-to-determine-which-versions-are-installed.md b/docs/framework/migration-guide/how-to-determine-which-versions-are-installed.md index 71c3e9cb577e1..65d2cb39f2a11 100644 --- a/docs/framework/migration-guide/how-to-determine-which-versions-are-installed.md +++ b/docs/framework/migration-guide/how-to-determine-which-versions-are-installed.md @@ -181,7 +181,7 @@ if ($version) { This example follows the recommended practice for version checking: -- It checks whether the value of the **Release** entry is *greater than or equal to* the value of the known release keys. +- It checks whether the value of the **Release** entry is *greater than or equal to* the value of the known release values. - It checks in order from most recent version to earliest version. ### .NET Framework 1.0-4.0 diff --git a/docs/navigate/tools-diagnostics/toc.yml b/docs/navigate/tools-diagnostics/toc.yml index c07453b11f596..6477caee0c03c 100644 --- a/docs/navigate/tools-diagnostics/toc.yml +++ b/docs/navigate/tools-diagnostics/toc.yml @@ -363,7 +363,15 @@ items: - name: ILogger Logging href: ../../core/extensions/logging.md - name: Observability with OpenTelemetry - href: ../../core/diagnostics/observability-with-otel.md + items: + - name: Overview + href: ../../core/diagnostics/observability-with-otel.md + - name: "Example: Use OpenTelemetry with OTLP and the standalone Aspire Dashboard" + href: ../../core/diagnostics/observability-OTLP-example.md + - name: "Example: Use OpenTelemetry with Prometheus, Grafana, and Jaeger" + href: ../../core/diagnostics/observability-PrGrJa-example.md + - name: "Example: Use OpenTelemetry with Azure Monitor and Application Insights" + href: ../../core/diagnostics/observability-applicationinsights.md - name: Resource monitoring href: ../../core/diagnostics/diagnostic-resource-monitoring.md displayName: resource monitoring, diff --git a/docs/standard/serialization/binaryformatter-migration-guide/winforms-applications.md b/docs/standard/serialization/binaryformatter-migration-guide/winforms-applications.md index f129e3b28392e..9fd09fc8d2b8a 100644 --- a/docs/standard/serialization/binaryformatter-migration-guide/winforms-applications.md +++ b/docs/standard/serialization/binaryformatter-migration-guide/winforms-applications.md @@ -74,6 +74,10 @@ If these statements are true, the Designer determines if that property's type ha Types that had been previously serialized into resource files via [BinaryFormatter] will continue to deserialize as expected without the need for [BinaryFormatter] as the content of ResX files are considered trusted data. In the rare case that deserialization cannot occur without [BinaryFormatter], it can be added back with an unsupported compatibility package. See [BinaryFormatter migration guide: Compatibility Package](compatibility-package.md) for details. Note that an extra step of setting `System.Resources.Extensions.UseBinaryFormatter` app context switch to `true` is required to use [BinaryFormatter] for resources. +##### Generating resource files via msbuild + +When generating resource files via msbuild, you may encounter an `MSB3825` error which specifies that your binary formatted resources may be deserialized using [BinaryFormatter] during runtime. As stated above, these resources will not deserialize using [BinaryFormatter] and this warning can be turned off by setting the property `GenerateResourceWarnOnBinaryFormatterUse` to `false`. In rare cases that deserialization cannot occur without [BinaryFormatter], it can be added back with an unsupported compatibility package. See [BinaryFormatter migration guide: Compatibility Package](compatibility-package.md) for details. Note that an additional step of setting `System.Resources.Extensions.UseBinaryFormatter` app context switch to `true` is required to use [BinaryFormatter] for resources. + ## Migrate away from BinaryFormatter If types that aren't intrinsically handled during serialization and deserialization are used in the affected scenarios, you'll need to take action to complete migration to .NET 9 or a later version. diff --git a/includes/net-framework-future.md b/includes/net-framework-future.md index 70820bc2233d7..f954e08c36988 100644 --- a/includes/net-framework-future.md +++ b/includes/net-framework-future.md @@ -1,2 +1,2 @@ > [!NOTE] -> .NET Framework [is serviced monthly](https://devblogs.microsoft.com/dotnet/category/net-framework/) with security and reliability bug fixes. .NET Framework will continue to be included with Windows, with no plans to remove it. You don't need to migrate your .NET Framework apps, but for new development, use [.NET 6 or later](~/docs/core/introduction.md). +> .NET Framework is serviced independently from Windows updates with security and reliability bug fixes. In general, security updates are released quarterly. .NET Framework will continue to be included with Windows, with no plans to remove it. You don't need to migrate your .NET Framework apps, but for new development, use [.NET 8 or later](~/docs/core/introduction.md).