[Repo] TracerProviderBuilder dependency injection docs updates (#3869)
* Added content for the "Guidance for library authors" section. * Code review. * Spellcheck! * Code review. * Code review. * Code review. * Bug fix. * Lint. * Tweaks. Co-authored-by: Cijo Thomas <cijo.thomas@gmail.com> Co-authored-by: Utkarsh Umesan Pillai <utpilla@microsoft.com>
This commit is contained in:
parent
82941d6c79
commit
165b6f4ca4
|
|
@ -378,17 +378,15 @@ For the below examples imagine an exporter with this constructor:
|
|||
```csharp
|
||||
public class MyCustomExporter : BaseExporter<Activity>
|
||||
{
|
||||
public MyCustomExporter(
|
||||
ILogger<MyCustomExporter> logger,
|
||||
MyCustomService myCustomService)
|
||||
public MyCustomExporter(MyCustomService myCustomService)
|
||||
{
|
||||
// Implementation not important
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We want to inject the `ILogger<MyCustomExporter>` and `MyCustomService`
|
||||
dependencies into our `MyCustomExporter` instance.
|
||||
We want to inject `MyCustomService` dependency into our `MyCustomExporter`
|
||||
instance.
|
||||
|
||||
#### Using Sdk.CreateTracerProviderBuilder()
|
||||
|
||||
|
|
@ -399,7 +397,6 @@ To register `MyCustomExporter` and `MyCustomService` we can use the
|
|||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddLogging();
|
||||
services.AddSingleton<MyCustomService>();
|
||||
})
|
||||
.AddExporter<MyCustomExporter>(ExportProcessorType.Batch)
|
||||
|
|
@ -486,10 +483,6 @@ shutdown.
|
|||
`IServiceCollection` during `ConfigureBuilder` because the `IServiceProvider`
|
||||
has already been created.
|
||||
|
||||
### Guidance for library authors
|
||||
|
||||
// TODO: Add details here
|
||||
|
||||
## Configuration files and environment variables
|
||||
|
||||
// TODO: Add details here
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
# Extending the OpenTelemetry .NET SDK
|
||||
|
||||
Quick links:
|
||||
|
||||
* [Building your own exporter](#exporter)
|
||||
* [Building your own instrumentation library](#instrumentation-library)
|
||||
* [Building your own processor](#processor)
|
||||
* [Building your own sampler](#sampler)
|
||||
* [Building your own resource detector](#resource-detector)
|
||||
* [Registration extension method guidance for library authors](#registration-extension-method-guidance-for-library-authors)
|
||||
* [References](#references)
|
||||
|
||||
## Exporter
|
||||
|
|
@ -62,7 +65,9 @@ A demo exporter which simply writes activity name to the console is shown
|
|||
|
||||
Apart from the exporter itself, you should also provide extension methods as
|
||||
shown [here](./MyExporterExtensions.cs). This allows users to add the Exporter
|
||||
to the `TracerProvider` as shown in the example [here](./Program.cs).
|
||||
to the `TracerProvider` as shown in the example [here](./Program.cs). See
|
||||
[here](#registration-extension-method-guidance-for-library-authors) for more
|
||||
detailed extension method guidance.
|
||||
|
||||
### Exporting Activity Status
|
||||
|
||||
|
|
@ -334,6 +339,278 @@ Custom resource detectors can be implemented:
|
|||
|
||||
A demo ResourceDetector is shown [here](./MyResourceDetector.cs).
|
||||
|
||||
## Registration extension method guidance for library authors
|
||||
|
||||
**Note** This information applies to the OpenTelemetry SDK version 1.4.0 and
|
||||
newer only.
|
||||
|
||||
Library authors are encouraged to provide extension methods users may call to
|
||||
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.
|
||||
|
||||
When providing registration extensions:
|
||||
|
||||
* **DO** support the [.NET Options
|
||||
pattern](https://learn.microsoft.com/dotnet/core/extensions/options) and
|
||||
**DO** support [named
|
||||
options](https://learn.microsoft.com/dotnet/core/extensions/options#named-options-support-using-iconfigurenamedoptions).
|
||||
The Options pattern allows users to bind
|
||||
[configuration](https://learn.microsoft.com/dotnet/core/extensions/configuration)
|
||||
to options classes and provides extension points for working with instances as
|
||||
they are created. Multiple providers may exist in the same application for a
|
||||
single configuration and multiple components (for example exporters) may exist
|
||||
in the same provider. Named options help users target configuration to
|
||||
specific components.
|
||||
|
||||
* Use the
|
||||
[Configure](https://learn.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.optionsservicecollectionextensions.configure#microsoft-extensions-dependencyinjection-optionsservicecollectionextensions-configure-1(microsoft-extensions-dependencyinjection-iservicecollection-system-string-system-action((-0))))
|
||||
extension to register configuration callbacks for a given name.
|
||||
|
||||
* Use the
|
||||
[IOptionsMonitor<T>.Get](https://learn.microsoft.com/dotnet/api/microsoft.extensions.options.ioptionsmonitor-1.get)
|
||||
method to access options class instances by name.
|
||||
|
||||
* **DO** throw exceptions for issues that prevent the component being registered
|
||||
from starting. The OpenTelemetry SDK is allowed to crash if it cannot be
|
||||
started. It **MUST NOT** crash once running.
|
||||
|
||||
### TracerProviderBuilder extension methods
|
||||
|
||||
When registering pipeline components (for example samplers, exporters, or
|
||||
resource detectors) it is recommended to use the `TracerProviderBuilder` as the
|
||||
target type for registration extension methods. These extensions will be highly
|
||||
discoverable for users interacting with the `TracerProviderBuilder` in their IDE
|
||||
of choice.
|
||||
|
||||
The following example shows how to register a custom exporter with named options
|
||||
support using a `TracerProviderBuilder` extension.
|
||||
|
||||
```csharp
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MyLibrary;
|
||||
using OpenTelemetry;
|
||||
using OpenTelemetry.Metrics;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
namespace OpenTelemetry.Trace
|
||||
{
|
||||
public static class MyLibraryTracerProviderBuilderRegistrationExtensions
|
||||
{
|
||||
public static TracerProviderBuilder AddMyLibraryExporter(
|
||||
this TracerProviderBuilder builder,
|
||||
string? name = null,
|
||||
Action<MyExporterOptions>? configureExporterOptions = null,
|
||||
Action<BatchExportActivityProcessorOptions>? configureBatchProcessorOptions = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(builder);
|
||||
|
||||
// Support named options.
|
||||
name ??= Options.DefaultName;
|
||||
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
if (configureExporterOptions != null)
|
||||
{
|
||||
// Support configuration through Options API.
|
||||
services.Configure(name, configureExporterOptions);
|
||||
}
|
||||
|
||||
if (configureBatchProcessorOptions != null)
|
||||
{
|
||||
// Support configuration through Options API.
|
||||
services.Configure<ExportActivityProcessorOptions>(
|
||||
name,
|
||||
o => configureBatchProcessorOptions(o.BatchExportProcessorOptions));
|
||||
}
|
||||
|
||||
// Register custom service as a singleton.
|
||||
services.TryAddSingleton<MyCustomService>();
|
||||
});
|
||||
|
||||
builder.ConfigureBuilder((sp, builder) =>
|
||||
{
|
||||
// Retrieve MyExporterOptions instance using name.
|
||||
var options = sp.GetRequiredService<IOptionsMonitor<MyExporterOptions>>().Get(name);
|
||||
|
||||
// Retrieve MyCustomService singleton.
|
||||
var myCustomService = sp.GetRequiredService<MyCustomService>();
|
||||
|
||||
// Registers MyCustomExporter with a batch processor.
|
||||
builder.AddExporter(
|
||||
ExportProcessorType.Batch,
|
||||
new MyCustomExporter(options, myCustomService),
|
||||
name,
|
||||
configure: null);
|
||||
});
|
||||
|
||||
// Return builder for call chaining.
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace MyLibrary
|
||||
{
|
||||
// Options class can be bound to IConfiguration or configured by code.
|
||||
public class MyExporterOptions
|
||||
{
|
||||
public Uri? IngestionUri { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class MyCustomExporter : BaseExporter<Activity>
|
||||
{
|
||||
public MyCustomExporter(
|
||||
MyExporterOptions options,
|
||||
MyCustomService myCustomService)
|
||||
{
|
||||
// Implementation not shown.
|
||||
}
|
||||
|
||||
public override ExportResult Export(in Batch<Activity> batch)
|
||||
{
|
||||
// Implementation not shown.
|
||||
|
||||
return ExportResult.Success;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class MyCustomService
|
||||
{
|
||||
// Implementation not shown.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When providing `TracerProviderBuilder` registration extensions:
|
||||
|
||||
* **DO** Use the `OpenTelemetry.Trace` namespace for `TracerProviderBuilder`
|
||||
registration extensions to help with discoverability.
|
||||
|
||||
* **DO** Return the `TracerProviderBuilder` passed in to support call chaining
|
||||
of registration methods.
|
||||
|
||||
* **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.
|
||||
|
||||
### IServiceCollection extension methods
|
||||
|
||||
When registering instrumentation or listening to telemetry in a library
|
||||
providing other features it is recommended to use the `IServiceCollection` as
|
||||
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
|
||||
`ConfigureOpenTelemetryTracing`.
|
||||
|
||||
```csharp
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MyLibrary;
|
||||
using OpenTelemetry.Metrics;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
public static class MyLibraryServiceCollectionRegistrationExtensions
|
||||
{
|
||||
public static IServiceCollection AddMyLibrary(
|
||||
this IServiceCollection services,
|
||||
string? name = null,
|
||||
Action<MyLibraryOptions>? configure = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
|
||||
// Register library services.
|
||||
services.TryAddSingleton<IMyLibraryService, MyLibraryService>();
|
||||
|
||||
// Support named options.
|
||||
name ??= Options.Options.DefaultName;
|
||||
|
||||
if (configure != null)
|
||||
{
|
||||
// Support configuration through Options API.
|
||||
services.Configure(name, configure);
|
||||
}
|
||||
|
||||
// Configure OpenTelemetry tracing.
|
||||
services.ConfigureOpenTelemetryTracing(builder => builder.ConfigureBuilder((sp, builder) =>
|
||||
{
|
||||
var options = sp.GetRequiredService<IOptionsMonitor<MyLibraryOptions>>().Get(name);
|
||||
if (options.EnableTracing)
|
||||
{
|
||||
builder.AddSource("MyLibrary");
|
||||
}
|
||||
}));
|
||||
|
||||
// Configure OpenTelemetry metrics.
|
||||
services.ConfigureOpenTelemetryMetrics(builder => builder.ConfigureBuilder((sp, builder) =>
|
||||
{
|
||||
var options = sp.GetRequiredService<IOptionsMonitor<MyLibraryOptions>>().Get(name);
|
||||
if (options.EnableMetrics)
|
||||
{
|
||||
builder.AddMeter("MyLibrary");
|
||||
}
|
||||
}));
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace MyLibrary
|
||||
{
|
||||
// Options class can be bound to IConfiguration or configured by code.
|
||||
public class MyLibraryOptions
|
||||
{
|
||||
public bool EnableTracing { get; set; }
|
||||
|
||||
public bool EnableMetrics { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class MyLibraryService : IMyLibraryService
|
||||
{
|
||||
// Implementation not shown.
|
||||
}
|
||||
|
||||
public interface IMyLibraryService
|
||||
{
|
||||
// Implementation not shown.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The benefit to using the `IServiceCollection` style is users only need to call a
|
||||
single `AddMyLibrary` extension to configure the library itself and optionally
|
||||
turn on OpenTelemetry integration for multiple signals (tracing & metrics in
|
||||
this case).
|
||||
|
||||
**Note** `ConfigureOpenTelemetryTracing` does not automatically start
|
||||
OpenTelemetry. The host is responsible for either calling
|
||||
`AddOpenTelemetryTracing` 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
|
||||
`ConfigureOpenTelemetryTracing` was performed.
|
||||
|
||||
When providing `IServiceCollection` registration extensions:
|
||||
|
||||
* **DO** Use the `Microsoft.Extensions.DependencyInjection` namespace for
|
||||
`IServiceCollection` registration extensions to help with discoverability.
|
||||
|
||||
* **DO** Return the `IServiceCollection` passed in to support call chaining of
|
||||
registration methods.
|
||||
|
||||
* **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.
|
||||
|
||||
## References
|
||||
|
||||
* [Exporter
|
||||
|
|
|
|||
Loading…
Reference in New Issue