From 000dac50f1be67d68742495d6e444449c0e9d839 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 31 Jan 2023 12:29:15 -0800 Subject: [PATCH 1/2] Doc updates for DI tweaks. --- docs/trace/customizing-the-sdk/README.md | 43 ++++++---------- docs/trace/extending-the-sdk/README.md | 62 +++++++++++++++--------- 2 files changed, 54 insertions(+), 51 deletions(-) diff --git a/docs/trace/customizing-the-sdk/README.md b/docs/trace/customizing-the-sdk/README.md index 30bd03473e8..a7428a4644d 100644 --- a/docs/trace/customizing-the-sdk/README.md +++ b/docs/trace/customizing-the-sdk/README.md @@ -446,45 +446,34 @@ it is shutdown. `TracerProvider`. Only a single `TraceProvider` may exist in an `IServiceCollection` \ `IServiceProvider`. -### Dependency injection `TracerProviderBuilder` extension method reference +### Dependency injection TracerProviderBuilder extension method reference * `AddInstrumentation`: Adds instrumentation of type `T` into the `TracerProvider`. +* `AddInstrumentation(Func instrumentationFactory)`: + Adds instrumentation of type `T` into the + `TracerProvider` using a factory function to create the instrumentation + instance. + * `AddProcessor`: Adds a processor of type `T` (must derive from `BaseProcessor`) into the `TracerProvider`. -* `SetSampler`: Register type `T` (must derive from `Sampler`) as the sampler - for the `TracerProvider`. +* `AddProcessor(Func> + implementationFactory)`: Adds a processor into the `TracerProvider` using a + factory function to create the processor instance. * `ConfigureServices`: Registers a callback function for configuring the `IServiceCollection` used by the `TracerProviderBuilder`. **Note:** `ConfigureServices` may only be called before the `IServiceProvider` has been - created after which point service can no longer be added. - -* `ConfigureBuilder`: Registers a callback function for configuring the - `TracerProviderBuilder` once the `IServiceProvider` is available. - - ```csharp - var appBuilder = WebApplication.CreateBuilder(args); - - appBuilder.Services.AddOpenTelemetry() - .WithTracing(builder => builder - .ConfigureBuilder((sp, builder) => - { - builder.AddProcessor( - new MyCustomProcessor( - // Note: This example uses the final IServiceProvider once it is available. - sp.GetRequiredService(), - sp.GetRequiredService>().Value)); - })) - .StartWithHost(); - ``` + created after which point services can no longer be added. + +* `SetSampler`: Register type `T` (must derive from `Sampler`) as the sampler + for the `TracerProvider`. - **Note:** `ConfigureBuilder` is an advanced API and is expected to be used - primarily by library authors. Services may NOT be added to the - `IServiceCollection` during `ConfigureBuilder` because the `IServiceProvider` - has already been created. +* `SetSampler(Func + implementationFactory)`: Adds a sampler into the `TracerProvider` using a + factory function to create the sampler instance. ## Configuration files and environment variables diff --git a/docs/trace/extending-the-sdk/README.md b/docs/trace/extending-the-sdk/README.md index be9c9fabef2..8fd36bbbfa6 100644 --- a/docs/trace/extending-the-sdk/README.md +++ b/docs/trace/extending-the-sdk/README.md @@ -362,6 +362,14 @@ register custom OpenTelemetry components into their `TracerProvider`s. These extension methods can target either the `TracerProviderBuilder` or the `IServiceCollection` classes. Both of these patterns are described below. +**Note:** Libraries providing SDK plugins such as exporters, resource detectors, +and/or samplers should take a dependency on the SDK (`OpenTelemetry.dll`) +package. Library authors providing instrumentation should take a dependency on +`OpenTelemetry.Api` or `OpenTelemetry.Api.ProviderBuilderExtensions` package. +`OpenTelemetry.Api.ProviderBuilderExtensions` exposes interfaces for accessing +the `IServiceCollection` which is a requirement for supporting the [.NET Options +pattern](https://learn.microsoft.com/dotnet/core/extensions/options). + When providing registration extensions: * **DO** support the [.NET Options @@ -449,7 +457,7 @@ namespace OpenTelemetry.Trace services.TryAddSingleton(); }); - builder.ConfigureBuilder((sp, builder) => + builder.AddProcessor(serviceProvider => { // Retrieve MyExporterOptions instance using name. var exporterOptions = serviceProvider.GetRequiredService>().Get(name); @@ -458,16 +466,15 @@ namespace OpenTelemetry.Trace var batchOptions = serviceProvider.GetRequiredService>().Get(name); // Retrieve MyCustomService singleton. - var myCustomService = sp.GetRequiredService(); - - // Registers MyCustomExporter with a batch processor. - builder.AddProcessor( - new BatchActivityExportProcessor( - new MyCustomExporter(exporterOptions, myCustomService), - batchOptions.MaxQueueSize, - batchOptions.ScheduledDelayMilliseconds, - batchOptions.ExporterTimeoutMilliseconds, - batchOptions.MaxExportBatchSize)); + var myCustomService = serviceProvider.GetRequiredService(); + + // Return a batch export processor using MyCustomExporter. + return new BatchActivityExportProcessor( + new MyCustomExporter(exporterOptions, myCustomService), + batchOptions.MaxQueueSize, + batchOptions.ScheduledDelayMilliseconds, + batchOptions.ExporterTimeoutMilliseconds, + batchOptions.MaxExportBatchSize); }); // Return builder for call chaining. @@ -519,8 +526,10 @@ When providing `TracerProviderBuilder` registration extensions: * **DO** Use the `TracerProviderBuilder.ConfigureServices` extension method to register dependent services. -* **DO** Use the `TracerProviderBuilder.ConfigureBuilder` extension method to - peform configuration once the final `IServiceProvider` is available. +* **DO** Use [the dependency injection extension + methods](../customizing-the-sdk/README.md#dependency-injection-tracerproviderbuilder-extension-method-reference) + utilizing factory patterns to perform configuration once the final + `IServiceProvider` is available. ### IServiceCollection extension methods @@ -530,7 +539,8 @@ the target type for registration extension methods. The following example shows how a library might enable tracing and metric support using an `IServiceCollection` extension by calling -`ConfigureOpenTelemetryTracerProvider`. +`ConfigureOpenTelemetryTracerProvider` and +`ConfigureOpenTelemetryMeterProvider`. ```csharp using Microsoft.Extensions.DependencyInjection.Extensions; @@ -554,7 +564,7 @@ namespace Microsoft.Extensions.DependencyInjection services.TryAddSingleton(); // Support named options. - name ??= Options.DefaultName; + name ??= Options.Options.DefaultName; if (configure != null) { @@ -570,7 +580,7 @@ namespace Microsoft.Extensions.DependencyInjection { builder.AddSource("MyLibrary"); } - })); + }); // Configure OpenTelemetry metrics. services.ConfigureOpenTelemetryMeterProvider((sp, builder) => @@ -580,7 +590,7 @@ namespace Microsoft.Extensions.DependencyInjection { builder.AddMeter("MyLibrary"); } - })); + }); return services; } @@ -614,12 +624,14 @@ single `AddMyLibrary` extension to configure the library itself and optionally turn on OpenTelemetry integration for multiple signals (tracing & metrics in this case). -**Note:** `ConfigureOpenTelemetryTracerProvider` does not automatically start -OpenTelemetry. The host is responsible for either calling `StartWithHost` in the +**Note:** `ConfigureOpenTelemetryTracerProvider` and +`ConfigureOpenTelemetryMeterProvider` do not automatically start OpenTelemetry. +The host is responsible for either calling `StartWithHost` in the [OpenTelemetry.Extensions.Hosting](../../../src/OpenTelemetry.Extensions.Hosting/README.md) -package, calling `Build` when using the `Sdk.CreateTracerProviderBuilder` -method, or by accessing the `TracerProvider` from the `IServiceCollection` where -`ConfigureOpenTelemetryTracerProvider` was performed. +package, calling `Build` when using the `Sdk.CreateTracerProviderBuilder` and +`Sdk.CreateMeterProviderBuilder` methods, or by accessing the `TracerProvider` +and `MeterProvider` from the `IServiceCollection` where configuration was +performed. When providing `IServiceCollection` registration extensions: @@ -631,8 +643,10 @@ When providing `IServiceCollection` registration extensions: * **DO** Use the `IServiceCollection` directly to register dependent services. -* **DO** Use the `TracerProviderBuilder.ConfigureBuilder` extension method to - peform configuration once the final `IServiceProvider` is available. +* **DO** Use [the dependency injection extension + methods](../customizing-the-sdk/README.md#dependency-injection-tracerproviderbuilder-extension-method-reference) + utilizing factory patterns to perform configuration once the final + `IServiceProvider` is available. ## References From 4a3728934b6c7c405503a047f7752193d44f5037 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 31 Jan 2023 12:55:18 -0800 Subject: [PATCH 2/2] Code review. --- docs/trace/customizing-the-sdk/README.md | 13 +++++++++---- docs/trace/extending-the-sdk/README.md | 7 ++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/trace/customizing-the-sdk/README.md b/docs/trace/customizing-the-sdk/README.md index a7428a4644d..b80af2cc90c 100644 --- a/docs/trace/customizing-the-sdk/README.md +++ b/docs/trace/customizing-the-sdk/README.md @@ -193,6 +193,11 @@ var tracerProvider = Sdk.CreateTracerProviderBuilder() tracerProvider.AddProcessor(new MyProcessor3()); ``` +**Note:** The order of processor registration is important. Each processor added +is invoked in order by the SDK. For example if a simple exporting processor is +added before an enrichment processor the exported data will not contain anything +added by the enrichment because it happens after the export. + **Note:** A `TracerProvider` assumes ownership of **all** processors added to it. This means that the provider will call the `Shutdown` method on all registered processors when it is shutting down and call the `Dispose` method on @@ -202,7 +207,7 @@ registered on each provider. Otherwise shutting down one provider will cause the shared processor(s) in other providers to be shut down as well which may lead to undesired results. -Processors can be used for enriching. exporting, and/or filtering telemetry. +Processors can be used for enriching, exporting, and/or filtering telemetry. To enrich telemetry, users may write custom processors overriding the `OnStart` and/or `OnEnd` methods (as needed) to implement custom logic to change the data @@ -329,9 +334,9 @@ reducing the number of samples of traces collected and sent to the processors. If no sampler is explicitly configured, the default is to use `ParentBased(root=AlwaysOn)`. `SetSampler` method on `TracerProviderBuilder` can be used to set sampler. Only one sampler can be associated with a provider. If -multiple `SetSampler` is called, the last one wins. Also, it is not possible to -change the sampler *after* the provider is built, by calling the `Build()` -method on the `TracerProviderBuilder`. +`SetSampler` is called multiple times, the last one wins. Also, it is not +possible to change the sampler *after* the provider is built, by calling the +`Build()` method on the `TracerProviderBuilder`. The snippet below shows configuring a custom sampler to the provider. diff --git a/docs/trace/extending-the-sdk/README.md b/docs/trace/extending-the-sdk/README.md index 8fd36bbbfa6..47dcbc37ec8 100644 --- a/docs/trace/extending-the-sdk/README.md +++ b/docs/trace/extending-the-sdk/README.md @@ -363,9 +363,10 @@ extension methods can target either the `TracerProviderBuilder` or the `IServiceCollection` classes. Both of these patterns are described below. **Note:** Libraries providing SDK plugins such as exporters, resource detectors, -and/or samplers should take a dependency on the SDK (`OpenTelemetry.dll`) -package. Library authors providing instrumentation should take a dependency on -`OpenTelemetry.Api` or `OpenTelemetry.Api.ProviderBuilderExtensions` package. +and/or samplers should take a dependency on the [OpenTelemetry SDK +package](https://www.nuget.org/packages/opentelemetry). Library authors +providing instrumentation should take a dependency on `OpenTelemetry.Api` or +`OpenTelemetry.Api.ProviderBuilderExtensions` package. `OpenTelemetry.Api.ProviderBuilderExtensions` exposes interfaces for accessing the `IServiceCollection` which is a requirement for supporting the [.NET Options pattern](https://learn.microsoft.com/dotnet/core/extensions/options).