[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
|
```csharp
|
||||||
public class MyCustomExporter : BaseExporter<Activity>
|
public class MyCustomExporter : BaseExporter<Activity>
|
||||||
{
|
{
|
||||||
public MyCustomExporter(
|
public MyCustomExporter(MyCustomService myCustomService)
|
||||||
ILogger<MyCustomExporter> logger,
|
|
||||||
MyCustomService myCustomService)
|
|
||||||
{
|
{
|
||||||
// Implementation not important
|
// Implementation not important
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We want to inject the `ILogger<MyCustomExporter>` and `MyCustomService`
|
We want to inject `MyCustomService` dependency into our `MyCustomExporter`
|
||||||
dependencies into our `MyCustomExporter` instance.
|
instance.
|
||||||
|
|
||||||
#### Using Sdk.CreateTracerProviderBuilder()
|
#### Using Sdk.CreateTracerProviderBuilder()
|
||||||
|
|
||||||
|
|
@ -399,7 +397,6 @@ To register `MyCustomExporter` and `MyCustomService` we can use the
|
||||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||||
.ConfigureServices(services =>
|
.ConfigureServices(services =>
|
||||||
{
|
{
|
||||||
services.AddLogging();
|
|
||||||
services.AddSingleton<MyCustomService>();
|
services.AddSingleton<MyCustomService>();
|
||||||
})
|
})
|
||||||
.AddExporter<MyCustomExporter>(ExportProcessorType.Batch)
|
.AddExporter<MyCustomExporter>(ExportProcessorType.Batch)
|
||||||
|
|
@ -486,10 +483,6 @@ shutdown.
|
||||||
`IServiceCollection` during `ConfigureBuilder` because the `IServiceProvider`
|
`IServiceCollection` during `ConfigureBuilder` because the `IServiceProvider`
|
||||||
has already been created.
|
has already been created.
|
||||||
|
|
||||||
### Guidance for library authors
|
|
||||||
|
|
||||||
// TODO: Add details here
|
|
||||||
|
|
||||||
## Configuration files and environment variables
|
## Configuration files and environment variables
|
||||||
|
|
||||||
// TODO: Add details here
|
// TODO: Add details here
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
# Extending the OpenTelemetry .NET SDK
|
# Extending the OpenTelemetry .NET SDK
|
||||||
|
|
||||||
|
Quick links:
|
||||||
|
|
||||||
* [Building your own exporter](#exporter)
|
* [Building your own exporter](#exporter)
|
||||||
* [Building your own instrumentation library](#instrumentation-library)
|
* [Building your own instrumentation library](#instrumentation-library)
|
||||||
* [Building your own processor](#processor)
|
* [Building your own processor](#processor)
|
||||||
* [Building your own sampler](#sampler)
|
* [Building your own sampler](#sampler)
|
||||||
* [Building your own resource detector](#resource-detector)
|
* [Building your own resource detector](#resource-detector)
|
||||||
|
* [Registration extension method guidance for library authors](#registration-extension-method-guidance-for-library-authors)
|
||||||
* [References](#references)
|
* [References](#references)
|
||||||
|
|
||||||
## Exporter
|
## 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
|
Apart from the exporter itself, you should also provide extension methods as
|
||||||
shown [here](./MyExporterExtensions.cs). This allows users to add the Exporter
|
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
|
### Exporting Activity Status
|
||||||
|
|
||||||
|
|
@ -334,6 +339,278 @@ Custom resource detectors can be implemented:
|
||||||
|
|
||||||
A demo ResourceDetector is shown [here](./MyResourceDetector.cs).
|
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
|
## References
|
||||||
|
|
||||||
* [Exporter
|
* [Exporter
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue