From f5f71b90e639c71a1dbc40a9fc86ef7d40e4e001 Mon Sep 17 00:00:00 2001 From: IAMebonyhope Date: Fri, 19 Jan 2024 23:29:39 -0800 Subject: [PATCH 01/22] add example app preparation --- .../en/docs/languages/net/instrumentation.md | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 540c30133ce6..0e6c77cd37ff 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -7,6 +7,20 @@ description: Instrumentation for OpenTelemetry .NET {{% docs/languages/manual-intro %}} +{{% alert title="Note" color="info" %}} + +On this page you will learn how you can add traces, metrics and logs to your +code _manually_. But, you are not limited to only use one kind of +instrumentation: use [automatic instrumentation](/docs/languages/net/automatic/) +to get started and then enrich your code with manual instrumentation as needed. + +Also, for libraries your code depends on, you don't have to write +instrumentation code yourself, since they might come with OpenTelemetry built-in +_natively_ or you can make use of +[instrumentation libraries](/docs/languages/net/libraries/). + +{{% /alert %}} + ## A note on terminology .NET is different from other languages/runtimes that support OpenTelemetry. The @@ -22,6 +36,119 @@ are covered here as well as the `System.Diagnostics` API. If you prefer to use OpenTelemetry APIs instead of `System.Diagnostics` APIs, you can refer to the [OpenTelemetry API Shim docs for tracing](../shim). +## Example app preparation {#example-app} + +This page uses a modified version of the example app from +[Getting Started](/docs/languages/net/getting-started/) to help you learn +about manual instrumentation. + +You don't have to use the example app: if you want to instrument your own app or +library, follow the instructions here to adapt the process to your own code. + +### Dependencies {#example-app-dependencies} + +// TODO + +### Create and launch an HTTP Server + +To highlight the difference between instrumenting a _library_ and a standalone +_app_, split out the dice rolling into a _library file_, which then will be +imported as a dependency by the _app file_. + +Create the _library file_ named `Dice.cs` and add the following code to it: + +``````csharp +/*Dice.cs*/ +namespace otel +{ + public class Dice + { + private int min; + private int max; + + public Dice(int min, int max) + { + this.min = min; + this.max = max; + } + + public List rollTheDice(int rolls) + { + List results = new List(); + + for (int i = 0; i < rolls; i++) + { + results.Add(rollOnce()); + } + + return results; + } + + private int rollOnce() + { + return Random.Shared.Next(min, max + 1); + } + } +} +`````` + +Create the _app file_ `DiceController.cs` and add the following code to it: + +``````csharp +using Microsoft.AspNetCore.Mvc; +using System.Net; + +namespace otel +{ + public class DiceController : ControllerBase + { + private ILogger logger; + + public DiceController(ILogger logger) + { + this.logger = logger; + } + + [HttpGet("/rolldice")] + public List RollDice(string player, int? rolls) + { + if(!rolls.HasValue) + { + logger.LogError("Missing rolls parameter"); + throw new HttpRequestException("Missing rolls parameter", null, HttpStatusCode.BadRequest); + } + + var result = new Dice(1, 6).rollTheDice(rolls.Value); + + if (string.IsNullOrEmpty(player)) + { + logger.LogInformation("Anonymous player is rolling the dice: {result}", result); + } + else + { + logger.LogInformation("{player} is rolling the dice: {result}", player, result); + } + + return result; + } + } +} +`````` + +To ensure that it is working, run the application with the following command and +open in your web browser: + +```sh +dotnet build +dotnet run +``` + +You should get a list of 12 numbers in your browser window, for example: + +```text +[5,6,5,3,6,1,2,5,4,4,2,4] +``` + ## Traces ### Initializing tracing From f75bdbd10be1beb682d42283a6f63417eb1fd33b Mon Sep 17 00:00:00 2001 From: IAMebonyhope Date: Sat, 20 Jan 2024 01:21:02 -0800 Subject: [PATCH 02/22] add manual instrumentation setup --- .../en/docs/languages/net/instrumentation.md | 101 +++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 0e6c77cd37ff..437f9467dfda 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -47,7 +47,7 @@ library, follow the instructions here to adapt the process to your own code. ### Dependencies {#example-app-dependencies} -// TODO +- [.NET SDK](https://dotnet.microsoft.com/download/dotnet) 6+ ### Create and launch an HTTP Server @@ -135,6 +135,20 @@ namespace otel } `````` +Replace the program.cs content with the following code: + +``````csharp +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllers(); + +var app = builder.Build(); + +app.MapControllers(); + +app.Run(); +`````` + To ensure that it is working, run the application with the following command and open in your web browser: @@ -149,6 +163,91 @@ You should get a list of 12 numbers in your browser window, for example: [5,6,5,3,6,1,2,5,4,4,2,4] ``` +## Manual instrumentation setup + +### Dependencies + +Install the [OpenTelemetry API and SDK NuGet packages](https://www.nuget.org/profiles/OpenTelemetry): + +```sh +dotnet add package OpenTelemetry +dotnet add package OpenTelemetry.Exporter.Console +dotnet add package OpenTelemetry.Extensions.Hosting +``` + +### Initialize the SDK + +{{% alert title="Note" color="info" %}} If you’re instrumenting a library, +**skip this step**. {{% /alert %}} + +Before any other module in your application is loaded, you must initialize the SDK. +If you fail to initialize the SDK or initialize it too late, no-op +implementations will be provided to any library that acquires a tracer or meter from the API. + +To intialize the sdks, replace the program.cs content with the following code: + +``````csharp +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +var serviceName = "dice-server"; +var serviceVersion = "1.0.0"; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddOpenTelemetry() + .ConfigureResource(resource => resource.AddService( + serviceName: serviceName, + serviceVersion: serviceVersion)) + .WithTracing(tracing => tracing + .AddSource(serviceName) + .AddConsoleExporter()) + .WithMetrics(metrics => metrics + .AddMeter(serviceName) + .AddConsoleExporter()); + +builder.Logging.AddOpenTelemetry(options => options + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService( + serviceName: serviceName, + serviceVersion: serviceVersion)) + .AddConsoleExporter()); + +builder.Services.AddControllers(); + +var app = builder.Build(); + +app.MapControllers(); + +app.Run(); + +`````` + +For debugging and local development purposes, the following example exports +telemetry to the console. After you have finished setting up manual +instrumentation, you need to configure an appropriate exporter to +[export the app's telemetry data](/docs/languages/net/exporters/) to one or more +telemetry backends. + +The example also sets up the mandatory SDK default attribute `service.name`, +which holds the logical name of the service, and the optional (but highly +encouraged!) attribute `service.version`, which holds the version of the service +API or implementation. + +Alternative methods exist for setting up resource attributes. For more +information, see [Resources](/docs/languages/net/resources/). + +To verify your code, build and run the app: + +```sh +dotnet build +dotnet run +``` + +This basic setup has no effect on your app yet. You need to add code for +[traces](#traces), [metrics](#metrics), and/or [logs](#logs). + ## Traces ### Initializing tracing From 75947014ad33228d34bc6e23c0cf06b90147a7d8 Mon Sep 17 00:00:00 2001 From: IAMebonyhope Date: Sat, 20 Jan 2024 02:02:56 -0800 Subject: [PATCH 03/22] fix code snippets --- content/en/docs/languages/net/instrumentation.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 437f9467dfda..bd1fd230741d 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -57,7 +57,7 @@ imported as a dependency by the _app file_. Create the _library file_ named `Dice.cs` and add the following code to it: -``````csharp +```csharp /*Dice.cs*/ namespace otel { @@ -90,11 +90,11 @@ namespace otel } } } -`````` +``` Create the _app file_ `DiceController.cs` and add the following code to it: -``````csharp +```csharp using Microsoft.AspNetCore.Mvc; using System.Net; @@ -133,11 +133,11 @@ namespace otel } } } -`````` +``` Replace the program.cs content with the following code: -``````csharp +```csharp var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); @@ -147,7 +147,7 @@ var app = builder.Build(); app.MapControllers(); app.Run(); -`````` +``` To ensure that it is working, run the application with the following command and open in your web browser: @@ -186,7 +186,7 @@ implementations will be provided to any library that acquires a tracer or meter To intialize the sdks, replace the program.cs content with the following code: -``````csharp +```csharp using OpenTelemetry.Logs; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; @@ -222,7 +222,7 @@ app.MapControllers(); app.Run(); -`````` +``` For debugging and local development purposes, the following example exports telemetry to the console. After you have finished setting up manual From 8ead5ef3e5c0cfb09da6e0877036efa3d860e74a Mon Sep 17 00:00:00 2001 From: IAMebonyhope Date: Fri, 19 Jan 2024 23:29:39 -0800 Subject: [PATCH 04/22] add example app preparation --- .../en/docs/languages/net/instrumentation.md | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 540c30133ce6..0e6c77cd37ff 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -7,6 +7,20 @@ description: Instrumentation for OpenTelemetry .NET {{% docs/languages/manual-intro %}} +{{% alert title="Note" color="info" %}} + +On this page you will learn how you can add traces, metrics and logs to your +code _manually_. But, you are not limited to only use one kind of +instrumentation: use [automatic instrumentation](/docs/languages/net/automatic/) +to get started and then enrich your code with manual instrumentation as needed. + +Also, for libraries your code depends on, you don't have to write +instrumentation code yourself, since they might come with OpenTelemetry built-in +_natively_ or you can make use of +[instrumentation libraries](/docs/languages/net/libraries/). + +{{% /alert %}} + ## A note on terminology .NET is different from other languages/runtimes that support OpenTelemetry. The @@ -22,6 +36,119 @@ are covered here as well as the `System.Diagnostics` API. If you prefer to use OpenTelemetry APIs instead of `System.Diagnostics` APIs, you can refer to the [OpenTelemetry API Shim docs for tracing](../shim). +## Example app preparation {#example-app} + +This page uses a modified version of the example app from +[Getting Started](/docs/languages/net/getting-started/) to help you learn +about manual instrumentation. + +You don't have to use the example app: if you want to instrument your own app or +library, follow the instructions here to adapt the process to your own code. + +### Dependencies {#example-app-dependencies} + +// TODO + +### Create and launch an HTTP Server + +To highlight the difference between instrumenting a _library_ and a standalone +_app_, split out the dice rolling into a _library file_, which then will be +imported as a dependency by the _app file_. + +Create the _library file_ named `Dice.cs` and add the following code to it: + +``````csharp +/*Dice.cs*/ +namespace otel +{ + public class Dice + { + private int min; + private int max; + + public Dice(int min, int max) + { + this.min = min; + this.max = max; + } + + public List rollTheDice(int rolls) + { + List results = new List(); + + for (int i = 0; i < rolls; i++) + { + results.Add(rollOnce()); + } + + return results; + } + + private int rollOnce() + { + return Random.Shared.Next(min, max + 1); + } + } +} +`````` + +Create the _app file_ `DiceController.cs` and add the following code to it: + +``````csharp +using Microsoft.AspNetCore.Mvc; +using System.Net; + +namespace otel +{ + public class DiceController : ControllerBase + { + private ILogger logger; + + public DiceController(ILogger logger) + { + this.logger = logger; + } + + [HttpGet("/rolldice")] + public List RollDice(string player, int? rolls) + { + if(!rolls.HasValue) + { + logger.LogError("Missing rolls parameter"); + throw new HttpRequestException("Missing rolls parameter", null, HttpStatusCode.BadRequest); + } + + var result = new Dice(1, 6).rollTheDice(rolls.Value); + + if (string.IsNullOrEmpty(player)) + { + logger.LogInformation("Anonymous player is rolling the dice: {result}", result); + } + else + { + logger.LogInformation("{player} is rolling the dice: {result}", player, result); + } + + return result; + } + } +} +`````` + +To ensure that it is working, run the application with the following command and +open in your web browser: + +```sh +dotnet build +dotnet run +``` + +You should get a list of 12 numbers in your browser window, for example: + +```text +[5,6,5,3,6,1,2,5,4,4,2,4] +``` + ## Traces ### Initializing tracing From 996dc8a72d54df1c2da8fb09b4dc1068fb2f477c Mon Sep 17 00:00:00 2001 From: IAMebonyhope Date: Sat, 20 Jan 2024 01:21:02 -0800 Subject: [PATCH 05/22] add manual instrumentation setup --- .../en/docs/languages/net/instrumentation.md | 101 +++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 0e6c77cd37ff..437f9467dfda 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -47,7 +47,7 @@ library, follow the instructions here to adapt the process to your own code. ### Dependencies {#example-app-dependencies} -// TODO +- [.NET SDK](https://dotnet.microsoft.com/download/dotnet) 6+ ### Create and launch an HTTP Server @@ -135,6 +135,20 @@ namespace otel } `````` +Replace the program.cs content with the following code: + +``````csharp +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllers(); + +var app = builder.Build(); + +app.MapControllers(); + +app.Run(); +`````` + To ensure that it is working, run the application with the following command and open in your web browser: @@ -149,6 +163,91 @@ You should get a list of 12 numbers in your browser window, for example: [5,6,5,3,6,1,2,5,4,4,2,4] ``` +## Manual instrumentation setup + +### Dependencies + +Install the [OpenTelemetry API and SDK NuGet packages](https://www.nuget.org/profiles/OpenTelemetry): + +```sh +dotnet add package OpenTelemetry +dotnet add package OpenTelemetry.Exporter.Console +dotnet add package OpenTelemetry.Extensions.Hosting +``` + +### Initialize the SDK + +{{% alert title="Note" color="info" %}} If you’re instrumenting a library, +**skip this step**. {{% /alert %}} + +Before any other module in your application is loaded, you must initialize the SDK. +If you fail to initialize the SDK or initialize it too late, no-op +implementations will be provided to any library that acquires a tracer or meter from the API. + +To intialize the sdks, replace the program.cs content with the following code: + +``````csharp +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +var serviceName = "dice-server"; +var serviceVersion = "1.0.0"; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddOpenTelemetry() + .ConfigureResource(resource => resource.AddService( + serviceName: serviceName, + serviceVersion: serviceVersion)) + .WithTracing(tracing => tracing + .AddSource(serviceName) + .AddConsoleExporter()) + .WithMetrics(metrics => metrics + .AddMeter(serviceName) + .AddConsoleExporter()); + +builder.Logging.AddOpenTelemetry(options => options + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService( + serviceName: serviceName, + serviceVersion: serviceVersion)) + .AddConsoleExporter()); + +builder.Services.AddControllers(); + +var app = builder.Build(); + +app.MapControllers(); + +app.Run(); + +`````` + +For debugging and local development purposes, the following example exports +telemetry to the console. After you have finished setting up manual +instrumentation, you need to configure an appropriate exporter to +[export the app's telemetry data](/docs/languages/net/exporters/) to one or more +telemetry backends. + +The example also sets up the mandatory SDK default attribute `service.name`, +which holds the logical name of the service, and the optional (but highly +encouraged!) attribute `service.version`, which holds the version of the service +API or implementation. + +Alternative methods exist for setting up resource attributes. For more +information, see [Resources](/docs/languages/net/resources/). + +To verify your code, build and run the app: + +```sh +dotnet build +dotnet run +``` + +This basic setup has no effect on your app yet. You need to add code for +[traces](#traces), [metrics](#metrics), and/or [logs](#logs). + ## Traces ### Initializing tracing From 02afc008b5af61f6f05787ae0ff45535d7ad2fc9 Mon Sep 17 00:00:00 2001 From: IAMebonyhope Date: Sat, 20 Jan 2024 02:02:56 -0800 Subject: [PATCH 06/22] fix code snippets --- content/en/docs/languages/net/instrumentation.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 437f9467dfda..bd1fd230741d 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -57,7 +57,7 @@ imported as a dependency by the _app file_. Create the _library file_ named `Dice.cs` and add the following code to it: -``````csharp +```csharp /*Dice.cs*/ namespace otel { @@ -90,11 +90,11 @@ namespace otel } } } -`````` +``` Create the _app file_ `DiceController.cs` and add the following code to it: -``````csharp +```csharp using Microsoft.AspNetCore.Mvc; using System.Net; @@ -133,11 +133,11 @@ namespace otel } } } -`````` +``` Replace the program.cs content with the following code: -``````csharp +```csharp var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); @@ -147,7 +147,7 @@ var app = builder.Build(); app.MapControllers(); app.Run(); -`````` +``` To ensure that it is working, run the application with the following command and open in your web browser: @@ -186,7 +186,7 @@ implementations will be provided to any library that acquires a tracer or meter To intialize the sdks, replace the program.cs content with the following code: -``````csharp +```csharp using OpenTelemetry.Logs; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; @@ -222,7 +222,7 @@ app.MapControllers(); app.Run(); -`````` +``` For debugging and local development purposes, the following example exports telemetry to the console. After you have finished setting up manual From 020f5d4ae418b61c9119488c9219c2900d6e1ae3 Mon Sep 17 00:00:00 2001 From: Hope Oluwalolope Date: Tue, 23 Jan 2024 23:34:26 -0800 Subject: [PATCH 07/22] Apply suggestions from code review Co-authored-by: Fabrizio Ferri-Benedetti --- .../en/docs/languages/net/instrumentation.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index bd1fd230741d..1958145deb31 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -10,13 +10,13 @@ description: Instrumentation for OpenTelemetry .NET {{% alert title="Note" color="info" %}} On this page you will learn how you can add traces, metrics and logs to your -code _manually_. But, you are not limited to only use one kind of -instrumentation: use [automatic instrumentation](/docs/languages/net/automatic/) +code manually. You are not limited to using one kind of instrumentation: +you can also use [automatic instrumentation](/docs/languages/net/automatic/) to get started and then enrich your code with manual instrumentation as needed. Also, for libraries your code depends on, you don't have to write -instrumentation code yourself, since they might come with OpenTelemetry built-in -_natively_ or you can make use of +instrumentation code yourself, since they might come with OpenTelemetry +or you can make use of [instrumentation libraries](/docs/languages/net/libraries/). {{% /alert %}} @@ -135,7 +135,7 @@ namespace otel } ``` -Replace the program.cs content with the following code: +Replace the content of the program.cs file with the following code: ```csharp var builder = WebApplication.CreateBuilder(args); @@ -184,7 +184,8 @@ Before any other module in your application is loaded, you must initialize the S If you fail to initialize the SDK or initialize it too late, no-op implementations will be provided to any library that acquires a tracer or meter from the API. -To intialize the sdks, replace the program.cs content with the following code: +To initialize the SKDs, replace the content of the program.cs file with the +following code: ```csharp using OpenTelemetry.Logs; @@ -228,13 +229,12 @@ For debugging and local development purposes, the following example exports telemetry to the console. After you have finished setting up manual instrumentation, you need to configure an appropriate exporter to [export the app's telemetry data](/docs/languages/net/exporters/) to one or more -telemetry backends. +telemetry back ends. The example also sets up the mandatory SDK default attribute `service.name`, -which holds the logical name of the service, and the optional (but highly -encouraged!) attribute `service.version`, which holds the version of the service +which holds the logical name of the service, and the optional, but highly +encouraged, attribute `service.version`, which holds the version of the service API or implementation. - Alternative methods exist for setting up resource attributes. For more information, see [Resources](/docs/languages/net/resources/). From a977d672044fae3ddd2f5899764af5a83e3f016f Mon Sep 17 00:00:00 2001 From: IAMebonyhope Date: Thu, 8 Feb 2024 00:33:30 -0800 Subject: [PATCH 08/22] update traces section --- .../en/docs/languages/net/instrumentation.md | 335 +++++++++++------- 1 file changed, 198 insertions(+), 137 deletions(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 1958145deb31..95144effff29 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -51,11 +51,11 @@ library, follow the instructions here to adapt the process to your own code. ### Create and launch an HTTP Server -To highlight the difference between instrumenting a _library_ and a standalone -_app_, split out the dice rolling into a _library file_, which then will be -imported as a dependency by the _app file_. +To highlight the difference between instrumenting a library and a standalone +app, split out the dice rolling into a library file, which then will be +imported as a dependency by the app file. -Create the _library file_ named `Dice.cs` and add the following code to it: +Create the library file named `Dice.cs` and add the following code to it: ```csharp /*Dice.cs*/ @@ -92,9 +92,10 @@ namespace otel } ``` -Create the _app file_ `DiceController.cs` and add the following code to it: +Create the app file `DiceController.cs` and add the following code to it: ```csharp +/*DiceController.cs*/ using Microsoft.AspNetCore.Mvc; using System.Net; @@ -167,25 +168,36 @@ You should get a list of 12 numbers in your browser window, for example: ### Dependencies -Install the [OpenTelemetry API and SDK NuGet packages](https://www.nuget.org/profiles/OpenTelemetry): +Install the following OpenTelemetry NuGet packages: + +[OpenTelemetry.Exporter.Console](https://www.nuget.org/packages/OpenTelemetry.Exporter.Console) + +[OpenTelemetry.Extensions.Hosting](https://www.nuget.org/packages/OpenTelemetry.Extensions.Hosting) + ```sh -dotnet add package OpenTelemetry dotnet add package OpenTelemetry.Exporter.Console dotnet add package OpenTelemetry.Extensions.Hosting ``` +For ASP.NET Core-based applications, also install the AspNetCore instrumentation package + +[OpenTelemetry.Instrumentation.AspNetCore](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AspNetCore) + +```sh +dotnet add package OpenTelemetry.Instrumentation.AspNetCore +``` + + ### Initialize the SDK {{% alert title="Note" color="info" %}} If you’re instrumenting a library, **skip this step**. {{% /alert %}} -Before any other module in your application is loaded, you must initialize the SDK. -If you fail to initialize the SDK or initialize it too late, no-op -implementations will be provided to any library that acquires a tracer or meter from the API. +It is important to configure an instance of the OpenTelemetry SDK as early as possible in your application. -To initialize the SKDs, replace the content of the program.cs file with the -following code: +To initialize the OpenTelemetry SDK for an ASP.NET Core app like in the case of the example app, +replace the content of the program.cs file with the following code: ```csharp using OpenTelemetry.Logs; @@ -193,7 +205,8 @@ using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; -var serviceName = "dice-server"; +// Ideally, you will want this name to come from a config file, constants file, etc. +var serviceName = "Dice.*"; var serviceVersion = "1.0.0"; var builder = WebApplication.CreateBuilder(args); @@ -225,11 +238,41 @@ app.Run(); ``` -For debugging and local development purposes, the following example exports +If initializing the OpenTelemetry SDK for a console app, +add the following code at the beginning of your program, during any important startup operations. + +```csharp +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +//... + +var serviceName = "MyServiceName"; +var serviceVersion = "1.0.0"; + +var tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddSource(serviceName) + .ConfigureResource(resource => + resource.AddService( + serviceName: serviceName, + serviceVersion: serviceVersion)) + .AddConsoleExporter() + .Build(); + +var meterProvider = Sdk.CreateMeterProviderBuilder() + .AddMeter(serviceName) + .AddConsoleExporter() + .Build(); + +//... + +``` + +For debugging and local development purposes, the example exports telemetry to the console. After you have finished setting up manual instrumentation, you need to configure an appropriate exporter to [export the app's telemetry data](/docs/languages/net/exporters/) to one or more -telemetry back ends. +telemetry backends. The example also sets up the mandatory SDK default attribute `service.name`, which holds the logical name of the service, and the optional, but highly @@ -245,184 +288,202 @@ dotnet build dotnet run ``` -This basic setup has no effect on your app yet. You need to add code for -[traces](#traces), [metrics](#metrics), and/or [logs](#logs). ## Traces -### Initializing tracing +### Initialize Tracing + +{{% alert title="Note" color="info" %}} If you’re instrumenting a library, +**skip this step**. {{% /alert %}} + +To enable [tracing](/docs/concepts/signals/traces/) in your app, you'll need to +have an initialized +[`TracerProvider`](/docs/concepts/signals/traces/#tracer-provider) that will let +you create a [`Tracer`](/docs/concepts/signals/traces/#tracer). -There are two main ways to initialize [tracing](/docs/concepts/signals/traces/), -depending on whether you're using a console app or something that's ASP.NET -Core-based. +If a `TracerProvider` is not created, the OpenTelemetry APIs for tracing will +use a no-op implementation and fail to generate data. -#### Console app +If you followed the instructions to [initialize the SDK](#initialize-the-sdk) +above, you have a `TracerProvider` setup for you already. You can continue with +[acquiring a tracer](#acquiring-a-tracer). -To start tracing in a console app, you need to create a tracer provider. -First, ensure that you have the right packages: +### Setting up an ActivitySource -```sh -dotnet add package OpenTelemetry -dotnet add package OpenTelemetry.Exporter.Console -``` +Anywhere in your application where you write manual tracing code should configure an +[`ActivitySource`](/docs/concepts/signals/traces/#tracer), which will be how you +trace operations with [`Activity`](/docs/concepts/signals/traces/#spans) +elements. -And then use code like this at the beginning of your program, during any -important startup operations. +For example: ```csharp -using OpenTelemetry; -using OpenTelemetry.Resources; -using OpenTelemetry.Trace; - -// ... -var serviceName = "MyServiceName"; -var serviceVersion = "1.0.0"; +public static class Telemetry +{ + //... -using var tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddSource(serviceName) - .ConfigureResource(resource => - resource.AddService( - serviceName: serviceName, - serviceVersion: serviceVersion)) - .AddConsoleExporter() - .Build(); + public static readonly ActivitySource MyActivitySource = new("name", "version"); -// ... + //... +} ``` +The values of name and version should uniquely identify the Instrumentation Scope, such as the package, module or class name. While the name is required, the version is still recommended despite being optional. -This is also where you can configure instrumentation libraries. +It’s generally recommended to define `ActivitySource` once per app/service that is been instrumented, but you can instantiate several `ActivitySource`s if that suits your scenario -Note that this sample uses the Console Exporter. If you are exporting to another -endpoint, you'll have to use a different exporter. +In the case of the example app, there are two places where the `ActivitySource` will be instantiated with an appropriate Instrumentation Scope: -#### ASP.NET Core +First, in the application file `DiceController.cs`: -To start tracing in an ASP.NET Core-based app, use the OpenTelemetry extensions -for ASP.NET Core setup. +```csharp +using Microsoft.AspNetCore.Mvc; +using System.Diagnostics; +using System.Net; -First, ensure that you have the right packages: +namespace otel +{ + public class DiceController : ControllerBase + { + private ILogger logger; + + public static readonly ActivitySource MyActivitySource = new("Dice.Server", "1.0.0"); -```sh -dotnet add package OpenTelemetry -dotnet add package OpenTelemetry.Extensions.Hosting -dotnet add package OpenTelemetry.Exporter.Console -``` + public DiceController(ILogger logger) + { + this.logger = logger; + } -Then you can install the Instrumentation package + //... + } +} -```sh -dotnet add package OpenTelemetry.Instrumentation.AspNetCore --prerelease ``` -Note that the `--prerelease` flag is required for all instrumentation packages -because they are all dependent on naming conventions for attributes/labels -(Semantic Conventions) that aren't yet classed as stable. - -Next, configure it in your ASP.NET Core startup routine where you have access to -an `IServiceCollection`. +And second, in the library file `Dice.cs`: ```csharp -using OpenTelemetry; -using OpenTelemetry.Resources; -using OpenTelemetry.Trace; -// Define some important constants and the activity source. -// These can come from a config file, constants file, etc. -var serviceName = "MyCompany.MyProduct.MyService"; -var serviceVersion = "1.0.0"; +using System.Diagnostics; -var builder = WebApplication.CreateBuilder(args); +namespace otel +{ + public class Dice + { + public static readonly ActivitySource MyActivitySource = new("Dice.Lib", "1.0.0"); + + private int min; + private int max; -// Configure important OpenTelemetry settings, the console exporter -builder.Services.AddOpenTelemetry() - .WithTracing(b => - { - b - .AddSource(serviceName) - .ConfigureResource(resource => - resource.AddService( - serviceName: serviceName, - serviceVersion: serviceVersion)) - .AddAspNetCoreInstrumentation() - .AddConsoleExporter(); - }); -``` + public Dice(int min, int max) + { + this.min = min; + this.max = max; + } -This is also where you can configure instrumentation libraries. + //... + } +} -Note that this sample uses the Console Exporter. If you are exporting to another -endpoint, you'll have to use a different exporter. +``` -### Setting up an ActivitySource +### Create Activities -Once tracing is initialized, you can configure an -[`ActivitySource`](/docs/concepts/signals/traces/#tracer), which will be how you -trace operations with [`Activity`](/docs/concepts/signals/traces/#spans) -elements. +Now that you have [activitySources](/docs/concepts/signals/traces/#tracer) initialized, you can create [activities](/docs/concepts/signals/traces/#spans). -Typically, an `ActivitySource` is instantiated once per app/service that is -being instrumented, so it's a good idea to instantiate it once in a shared -location. It is also typically named the same as the Service Name. +The code below illustrates how to create an activity. ```csharp -using System.Diagnostics; - -public static class Telemetry +public List rollTheDice(int rolls) { - //... + var activity = MyActivitySource.StartActivity("rollTheDice"); + List results = new List(); - // Name it after the service name for your app. - // It can come from a config file, constants file, etc. - public static readonly ActivitySource MyActivitySource = new(TelemetryConstants.ServiceName); + for (int i = 0; i < rolls; i++) + { + results.Add(rollOnce()); + } - //... + activity?.Stop(); + return results; } + ``` +Note, that it’s required to `Stop()` the activity, otherwise it will not be exported. -You can instantiate several `ActivitySource`s if that suits your scenario, -although it is generally sufficient to just have one defined per service. +If you followed the instructions using the [example app](#example-app) up to +this point, you can copy the code above in your library file `Dice.cs`. You +should now be able to see activities/spans emitted from your app. -### Creating Activities +Start your app as follows, and then send it requests by visiting http://localhost:8080/rolldice?rolls=12 with your browser or curl. -To create an [`Activity`](/docs/concepts/signals/traces/#spans), give it a name -and create it from your -[`ActivitySource`](/docs/concepts/signals/traces/#tracer). +```sh +dotnet run +``` -```csharp -using var myActivity = MyActivitySource.StartActivity("SayHello"); +After a while, you should see the spans printed in the console by the +`ConsoleExporter`, something like this: + +```json +service.name: Dice.* +service.version: 1.0.0 +service.instance.id: 17a824ba-9734-413d-951e-c44414b6b93b +telemetry.sdk.name: opentelemetry +telemetry.sdk.language: dotnet +telemetry.sdk.version: 1.7.0 + +Resource associated with Metric: + service.name: Dice.* + service.version: 1.0.0 + service.instance.id: 17a824ba-9734-413d-951e-c44414b6b93b + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: dotnet + telemetry.sdk.version: 1.7.0 +Activity.TraceId: b322c0ce4bf1941cd6a476a454d78434 +Activity.SpanId: 7bbfc1522b5595bb +Activity.TraceFlags: Recorded +Activity.ParentSpanId: c761f5e5ca3886bf +Activity.ActivitySourceName: Dice.Lib +Activity.DisplayName: rollTheDice +Activity.Kind: Internal +Activity.StartTime: 2024-02-08T08:10:47.5310248Z +Activity.Duration: 00:00:00.0002703 +Resource associated with Activity: + service.name: Dice.* + service.version: 1.0.0 + service.instance.id: 17a824ba-9734-413d-951e-c44414b6b93b + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: dotnet + telemetry.sdk.version: 1.7.0 -// do work that 'myActivity' will now track ``` -### Creating nested Activities - -If you have a distinct sub-operation you'd like to track as a part of another -one, you can create activities to represent the relationship. +### Create nested Activities +Nested [spans](/docs/concepts/signals/traces/#spans) let you track work that's +nested in nature. For example, the `rollOnce()` function below represents a +nested operation. The following sample creates a nested span that tracks +`rollOnce()`: ```csharp -public static void ParentOperation() +private int rollOnce() { - using var parentActivity = MyActivitySource.StartActivity("ParentActivity"); - - // Do some work tracked by parentActivity - - ChildOperation(); - - // Finish up work tracked by parentActivity again + var childActivity = MyActivitySource.StartActivity("rollOnce"); + + try + { + return Random.Shared.Next(min, max + 1); + } + finally + { + childActivity?.Stop(); + } } -public static void ChildOperation() -{ - using var childActivity = MyActivitySource.StartActivity("ChildActivity"); - - // Track work in ChildOperation with childActivity -} ``` -When you view spans in a trace visualization tool, `ChildActivity` will be -tracked as a nested operation under `ParentActivity`. +When you view the spans in a trace visualization tool, `rollOnce` childActivity will be +tracked as a nested operation under `rollTheDice` activity. ### Nested Activities in the same scope From 6ae0416637e1daa34efc7f560f4e5783578eada1 Mon Sep 17 00:00:00 2001 From: IAMebonyhope Date: Thu, 8 Feb 2024 16:03:41 -0800 Subject: [PATCH 09/22] Add traces section --- .../en/docs/languages/net/instrumentation.md | 215 +++++++----------- 1 file changed, 82 insertions(+), 133 deletions(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 95144effff29..9ec7f3d33b6f 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -468,16 +468,19 @@ nested operation. The following sample creates a nested span that tracks ```csharp private int rollOnce() { + int result; var childActivity = MyActivitySource.StartActivity("rollOnce"); try { - return Random.Shared.Next(min, max + 1); + result = Random.Shared.Next(min, max + 1); } finally { childActivity?.Stop(); } + + return result; } ``` @@ -485,185 +488,131 @@ private int rollOnce() When you view the spans in a trace visualization tool, `rollOnce` childActivity will be tracked as a nested operation under `rollTheDice` activity. -### Nested Activities in the same scope +### Get the current Activity -You may wish to create a parent-child relationship in the same scope. Although -possible, this is generally not recommended because you need to be careful to -end any nested `Activity` when you expect it to end. +Sometimes it’s helpful to do something with the current/active span at a particular point in program execution. ```csharp -public static void DoWork() -{ - using var parentActivity = MyActivitySource.StartActivity("ParentActivity"); - - // Do some work tracked by parentActivity - - using (var childActivity = MyActivitySource.StartActivity("ChildActivity")) - { - // Do some "child" work in the same function - } - - // Finish up work tracked by parentActivity again -} +var activity = Activity.Current; ``` -In the preceding example, `childActivity` is ended because the scope of the -`using` block is explicitly defined, rather than scoped to `DoWork` itself like -`parentActivity`. +### Activity Tags -### Creating independent Activities - -The previous examples showed how to create Activities that follow a nested -hierarchy. In some cases, you'll want to create independent Activities that are -siblings of the same root rather than being nested. +Tags (the equivalent of [Attributes](/docs/concepts/signals/traces/#attributes)) let you attach key/value +pairs to an [`Activity`](/docs/concepts/signals/traces/#spans) so it carries more +information about the current operation that it's tracking. ```csharp -public static void DoWork() +private int rollOnce() { - using var parent = MyActivitySource.StartActivity("parent"); - - using (var child1 = DemoSource.StartActivity("child1")) + int result; + var childActivity = MyActivitySource.StartActivity("rollOnce"); + + try { - // Do some work that 'child1' tracks + result = Random.Shared.Next(min, max + 1); + childActivity?.SetTag("dicelib.rolled", result); } - - using (var child2 = DemoSource.StartActivity("child2")) + finally { - // Do some work that 'child2' tracks + childActivity?.Stop(); } - // 'child1' and 'child2' both share 'parent' as a parent, but are independent - // from one another + return result; } ``` -### Creating new root Activities +### Create Activities with events -If you wish to create a new root Activity, you'll need to "de-parent" from the -current activity. +[Spans](/docs/concepts/signals/traces/#spans) can be annotated with named events +(called [Span Events](/docs/concepts/signals/traces/#span-events)) that can +carry zero or more [Span Attributes](#span-attributes), each of which itself is +a key:value map paired automatically with a timestamp. ```csharp -public static void DoWork() -{ - var previous = Activity.Current; - Activity.Current = null; - - var newRoot = MyActivitySource.StartActivity("NewRoot"); - - // Re-set the previous Current Activity so the trace isn't messed up - Activity.Current = previous; -} -``` - -### Get the current Activity - -Sometimes it's helpful to access whatever the current `Activity` is at a point -in time so you can enrich it with more information. - -```csharp -var activity = Activity.Current; -// may be null if there is none +myActivity?.AddEvent(new("Init")); +... +myActivity?.AddEvent(new("End")); ``` -Note that `using` is not used in the prior example. Doing so will end current -`Activity`, which is not likely to be desired. - -### Add tags to an Activity - -Tags (the equivalent of -[`Attributes`](/docs/concepts/signals/traces/#attributes) in OpenTelemetry) let -you attach key/value pairs to an `Activity` so it carries more information about -the current operation that it's tracking. - ```csharp -using var myActivity = MyActivitySource.StartActivity("SayHello"); +var eventTags = new ActivityTagsCollection +{ + { "operation", "calculate-pi" }, + { "result", 3.14159 } +}; -activity?.SetTag("operation.value", 1); -activity?.SetTag("operation.name", "Saying hello!"); -activity?.SetTag("operation.other-stuff", new int[] { 1, 2, 3 }); +activity?.AddEvent(new("End Computation", DateTimeOffset.Now, eventTags)); ``` -We recommend that all Tag names are defined in constants rather than defined -inline as this provides both consistency and also discoverability. +### Create Spans with links -### Adding events +A [Span](/docs/concepts/signals/traces/#spans) may be linked to zero or more +other Spans that are causally related via a +[Span Link](/docs/concepts/signals/traces/#span-links). Links can be used to +represent batched operations where a Span was initiated by multiple initiating +Spans, each representing a single incoming item being processed in the batch. -An [event](/docs/concepts/signals/traces/#span-events) is a human-readable -message on an `Activity` that represents "something happening" during its -lifetime. ```csharp -using var myActivity = MyActivitySource.StartActivity("SayHello"); - -// ... - -myActivity?.AddEvent(new("Gonna try it!")); -// ... - -myActivity?.AddEvent(new("Did it!")); -``` - -Events can also be created with a timestamp and a collection of Tags. - -```csharp -using var myActivity = MyActivitySource.StartActivity("SayHello"); - -// ... - -myActivity?.AddEvent(new("Gonna try it!", DateTimeOffset.Now)); - -// ... - -var eventTags = new Dictionary +var links = new List { - { "foo", 1 }, - { "bar", "Hello, World!" }, - { "baz", new int[] { 1, 2, 3 } } + new ActivityLink(activityContext1), + new ActivityLink(activityContext2), + new ActivityLink(activityContext3) }; -myActivity?.AddEvent(new("Gonna try it!", DateTimeOffset.Now, new(eventTags))); +var activity = MyActivitySource.StartActivity( + ActivityKind.Internal, + name: "activity-with-links", + links: links); ``` -### Adding links +For more details how to read context from remote processes, see +[Context Propagation](#context-propagation). -An `Activity` can be created with zero or more -[`ActivityLink`s](/docs/concepts/signals/traces/#span-links) that are causally -related. -```csharp -// Get a context from somewhere, perhaps it's passed in as a parameter -var activityContext = Activity.Current!.Context; +### Set Activity status -var links = new List -{ - new ActivityLink(activityContext) -}; +{{% docs/languages/span-status-preamble %}} -using var anotherActivity = - MyActivitySource.StartActivity( - ActivityKind.Internal, - name: "anotherActivity", - links: links); +A [status](/docs/concepts/signals/traces/#span-status) can be set on a +[span](/docs/concepts/signals/traces/#spans), typically used to specify that a +span has not completed successfully - `SpanStatus.Error`. -// do some work -``` +By default, all spans are `Unset`, which means a span completed without error. The `Ok` status is reserved for when you need to explicitly mark a span as successful rather than stick with the default of Unset (i.e., “without error”). -### Set Activity status +The status can be set at any time before the span is finished. -{{% docs/languages/span-status-preamble %}} +It can be a good idea to record exceptions when they happen. It's recommended to +do this in conjunction with +[setting span status](/docs/specs/otel/trace/api/#set-status). ```csharp -using var myActivity = MyActivitySource.StartActivity("SayHello"); - -try -{ - // do something -} -catch (Exception ex) +private int rollOnce() { - myActivity.SetStatus(ActivityStatusCode.Error, "Something bad happened!"); + int result; + var childActivity = MyActivitySource.StartActivity("rollOnce"); + + try + { + result = Random.Shared.Next(min, max + 1); + } + catch (Exception ex) + { + childActivity?.SetStatus(ActivityStatusCode.Error, ex.Message); + childActivity?.RecordException(ex); + throw; + } + finally + { + childActivity?.Stop(); + } + + return result; } + ``` ## Metrics From 3fdd65f2c5d98f6a0cdde3544b13184696b79e89 Mon Sep 17 00:00:00 2001 From: IAMebonyhope Date: Thu, 8 Feb 2024 16:12:55 -0800 Subject: [PATCH 10/22] fix some comments --- content/en/docs/languages/net/instrumentation.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 9ec7f3d33b6f..29e51a188651 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -191,8 +191,7 @@ dotnet add package OpenTelemetry.Instrumentation.AspNetCore ### Initialize the SDK -{{% alert title="Note" color="info" %}} If you’re instrumenting a library, -**skip this step**. {{% /alert %}} +{{% alert title="Note" color="info" %}} If you’re instrumenting a library, you dont need to initialize the sdk. {{% /alert %}} It is important to configure an instance of the OpenTelemetry SDK as early as possible in your application. @@ -293,8 +292,7 @@ dotnet run ### Initialize Tracing -{{% alert title="Note" color="info" %}} If you’re instrumenting a library, -**skip this step**. {{% /alert %}} +{{% alert title="Note" color="info" %}} If you’re instrumenting a library, you dont need to initialize a TraceProvider. {{% /alert %}} To enable [tracing](/docs/concepts/signals/traces/) in your app, you'll need to have an initialized @@ -545,7 +543,7 @@ var eventTags = new ActivityTagsCollection activity?.AddEvent(new("End Computation", DateTimeOffset.Now, eventTags)); ``` -### Create Spans with links +### Create Activities with links A [Span](/docs/concepts/signals/traces/#spans) may be linked to zero or more other Spans that are causally related via a From d3580d60a20ac505fd8d11b31bea10be884d2206 Mon Sep 17 00:00:00 2001 From: IAMebonyhope Date: Wed, 14 Feb 2024 00:16:02 -0800 Subject: [PATCH 11/22] incorporate feedback --- .../en/docs/languages/net/instrumentation.md | 152 +++++++++--------- 1 file changed, 72 insertions(+), 80 deletions(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 29e51a188651..928e94c9a6d9 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -15,9 +15,8 @@ you can also use [automatic instrumentation](/docs/languages/net/automatic/) to get started and then enrich your code with manual instrumentation as needed. Also, for libraries your code depends on, you don't have to write -instrumentation code yourself, since they might come with OpenTelemetry -or you can make use of -[instrumentation libraries](/docs/languages/net/libraries/). +instrumentation code yourself, since they might be already instrumented or there are +[instrumentation libraries](/docs/languages/net/libraries/) for them. {{% /alert %}} @@ -59,37 +58,36 @@ Create the library file named `Dice.cs` and add the following code to it: ```csharp /*Dice.cs*/ -namespace otel + +public class Dice { - public class Dice + private int min; + private int max; + + public Dice(int min, int max) { - private int min; - private int max; + this.min = min; + this.max = max; + } - public Dice(int min, int max) - { - this.min = min; - this.max = max; - } + public List rollTheDice(int rolls) + { + List results = new List(); - public List rollTheDice(int rolls) + for (int i = 0; i < rolls; i++) { - List results = new List(); - - for (int i = 0; i < rolls; i++) - { - results.Add(rollOnce()); - } - - return results; + results.Add(rollOnce()); } + + return results; + } - private int rollOnce() - { - return Random.Shared.Next(min, max + 1); - } + private int rollOnce() + { + return Random.Shared.Next(min, max + 1); } } + ``` Create the app file `DiceController.cs` and add the following code to it: @@ -99,44 +97,43 @@ Create the app file `DiceController.cs` and add the following code to it: using Microsoft.AspNetCore.Mvc; using System.Net; -namespace otel + +public class DiceController : ControllerBase { - public class DiceController : ControllerBase + private ILogger logger; + + public DiceController(ILogger logger) { - private ILogger logger; + this.logger = logger; + } - public DiceController(ILogger logger) + [HttpGet("/rolldice")] + public List RollDice(string player, int? rolls) + { + if(!rolls.HasValue) { - this.logger = logger; + logger.LogError("Missing rolls parameter"); + throw new HttpRequestException("Missing rolls parameter", null, HttpStatusCode.BadRequest); } + + var result = new Dice(1, 6).rollTheDice(rolls.Value); - [HttpGet("/rolldice")] - public List RollDice(string player, int? rolls) + if (string.IsNullOrEmpty(player)) { - if(!rolls.HasValue) - { - logger.LogError("Missing rolls parameter"); - throw new HttpRequestException("Missing rolls parameter", null, HttpStatusCode.BadRequest); - } - - var result = new Dice(1, 6).rollTheDice(rolls.Value); - - if (string.IsNullOrEmpty(player)) - { - logger.LogInformation("Anonymous player is rolling the dice: {result}", result); - } - else - { - logger.LogInformation("{player} is rolling the dice: {result}", player, result); - } - - return result; + logger.LogInformation("Anonymous player is rolling the dice: {result}", result); } + else + { + logger.LogInformation("{player} is rolling the dice: {result}", player, result); + } + + return result; } } + ``` -Replace the content of the program.cs file with the following code: +Replace the content of the Program.cs file with the following code: ```csharp var builder = WebApplication.CreateBuilder(args); @@ -154,7 +151,6 @@ To ensure that it is working, run the application with the following command and open in your web browser: ```sh -dotnet build dotnet run ``` @@ -191,7 +187,7 @@ dotnet add package OpenTelemetry.Instrumentation.AspNetCore ### Initialize the SDK -{{% alert title="Note" color="info" %}} If you’re instrumenting a library, you dont need to initialize the sdk. {{% /alert %}} +{{% alert title="Note" color="info" %}} If you’re instrumenting a library, you dont need to initialize the SDK. {{% /alert %}} It is important to configure an instance of the OpenTelemetry SDK as early as possible in your application. @@ -212,10 +208,11 @@ var builder = WebApplication.CreateBuilder(args); builder.Services.AddOpenTelemetry() .ConfigureResource(resource => resource.AddService( - serviceName: serviceName, + serviceName: serviceName, serviceVersion: serviceVersion)) .WithTracing(tracing => tracing .AddSource(serviceName) + .AddAspNetCoreInstrumentation() .AddConsoleExporter()) .WithMetrics(metrics => metrics .AddMeter(serviceName) @@ -340,23 +337,21 @@ using Microsoft.AspNetCore.Mvc; using System.Diagnostics; using System.Net; -namespace otel +public class DiceController : ControllerBase { - public class DiceController : ControllerBase - { - private ILogger logger; - - public static readonly ActivitySource MyActivitySource = new("Dice.Server", "1.0.0"); - - public DiceController(ILogger logger) - { - this.logger = logger; - } + private ILogger logger; + + public static readonly ActivitySource MyActivitySource = new("Dice.Server", "1.0.0"); - //... + public DiceController(ILogger logger) + { + this.logger = logger; } + + //... } + ``` And second, in the library file `Dice.cs`: @@ -365,23 +360,20 @@ And second, in the library file `Dice.cs`: using System.Diagnostics; -namespace otel +public class Dice { - public class Dice - { - public static readonly ActivitySource MyActivitySource = new("Dice.Lib", "1.0.0"); - - private int min; - private int max; - - public Dice(int min, int max) - { - this.min = min; - this.max = max; - } + public static readonly ActivitySource MyActivitySource = new("Dice.Lib", "1.0.0"); + + private int min; + private int max; - //... + public Dice(int min, int max) + { + this.min = min; + this.max = max; } + + //... } ``` From 7dc7ffcec07110404273ec790f20361b83178c12 Mon Sep 17 00:00:00 2001 From: IAMebonyhope Date: Wed, 14 Feb 2024 00:27:26 -0800 Subject: [PATCH 12/22] incorporate feedback --- content/en/docs/languages/net/instrumentation.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 928e94c9a6d9..18f979b6d92f 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -240,6 +240,7 @@ add the following code at the beginning of your program, during any important st ```csharp using OpenTelemetry.Resources; using OpenTelemetry.Trace; +using OpenTelemetry.Logs; //... @@ -260,6 +261,14 @@ var meterProvider = Sdk.CreateMeterProviderBuilder() .AddConsoleExporter() .Build(); +var loggerFactory = LoggerFactory.Create(builder => +{ + builder.AddOpenTelemetry(logging => + { + logging.AddConsoleExporter(); + }); +}); + //... ``` @@ -591,7 +600,7 @@ private int rollOnce() } catch (Exception ex) { - childActivity?.SetStatus(ActivityStatusCode.Error, ex.Message); + childActivity?.SetStatus(ActivityStatusCode.Error, "Something bad happened!"); childActivity?.RecordException(ex); throw; } From 505bcf97d88c2dc3da00a1a8a76b155ba7ba2f62 Mon Sep 17 00:00:00 2001 From: Hope Oluwalolope Date: Thu, 29 Feb 2024 23:18:11 -0800 Subject: [PATCH 13/22] Update content/en/docs/languages/net/instrumentation.md Co-authored-by: Severin Neumann --- content/en/docs/languages/net/instrumentation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 18f979b6d92f..c4b090a5121a 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -187,7 +187,7 @@ dotnet add package OpenTelemetry.Instrumentation.AspNetCore ### Initialize the SDK -{{% alert title="Note" color="info" %}} If you’re instrumenting a library, you dont need to initialize the SDK. {{% /alert %}} +{{% alert title="Note" color="info" %}} If you’re instrumenting a library, you dont need to initialize the SDK. {{% /alert %}} It is important to configure an instance of the OpenTelemetry SDK as early as possible in your application. From d84d5ca25f83b5c3ab0bd12c20094e332a4fb518 Mon Sep 17 00:00:00 2001 From: Hope Oluwalolope Date: Fri, 1 Mar 2024 02:59:19 -0800 Subject: [PATCH 14/22] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Severin Neumann Co-authored-by: Robert Pająk --- .../en/docs/languages/net/instrumentation.md | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index c4b090a5121a..6fe375714569 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -50,6 +50,10 @@ library, follow the instructions here to adapt the process to your own code. ### Create and launch an HTTP Server +To begin, set up an environment in a new directory called `dotnet-otel-example`. Within that directory, execute following command: + +```shell +dotnet new web To highlight the difference between instrumenting a library and a standalone app, split out the dice rolling into a library file, which then will be imported as a dependency by the app file. @@ -147,6 +151,25 @@ app.MapControllers(); app.Run(); ``` +In the `Properties` subdirectory, replace the content of `launchSettings.json` with the following: + +```json +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:8080", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} +``` + To ensure that it is working, run the application with the following command and open in your web browser: @@ -187,12 +210,12 @@ dotnet add package OpenTelemetry.Instrumentation.AspNetCore ### Initialize the SDK -{{% alert title="Note" color="info" %}} If you’re instrumenting a library, you dont need to initialize the SDK. {{% /alert %}} +{{% alert title="Note" color="info" %}} If you’re instrumenting a library, you don't need to initialize the SDK. {{% /alert %}} It is important to configure an instance of the OpenTelemetry SDK as early as possible in your application. To initialize the OpenTelemetry SDK for an ASP.NET Core app like in the case of the example app, -replace the content of the program.cs file with the following code: +update the content of the `Program.cs` file with the following code: ```csharp using OpenTelemetry.Logs; @@ -298,7 +321,7 @@ dotnet run ### Initialize Tracing -{{% alert title="Note" color="info" %}} If you’re instrumenting a library, you dont need to initialize a TraceProvider. {{% /alert %}} +{{% alert title="Note" color="info" %}} If you’re instrumenting a library, you don't need to initialize a TraceProvider. {{% /alert %}} To enable [tracing](/docs/concepts/signals/traces/) in your app, you'll need to have an initialized @@ -415,7 +438,7 @@ If you followed the instructions using the [example app](#example-app) up to this point, you can copy the code above in your library file `Dice.cs`. You should now be able to see activities/spans emitted from your app. -Start your app as follows, and then send it requests by visiting http://localhost:8080/rolldice?rolls=12 with your browser or curl. +Start your app as follows, and then send it requests by visiting with your browser or curl. ```sh dotnet run From 55ec3a05690eb7015a17cfc8939d8b717669be5b Mon Sep 17 00:00:00 2001 From: IAMebonyhope Date: Wed, 10 Apr 2024 08:38:33 -0700 Subject: [PATCH 15/22] incorporate feedback --- .../en/docs/languages/net/instrumentation.md | 164 +++++++++--------- 1 file changed, 81 insertions(+), 83 deletions(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 18f979b6d92f..4310c599e0d6 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -201,7 +201,7 @@ using OpenTelemetry.Resources; using OpenTelemetry.Trace; // Ideally, you will want this name to come from a config file, constants file, etc. -var serviceName = "Dice.*"; +var serviceName = "dice-server"; var serviceVersion = "1.0.0"; var builder = WebApplication.CreateBuilder(args); @@ -271,6 +271,10 @@ var loggerFactory = LoggerFactory.Create(builder => //... +tracerProvider.Dispose(); +meterProvider.Dispose(); +loggerFactory.Dispose(); + ``` For debugging and local development purposes, the example exports @@ -337,9 +341,26 @@ The values of name and version should uniquely identify the Instrumentation Scop It’s generally recommended to define `ActivitySource` once per app/service that is been instrumented, but you can instantiate several `ActivitySource`s if that suits your scenario -In the case of the example app, there are two places where the `ActivitySource` will be instantiated with an appropriate Instrumentation Scope: +In the case of the example app, the `ActivitySource` will be instantiated in the `program.cs` and then the same instance will be used in `DiceController.cs` and `Dice.cs` + +First we instantiate the activitySource in the `program.cs`: + +```csharp { hl_lines=["2-3"]} + +// Register the ActivitySource as a singleton to have a single instance of activitySource across the application +builder.Services.AddSingleton(new ActivitySource(serviceName, serviceVersion)); -First, in the application file `DiceController.cs`: +builder.Services.AddControllers(); + +var app = builder.Build(); + +app.MapControllers(); + +app.Run(); + +``` + +Then, in the application file `DiceController.cs` we make use of that activitySource instance: ```csharp using Microsoft.AspNetCore.Mvc; @@ -349,12 +370,13 @@ using System.Net; public class DiceController : ControllerBase { private ILogger logger; - - public static readonly ActivitySource MyActivitySource = new("Dice.Server", "1.0.0"); - public DiceController(ILogger logger) + private ActivitySource activitySource; + + public DiceController(ILogger logger, ActivitySource activitySource) { this.logger = logger; + this.activitySource = activitySource; } //... @@ -363,7 +385,7 @@ public class DiceController : ControllerBase ``` -And second, in the library file `Dice.cs`: +And then, we pass in the same activitySource to the library file `Dice.cs`: ```csharp @@ -371,15 +393,15 @@ using System.Diagnostics; public class Dice { - public static readonly ActivitySource MyActivitySource = new("Dice.Lib", "1.0.0"); - + public ActivitySource activitySource; private int min; private int max; - public Dice(int min, int max) + public Dice(int min, int max, ActivitySource activitySource) { this.min = min; this.max = max; + this.activitySource = activitySource; } //... @@ -395,22 +417,23 @@ The code below illustrates how to create an activity. ```csharp public List rollTheDice(int rolls) -{ - var activity = MyActivitySource.StartActivity("rollTheDice"); +{ List results = new List(); - - for (int i = 0; i < rolls; i++) + + // It is recommended to create activities, only when doing operations that are worth measuring independently. + // Too many activities makes it harder to visualize in tools like Jaeger. + using (var myActivity = activitySource.StartActivity("rollTheDice")) { - results.Add(rollOnce()); - } + for (int i = 0; i < rolls; i++) + { + results.Add(rollOnce()); + } - activity?.Stop(); - return results; + return results; + } } ``` -Note, that it’s required to `Stop()` the activity, otherwise it will not be exported. - If you followed the instructions using the [example app](#example-app) up to this point, you can copy the code above in your library file `Dice.cs`. You should now be able to see activities/spans emitted from your app. @@ -425,33 +448,19 @@ After a while, you should see the spans printed in the console by the `ConsoleExporter`, something like this: ```json -service.name: Dice.* -service.version: 1.0.0 -service.instance.id: 17a824ba-9734-413d-951e-c44414b6b93b -telemetry.sdk.name: opentelemetry -telemetry.sdk.language: dotnet -telemetry.sdk.version: 1.7.0 - -Resource associated with Metric: - service.name: Dice.* - service.version: 1.0.0 - service.instance.id: 17a824ba-9734-413d-951e-c44414b6b93b - telemetry.sdk.name: opentelemetry - telemetry.sdk.language: dotnet - telemetry.sdk.version: 1.7.0 -Activity.TraceId: b322c0ce4bf1941cd6a476a454d78434 -Activity.SpanId: 7bbfc1522b5595bb +Activity.TraceId: 841d70616c883db82b4ae4e11c728636 +Activity.SpanId: 9edfe4d69b0d6d8b Activity.TraceFlags: Recorded -Activity.ParentSpanId: c761f5e5ca3886bf -Activity.ActivitySourceName: Dice.Lib +Activity.ParentSpanId: 39fcd105cf958377 +Activity.ActivitySourceName: dice-server Activity.DisplayName: rollTheDice Activity.Kind: Internal -Activity.StartTime: 2024-02-08T08:10:47.5310248Z -Activity.Duration: 00:00:00.0002703 +Activity.StartTime: 2024-04-10T15:24:00.3620354Z +Activity.Duration: 00:00:00.0144329 Resource associated with Activity: - service.name: Dice.* + service.name: dice-server service.version: 1.0.0 - service.instance.id: 17a824ba-9734-413d-951e-c44414b6b93b + service.instance.id: 7a7a134f-3178-4ac6-9625-96df77cff8b4 telemetry.sdk.name: opentelemetry telemetry.sdk.language: dotnet telemetry.sdk.version: 1.7.0 @@ -467,19 +476,14 @@ nested operation. The following sample creates a nested span that tracks ```csharp private int rollOnce() { - int result; - var childActivity = MyActivitySource.StartActivity("rollOnce"); - - try - { - result = Random.Shared.Next(min, max + 1); - } - finally + using (var childActivity = activitySource.StartActivity("rollOnce")) { - childActivity?.Stop(); + int result; + + result = Random.Shared.Next(min, max + 1); + + return result; } - - return result; } ``` @@ -504,20 +508,15 @@ information about the current operation that it's tracking. ```csharp private int rollOnce() { - int result; - var childActivity = MyActivitySource.StartActivity("rollOnce"); - - try + using (var childActivity = activitySource.StartActivity("rollOnce")) { - result = Random.Shared.Next(min, max + 1); - childActivity?.SetTag("dicelib.rolled", result); - } - finally - { - childActivity?.Stop(); - } - - return result; + int result; + + result = Random.Shared.Next(min, max + 1); + childActivity?.SetTag("dicelib.rolled", result); + + return result; + } } ``` @@ -591,25 +590,24 @@ do this in conjunction with ```csharp private int rollOnce() { - int result; - var childActivity = MyActivitySource.StartActivity("rollOnce"); - - try - { - result = Random.Shared.Next(min, max + 1); - } - catch (Exception ex) + using (var childActivity = activitySource.StartActivity("rollOnce")) { - childActivity?.SetStatus(ActivityStatusCode.Error, "Something bad happened!"); - childActivity?.RecordException(ex); - throw; - } - finally - { - childActivity?.Stop(); - } + int result; + + try + { + result = Random.Shared.Next(min, max + 1); + childActivity?.SetTag("dicelib.rolled", result); + } + catch (Exception ex) + { + childActivity?.SetStatus(ActivityStatusCode.Error, "Something bad happened!"); + childActivity?.RecordException(ex); + throw; + } - return result; + return result; + } } ``` From dbbf6f76a0c0b55bf09f4f1ac709e85e949c9909 Mon Sep 17 00:00:00 2001 From: IAMebonyhope Date: Wed, 10 Apr 2024 08:48:49 -0700 Subject: [PATCH 16/22] fix alignment --- content/en/docs/languages/net/instrumentation.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 36f4df376972..127414189cea 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -54,6 +54,8 @@ To begin, set up an environment in a new directory called `dotnet-otel-example`. ```shell dotnet new web +``` + To highlight the difference between instrumenting a library and a standalone app, split out the dice rolling into a library file, which then will be imported as a dependency by the app file. From 5ea327fd129b14632962f40630df1c6548ad2ac9 Mon Sep 17 00:00:00 2001 From: IAMebonyhope Date: Thu, 11 Apr 2024 01:20:31 -0700 Subject: [PATCH 17/22] add Instrumentation wrapper --- .../en/docs/languages/net/instrumentation.md | 46 +++++++++++++++---- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 127414189cea..45d1bed175b3 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -366,14 +366,42 @@ The values of name and version should uniquely identify the Instrumentation Scop It’s generally recommended to define `ActivitySource` once per app/service that is been instrumented, but you can instantiate several `ActivitySource`s if that suits your scenario -In the case of the example app, the `ActivitySource` will be instantiated in the `program.cs` and then the same instance will be used in `DiceController.cs` and `Dice.cs` +In the case of the example app, we will create a new file `Instrumentation.cs` as a custom type to hold reference for the ActivitySource. -First we instantiate the activitySource in the `program.cs`: +```csharp +using System.Diagnostics; + +/// +/// It is recommended to use a custom type to hold references for ActivitySource. +/// This avoids possible type collisions with other components in the DI container. +/// +public class Instrumentation : IDisposable +{ + internal const string ActivitySourceName = "dice-server"; + internal const string ActivitySourceVersion = "1.0.0"; + + public Instrumentation() + { + this.ActivitySource = new ActivitySource(ActivitySourceName, ActivitySourceVersion); + } + + public ActivitySource ActivitySource { get; } -```csharp { hl_lines=["2-3"]} + public void Dispose() + { + this.ActivitySource.Dispose(); + } +} +``` -// Register the ActivitySource as a singleton to have a single instance of activitySource across the application -builder.Services.AddSingleton(new ActivitySource(serviceName, serviceVersion)); +Then we will update the `Program.cs` to add the Instrument object as a dependency injection: + +```csharp +//... + +// Register the ActivitySource as a singleton in the DI container. +// Create a service to expose the ActivitySource Instruments. +builder.Services.AddSingleton(); builder.Services.AddControllers(); @@ -385,7 +413,7 @@ app.Run(); ``` -Then, in the application file `DiceController.cs` we make use of that activitySource instance: +In the application file `DiceController.cs` we then reference that activitySource instance: ```csharp using Microsoft.AspNetCore.Mvc; @@ -398,10 +426,10 @@ public class DiceController : ControllerBase private ActivitySource activitySource; - public DiceController(ILogger logger, ActivitySource activitySource) + public DiceController(ILogger logger, Instrumentation instrumentation) { this.logger = logger; - this.activitySource = activitySource; + this.activitySource = instrumentation.ActivitySource; } //... @@ -410,7 +438,7 @@ public class DiceController : ControllerBase ``` -And then, we pass in the same activitySource to the library file `Dice.cs`: +The same activitySource is also passed to the library file `Dice.cs`: ```csharp From 2cb9cd0fd766b34464924064b0a9b2cd076a5eff Mon Sep 17 00:00:00 2001 From: IAMebonyhope Date: Fri, 10 May 2024 02:41:17 -0700 Subject: [PATCH 18/22] add reference to activitySource in Dice.cs --- .../en/docs/languages/net/instrumentation.md | 57 +++++++++++-------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 5f4f97d2f9fc..6e9eef07e424 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -100,6 +100,7 @@ Create the app file `DiceController.cs` and add the following code to it: ```csharp /*DiceController.cs*/ + using Microsoft.AspNetCore.Mvc; using System.Net; @@ -263,9 +264,9 @@ If initializing the OpenTelemetry SDK for a console app, add the following code at the beginning of your program, during any important startup operations. ```csharp +using OpenTelemetry.Logs; using OpenTelemetry.Resources; using OpenTelemetry.Trace; -using OpenTelemetry.Logs; //... @@ -339,7 +340,7 @@ use a no-op implementation and fail to generate data. If you followed the instructions to [initialize the SDK](#initialize-the-sdk) above, you have a `TracerProvider` setup for you already. You can continue with -[acquiring a tracer](#acquiring-a-tracer). +[setting up an ActivitySource](#setting-up-an-activitysource). ### Setting up an ActivitySource @@ -349,22 +350,7 @@ Anywhere in your application where you write manual tracing code should configur trace operations with [`Activity`](/docs/concepts/signals/traces/#spans) elements. -For example: - -```csharp - -public static class Telemetry -{ - //... - - public static readonly ActivitySource MyActivitySource = new("name", "version"); - - //... -} -``` -The values of name and version should uniquely identify the Instrumentation Scope, such as the package, module or class name. While the name is required, the version is still recommended despite being optional. - -It’s generally recommended to define `ActivitySource` once per app/service that is been instrumented, but you can instantiate several `ActivitySource`s if that suits your scenario +It’s generally recommended to define `ActivitySource` once per app/service that is been instrumented, but you can instantiate several `ActivitySource`s if that suits your scenario. In the case of the example app, we will create a new file `Instrumentation.cs` as a custom type to hold reference for the ActivitySource. @@ -413,9 +399,12 @@ app.Run(); ``` -In the application file `DiceController.cs` we then reference that activitySource instance: +In the application file `DiceController.cs` we will reference that activitySource instance +and the same activitySource instance will also be passed to the library file `Dice.cs` ```csharp +/*DiceController.cs*/ + using Microsoft.AspNetCore.Mvc; using System.Diagnostics; using System.Net; @@ -426,21 +415,42 @@ public class DiceController : ControllerBase private ActivitySource activitySource; - public DiceController(ILogger logger, Instrumentation instrumentation) + public DiceController(ILogger logger, Instrumentation instrumentation) { this.logger = logger; this.activitySource = instrumentation.ActivitySource; } - //... -} + [HttpGet("/rolldice")] + public List RollDice(string player, int? rolls) + { + List result = new List(); + + if (!rolls.HasValue) + { + logger.LogError("Missing rolls parameter"); + throw new HttpRequestException("Missing rolls parameter", null, HttpStatusCode.BadRequest); + } + + result = new Dice(1, 6, activitySource).rollTheDice(rolls.Value); + if (string.IsNullOrEmpty(player)) + { + logger.LogInformation("Anonymous player is rolling the dice: {result}", result); + } + else + { + logger.LogInformation("{player} is rolling the dice: {result}", player, result); + } + return result; + } +} ``` -The same activitySource is also passed to the library file `Dice.cs`: ```csharp +/*Dice.cs*/ using System.Diagnostics; @@ -459,7 +469,6 @@ public class Dice //... } - ``` ### Create Activities From 4ab73558aecb364b1a7807bce22b4d010a4e28b6 Mon Sep 17 00:00:00 2001 From: svrnm Date: Fri, 10 May 2024 13:06:54 +0200 Subject: [PATCH 19/22] update blog contribution policy Signed-off-by: svrnm --- archetypes/blog.md | 2 ++ content/en/docs/contributing/blog.md | 51 ++++++++++++++++++++-------- static/refcache.json | 4 +++ 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/archetypes/blog.md b/archetypes/blog.md index 3fbeb86a57d3..2f1159bc6866 100644 --- a/archetypes/blog.md +++ b/archetypes/blog.md @@ -9,6 +9,8 @@ author: >- # If you have only one author, then add the single name on this line draft: true # TODO: remove this line once your post is ready to be published # canonical_url: http://somewhere.else/ # TODO: if this blog post has been posted somewhere else already, uncomment & provide the canonical URL here. body_class: otel-with-contributions-from # TODO: remove this line if there are no secondary contributing authors +issue: the issue ID for this blog post # TODO: See https://opentelemetry.io/docs/contributing/blog/ for details +sig: SIG Name # TODO: add the name of the SIG that sponsors this blog post --- diff --git a/content/en/docs/contributing/blog.md b/content/en/docs/contributing/blog.md index 1a245ec94364..cfe63df2c54c 100644 --- a/content/en/docs/contributing/blog.md +++ b/content/en/docs/contributing/blog.md @@ -6,13 +6,25 @@ weight: 30 The [OpenTelemetry blog](/blog/) communicates new features, community reports, and any news that might be relevant to the OpenTelemetry community. This -includes end users and developers. Anyone can write a blog post and submit it -for review. +includes end users and developers. Anyone can write a blog post, read below what +the requirements are. + +## Documentation or blog post? + +Before writing a blog post, ask yourself if your content also might be a good +addition to the documentation. If the answer is "yes", create a new issue or +pull request (PR) with your content to get it added to the docs. + +Note, that the focus of maintainers and approvers of the OpenTelemetry Website +is to improve the documentation of the project, so your blog post will have a +lower priority for review. ## Before submitting a blog post Blog posts should not be commercial in nature and should consist of original -content that applies broadly to the OpenTelemetry community. +content that applies broadly to the OpenTelemetry community. Blog posts should +follow the policies outlined in the +[Social Media Guide](https://github.com/open-telemetry/community/blob/main/social-media-guide.md). Verify that your intended content broadly applies to the OpenTelemetry Community . Appropriate content includes: @@ -27,15 +39,28 @@ Unsuitable content includes: - Vendor product pitches -To submit a blog post, +If your blog post fits into the list of appropriate content, [raise an issue](https://github.com/open-telemetry/opentelemetry.io/issues/new?title=New%20Blog%20Post:%20%3Ctitle%3E) -with the title and a short description of your blog post. If you are not a -[Member](https://github.com/open-telemetry/community/blob/main/community-membership.md#member), -you also need to provide a _sponsor_ for your blog post, who is a Member (by -that definition) and who is willing to provide a first review of your blog post. - -If you do not raise an issue before providing your PR, we may request you to do -so before providing a review. +with the following details: + +- Title of the blog post +- Short description and outline of your blog post +- If applicable, list the technologies used in your blog post. Make sure that + all of them are open source, and prefer CNCF projects over non-CNCF projects + (e.g. use Jaeger for trace visualization, and Prometheus for metric + visualization) +- Name of a [SIG](https://github.com/open-telemetry/community/), which is + related to this blog post +- Name of a sponsor (maintainer or approver) from this SIG, who will help to + review that PR + +Maintainers of SIG Communication will verify, that your blog post satisfies all +the requirements for being accepted. If you can not name a SIG/sponsor in your +initial issue details, they will also point you to an appropriate SIG, you can +reach out to for sponsorship. + +If your issue has everything needed, a maintainer will verify that you can go +ahead and submit your blog post. ## Submit a blog post @@ -44,10 +69,6 @@ locally or by using the GitHub UI. In both cases we ask you to follow the instructions provided by the [blog post template](https://github.com/open-telemetry/opentelemetry.io/tree/main/archetypes/blog.md). -**Note**: Before writing a blog post, ask yourself if your content also might be -a good addition to the documentation. If the answer is "yes", create a new issue -or pull request (PR) with your content to get it added to the docs. - ### Fork and write locally After you've set up the local fork you can create a blog post using a template. diff --git a/static/refcache.json b/static/refcache.json index 09a8bcf5a1c1..3f431271b931 100644 --- a/static/refcache.json +++ b/static/refcache.json @@ -2923,6 +2923,10 @@ "StatusCode": 200, "LastSeen": "2024-01-30T05:18:13.185301-05:00" }, + "https://github.com/open-telemetry/community/": { + "StatusCode": 200, + "LastSeen": "2024-05-10T13:05:22.581509+02:00" + }, "https://github.com/open-telemetry/community/discussions/1203": { "StatusCode": 200, "LastSeen": "2024-01-18T20:05:24.761684-05:00" From 39733f8801e7167666121ece9f68ef021acbe6ee Mon Sep 17 00:00:00 2001 From: IAMebonyhope Date: Wed, 15 May 2024 02:45:54 -0700 Subject: [PATCH 20/22] include comments --- content/en/docs/languages/net/instrumentation.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 6e9eef07e424..1a15bf827de5 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -44,7 +44,7 @@ about manual instrumentation. You don't have to use the example app: if you want to instrument your own app or library, follow the instructions here to adapt the process to your own code. -### Dependencies {#example-app-dependencies} +### Prerequisites {#example-app-prerequisites} - [.NET SDK](https://dotnet.microsoft.com/download/dotnet) 6+ @@ -385,8 +385,7 @@ Then we will update the `Program.cs` to add the Instrument object as a dependenc ```csharp //... -// Register the ActivitySource as a singleton in the DI container. -// Create a service to expose the ActivitySource Instruments. +// Register the Instrumentation class as a singleton in the DI container. builder.Services.AddSingleton(); builder.Services.AddControllers(); From 4d02369e7ba2da64734e3095d5beaf2c333bcaa7 Mon Sep 17 00:00:00 2001 From: Hope Oluwalolope Date: Wed, 15 May 2024 02:47:48 -0700 Subject: [PATCH 21/22] Apply suggestions from code review Co-authored-by: Cijo Thomas --- content/en/docs/languages/net/instrumentation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index 1a15bf827de5..cde6deea79d9 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -554,7 +554,7 @@ tracked as a nested operation under `rollTheDice` activity. ### Get the current Activity -Sometimes it’s helpful to do something with the current/active span at a particular point in program execution. +Sometimes it’s helpful to do something with the current/active Activity/Span at a particular point in program execution. ```csharp var activity = Activity.Current; @@ -581,7 +581,7 @@ private int rollOnce() } ``` -### Create Activities with events +### Add Events to Activities [Spans](/docs/concepts/signals/traces/#spans) can be annotated with named events (called [Span Events](/docs/concepts/signals/traces/#span-events)) that can From da78f9cb9c8292e049a8ae5b65b77caadef4a5e9 Mon Sep 17 00:00:00 2001 From: svrnm Date: Thu, 16 May 2024 08:56:37 +0200 Subject: [PATCH 22/22] Prepare for merge Signed-off-by: svrnm --- .../en/docs/languages/net/instrumentation.md | 157 +++++++++--------- content/en/docs/languages/net/netframework.md | 4 +- 2 files changed, 81 insertions(+), 80 deletions(-) diff --git a/content/en/docs/languages/net/instrumentation.md b/content/en/docs/languages/net/instrumentation.md index cde6deea79d9..5cad2ffca78d 100644 --- a/content/en/docs/languages/net/instrumentation.md +++ b/content/en/docs/languages/net/instrumentation.md @@ -3,6 +3,7 @@ title: Instrumentation weight: 20 aliases: [manual] description: Instrumentation for OpenTelemetry .NET +cSpell:ignore: dicelib rolldice --- {{% docs/languages/instrumentation-intro %}} @@ -10,13 +11,13 @@ description: Instrumentation for OpenTelemetry .NET {{% alert title="Note" color="info" %}} On this page you will learn how you can add traces, metrics and logs to your -code manually. You are not limited to using one kind of instrumentation: -you can also use [automatic instrumentation](/docs/languages/net/automatic/) -to get started and then enrich your code with manual instrumentation as needed. +code manually. You are not limited to using one kind of instrumentation: you can +also use [automatic instrumentation](/docs/languages/net/automatic/) to get +started and then enrich your code with manual instrumentation as needed. Also, for libraries your code depends on, you don't have to write -instrumentation code yourself, since they might be already instrumented or there are -[instrumentation libraries](/docs/languages/net/libraries/) for them. +instrumentation code yourself, since they might be already instrumented or there +are [instrumentation libraries](/docs/languages/net/libraries/) for them. {{% /alert %}} @@ -38,8 +39,8 @@ you can refer to the [OpenTelemetry API Shim docs for tracing](../shim). ## Example app preparation {#example-app} This page uses a modified version of the example app from -[Getting Started](/docs/languages/net/getting-started/) to help you learn -about manual instrumentation. +[Getting Started](/docs/languages/net/getting-started/) to help you learn about +manual instrumentation. You don't have to use the example app: if you want to instrument your own app or library, follow the instructions here to adapt the process to your own code. @@ -50,15 +51,16 @@ library, follow the instructions here to adapt the process to your own code. ### Create and launch an HTTP Server -To begin, set up an environment in a new directory called `dotnet-otel-example`. Within that directory, execute following command: +To begin, set up an environment in a new directory called `dotnet-otel-example`. +Within that directory, execute following command: ```shell dotnet new web ``` To highlight the difference between instrumenting a library and a standalone -app, split out the dice rolling into a library file, which then will be -imported as a dependency by the app file. +app, split out the dice rolling into a library file, which then will be imported +as a dependency by the app file. Create the library file named `Dice.cs` and add the following code to it: @@ -84,7 +86,7 @@ public class Dice { results.Add(rollOnce()); } - + return results; } @@ -93,7 +95,6 @@ public class Dice return Random.Shared.Next(min, max + 1); } } - ``` Create the app file `DiceController.cs` and add the following code to it: @@ -122,7 +123,7 @@ public class DiceController : ControllerBase logger.LogError("Missing rolls parameter"); throw new HttpRequestException("Missing rolls parameter", null, HttpStatusCode.BadRequest); } - + var result = new Dice(1, 6).rollTheDice(rolls.Value); if (string.IsNullOrEmpty(player)) @@ -137,7 +138,6 @@ public class DiceController : ControllerBase return result; } } - ``` Replace the content of the Program.cs file with the following code: @@ -154,7 +154,8 @@ app.MapControllers(); app.Run(); ``` -In the `Properties` subdirectory, replace the content of `launchSettings.json` with the following: +In the `Properties` subdirectory, replace the content of `launchSettings.json` +with the following: ```json { @@ -196,13 +197,13 @@ Install the following OpenTelemetry NuGet packages: [OpenTelemetry.Extensions.Hosting](https://www.nuget.org/packages/OpenTelemetry.Extensions.Hosting) - ```sh dotnet add package OpenTelemetry.Exporter.Console dotnet add package OpenTelemetry.Extensions.Hosting ``` -For ASP.NET Core-based applications, also install the AspNetCore instrumentation package +For ASP.NET Core-based applications, also install the AspNetCore instrumentation +package [OpenTelemetry.Instrumentation.AspNetCore](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AspNetCore) @@ -210,15 +211,17 @@ For ASP.NET Core-based applications, also install the AspNetCore instrumentation dotnet add package OpenTelemetry.Instrumentation.AspNetCore ``` - ### Initialize the SDK -{{% alert title="Note" color="info" %}} If you’re instrumenting a library, you don't need to initialize the SDK. {{% /alert %}} +{{% alert title="Note" color="info" %}} If you’re instrumenting a library, you +don't need to initialize the SDK. {{% /alert %}} -It is important to configure an instance of the OpenTelemetry SDK as early as possible in your application. +It is important to configure an instance of the OpenTelemetry SDK as early as +possible in your application. -To initialize the OpenTelemetry SDK for an ASP.NET Core app like in the case of the example app, -update the content of the `Program.cs` file with the following code: +To initialize the OpenTelemetry SDK for an ASP.NET Core app like in the case of +the example app, update the content of the `Program.cs` file with the following +code: ```csharp using OpenTelemetry.Logs; @@ -257,11 +260,10 @@ var app = builder.Build(); app.MapControllers(); app.Run(); - ``` -If initializing the OpenTelemetry SDK for a console app, -add the following code at the beginning of your program, during any important startup operations. +If initializing the OpenTelemetry SDK for a console app, add the following code +at the beginning of your program, during any important startup operations. ```csharp using OpenTelemetry.Logs; @@ -300,21 +302,20 @@ var loggerFactory = LoggerFactory.Create(builder => tracerProvider.Dispose(); meterProvider.Dispose(); loggerFactory.Dispose(); - ``` -For debugging and local development purposes, the example exports -telemetry to the console. After you have finished setting up manual -instrumentation, you need to configure an appropriate exporter to +For debugging and local development purposes, the example exports telemetry to +the console. After you have finished setting up manual instrumentation, you need +to configure an appropriate exporter to [export the app's telemetry data](/docs/languages/net/exporters/) to one or more telemetry backends. The example also sets up the mandatory SDK default attribute `service.name`, which holds the logical name of the service, and the optional, but highly encouraged, attribute `service.version`, which holds the version of the service -API or implementation. -Alternative methods exist for setting up resource attributes. For more -information, see [Resources](/docs/languages/net/resources/). +API or implementation. Alternative methods exist for setting up resource +attributes. For more information, see +[Resources](/docs/languages/net/resources/). To verify your code, build and run the app: @@ -323,12 +324,12 @@ dotnet build dotnet run ``` - ## Traces ### Initialize Tracing -{{% alert title="Note" color="info" %}} If you’re instrumenting a library, you don't need to initialize a TraceProvider. {{% /alert %}} +{{% alert title="Note" color="info" %}} If you’re instrumenting a library, you +don't need to initialize a TraceProvider. {{% /alert %}} To enable [tracing](/docs/concepts/signals/traces/) in your app, you'll need to have an initialized @@ -342,23 +343,25 @@ If you followed the instructions to [initialize the SDK](#initialize-the-sdk) above, you have a `TracerProvider` setup for you already. You can continue with [setting up an ActivitySource](#setting-up-an-activitysource). - ### Setting up an ActivitySource -Anywhere in your application where you write manual tracing code should configure an -[`ActivitySource`](/docs/concepts/signals/traces/#tracer), which will be how you -trace operations with [`Activity`](/docs/concepts/signals/traces/#spans) -elements. +Anywhere in your application where you write manual tracing code should +configure an [`ActivitySource`](/docs/concepts/signals/traces/#tracer), which +will be how you trace operations with +[`Activity`](/docs/concepts/signals/traces/#spans) elements. -It’s generally recommended to define `ActivitySource` once per app/service that is been instrumented, but you can instantiate several `ActivitySource`s if that suits your scenario. +It’s generally recommended to define `ActivitySource` once per app/service that +is been instrumented, but you can instantiate several `ActivitySource`s if that +suits your scenario. -In the case of the example app, we will create a new file `Instrumentation.cs` as a custom type to hold reference for the ActivitySource. +In the case of the example app, we will create a new file `Instrumentation.cs` +as a custom type to hold reference for the ActivitySource. ```csharp using System.Diagnostics; /// -/// It is recommended to use a custom type to hold references for ActivitySource. +/// It is recommended to use a custom type to hold references for ActivitySource. /// This avoids possible type collisions with other components in the DI container. /// public class Instrumentation : IDisposable @@ -380,9 +383,10 @@ public class Instrumentation : IDisposable } ``` -Then we will update the `Program.cs` to add the Instrument object as a dependency injection: +Then we will update the `Program.cs` to add the Instrument object as a +dependency injection: -```csharp +```csharp //... // Register the Instrumentation class as a singleton in the DI container. @@ -395,11 +399,11 @@ var app = builder.Build(); app.MapControllers(); app.Run(); - ``` -In the application file `DiceController.cs` we will reference that activitySource instance -and the same activitySource instance will also be passed to the library file `Dice.cs` +In the application file `DiceController.cs` we will reference that +activitySource instance and the same activitySource instance will also be passed +to the library file `Dice.cs` ```csharp /*DiceController.cs*/ @@ -447,7 +451,6 @@ public class DiceController : ControllerBase } ``` - ```csharp /*Dice.cs*/ @@ -472,16 +475,17 @@ public class Dice ### Create Activities -Now that you have [activitySources](/docs/concepts/signals/traces/#tracer) initialized, you can create [activities](/docs/concepts/signals/traces/#spans). +Now that you have [activitySources](/docs/concepts/signals/traces/#tracer) +initialized, you can create [activities](/docs/concepts/signals/traces/#spans). The code below illustrates how to create an activity. ```csharp public List rollTheDice(int rolls) -{ +{ List results = new List(); - - // It is recommended to create activities, only when doing operations that are worth measuring independently. + + // It is recommended to create activities, only when doing operations that are worth measuring independently. // Too many activities makes it harder to visualize in tools like Jaeger. using (var myActivity = activitySource.StartActivity("rollTheDice")) { @@ -493,13 +497,14 @@ public List rollTheDice(int rolls) return results; } } - ``` + If you followed the instructions using the [example app](#example-app) up to this point, you can copy the code above in your library file `Dice.cs`. You should now be able to see activities/spans emitted from your app. -Start your app as follows, and then send it requests by visiting with your browser or curl. +Start your app as follows, and then send it requests by visiting + with your browser or curl. ```sh dotnet run @@ -525,10 +530,10 @@ Resource associated with Activity: telemetry.sdk.name: opentelemetry telemetry.sdk.language: dotnet telemetry.sdk.version: 1.7.0 - ``` ### Create nested Activities + Nested [spans](/docs/concepts/signals/traces/#spans) let you track work that's nested in nature. For example, the `rollOnce()` function below represents a nested operation. The following sample creates a nested span that tracks @@ -540,21 +545,21 @@ private int rollOnce() using (var childActivity = activitySource.StartActivity("rollOnce")) { int result; - + result = Random.Shared.Next(min, max + 1); - + return result; } } - ``` -When you view the spans in a trace visualization tool, `rollOnce` childActivity will be -tracked as a nested operation under `rollTheDice` activity. +When you view the spans in a trace visualization tool, `rollOnce` childActivity +will be tracked as a nested operation under `rollTheDice` activity. ### Get the current Activity -Sometimes it’s helpful to do something with the current/active Activity/Span at a particular point in program execution. +Sometimes it’s helpful to do something with the current/active Activity/Span at +a particular point in program execution. ```csharp var activity = Activity.Current; @@ -562,8 +567,9 @@ var activity = Activity.Current; ### Activity Tags -Tags (the equivalent of [Attributes](/docs/concepts/signals/traces/#attributes)) let you attach key/value -pairs to an [`Activity`](/docs/concepts/signals/traces/#spans) so it carries more +Tags (the equivalent of [Attributes](/docs/concepts/signals/traces/#attributes)) +let you attach key/value pairs to an +[`Activity`](/docs/concepts/signals/traces/#spans) so it carries more information about the current operation that it's tracking. ```csharp @@ -572,12 +578,12 @@ private int rollOnce() using (var childActivity = activitySource.StartActivity("rollOnce")) { int result; - + result = Random.Shared.Next(min, max + 1); childActivity?.SetTag("dicelib.rolled", result); - + return result; - } + } } ``` @@ -585,8 +591,8 @@ private int rollOnce() [Spans](/docs/concepts/signals/traces/#spans) can be annotated with named events (called [Span Events](/docs/concepts/signals/traces/#span-events)) that can -carry zero or more [Span Attributes](#span-attributes), each of which itself is -a key:value map paired automatically with a timestamp. +carry zero or more [Span Attributes](#activity-tags), each of which itself is a +key:value map paired automatically with a timestamp. ```csharp myActivity?.AddEvent(new("Init")); @@ -612,9 +618,7 @@ other Spans that are causally related via a represent batched operations where a Span was initiated by multiple initiating Spans, each representing a single incoming item being processed in the batch. - ```csharp - var links = new List { new ActivityLink(activityContext1), @@ -628,19 +632,17 @@ var activity = MyActivitySource.StartActivity( links: links); ``` -For more details how to read context from remote processes, see -[Context Propagation](#context-propagation). - - ### Set Activity status {{% docs/languages/span-status-preamble %}} A [status](/docs/concepts/signals/traces/#span-status) can be set on a [span](/docs/concepts/signals/traces/#spans), typically used to specify that a -span has not completed successfully - `SpanStatus.Error`. +span has not completed successfully - `SpanStatus.Error`. -By default, all spans are `Unset`, which means a span completed without error. The `Ok` status is reserved for when you need to explicitly mark a span as successful rather than stick with the default of Unset (i.e., “without error”). +By default, all spans are `Unset`, which means a span completed without error. +The `Ok` status is reserved for when you need to explicitly mark a span as +successful rather than stick with the default of Unset (i.e., “without error”). The status can be set at any time before the span is finished. @@ -654,7 +656,7 @@ private int rollOnce() using (var childActivity = activitySource.StartActivity("rollOnce")) { int result; - + try { result = Random.Shared.Next(min, max + 1); @@ -670,7 +672,6 @@ private int rollOnce() return result; } } - ``` ## Metrics diff --git a/content/en/docs/languages/net/netframework.md b/content/en/docs/languages/net/netframework.md index 1b8a590cc695..29c90a7b3d3f 100644 --- a/content/en/docs/languages/net/netframework.md +++ b/content/en/docs/languages/net/netframework.md @@ -131,8 +131,8 @@ this.tracerProvider = Sdk.CreateTracerProviderBuilder() .Build(); ``` -See [Add tags to an Activity](../instrumentation/#add-tags-to-an-activity) for -annotating trace data more generally. +See [Add tags to an Activity](../instrumentation/#activity-tags) for annotating +trace data more generally. ### RecordException