Skip to content

Commit

Permalink
[docs] TracerProviderBuilder configuration docs updates (open-telemet…
Browse files Browse the repository at this point in the history
…ry#3894)

* Configuration docs.

* Tweaks + lint.

* Spellcheck.

* Tweaks.

* Lint.

* Code review.

* Tweaks.

* Code review.

* Code review.

Co-authored-by: Cijo Thomas <[email protected]>
  • Loading branch information
CodeBlanch and cijothomas authored Nov 14, 2022
1 parent c7aefab commit 1daa2e3
Show file tree
Hide file tree
Showing 2 changed files with 261 additions and 55 deletions.
193 changes: 189 additions & 4 deletions docs/trace/customizing-the-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,6 @@ Sdk.SetDefaultTextMapPropagator(new MyCustomPropagator());

## Dependency injection support

### Overview

**Note** This information applies to the OpenTelemetry SDK version 1.4.0 and
newer only.

Expand All @@ -371,7 +369,7 @@ The SDK implementation of `TracerProviderBuilder` is backed by an
generally known as [dependency
injection](https://learn.microsoft.com/dotnet/core/extensions/dependency-injection).

### Examples
### Dependency injection examples

For the below examples imagine an exporter with this constructor:

Expand Down Expand Up @@ -485,4 +483,191 @@ shutdown.

## Configuration files and environment variables

// TODO: Add details here
**Note** This information applies to the OpenTelemetry SDK version 1.4.0 and
newer only.

The OpenTelemetry .NET SDK integrates with the standard
[configuration](https://learn.microsoft.com/dotnet/core/extensions/configuration)
and [options](https://learn.microsoft.com/dotnet/core/extensions/options)
patterns provided by .NET. The configuration pattern supports building a
composited view of settings from external sources and the options pattern helps
use those settings to configure features by binding to simple classes.

### How to set up configuration

The following sections describe how to set up configuration based on the host
and OpenTelemetry API being used.

#### Using .NET hosts with the OpenTelemetry.Extensions.Hosting package

`ASP.NET Core` and [.NET Generic
Host](https://learn.microsoft.com/dotnet/core/extensions/generic-host) users
using the
[OpenTelemetry.Extensions.Hosting](../../../src/OpenTelemetry.Extensions.Hosting/README.md)
package do not need to do anything extra to enable `IConfiguration` support. The
OpenTelemetry SDK will automatically use whatever `IConfiguration` has been
supplied by the host. The host by default will load environment variables,
command-line arguments, and config files. See [Configuration in
.NET](https://learn.microsoft.com/dotnet/core/extensions/configuration) for
details.

#### Using Sdk.CreateTracerProviderBuilder directly

By default the `Sdk.CreateTracerProviderBuilder` API will create an
`IConfiguration` from environment variables. The following example shows how to
customize the `IConfiguration` used by `Sdk.CreateTracerProviderBuilder` for
cases where additional sources beyond environment variables are required.

```csharp
// Build configuration from sources. Order is important.
var configuration = new ConfigurationBuilder()
.AddJsonFile("./myOTelSettings.json")
.AddEnvironmentVariables()
.AddCommandLine(args)
.Build();

// Set up a TracerProvider using the configuration.
var provider = Sdk.CreateTracerProviderBuilder()
.ConfigureServices(services => services.AddSingleton<IConfiguration>(configuration))
.Build();
```

### OpenTelemetry Specification environment variables

The [OpenTelemetry
Specification](https://github.com/open-telemetry/opentelemetry-specification)
defines [specific environment
variables](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md)
which may be used to configure SDK implementations.

The OpenTelemetry .NET SDK will look for the environment variables defined in
the specification using `IConfiguration` which means in addition to environment
variables users may also manage these settings via the command-line,
configuration files, or any other source registered with the .NET configuration
engine. This provides greater flexibility than what the specification defines.

**Note** Not all of the environment variables defined in the specification are
supported. Consult the individual project README files for details on specific
environment variable support.

As an example the OpenTelemetry Specification defines the `OTEL_SERVICE_NAME`
environment variable which may be used to configure the service name emitted on
telemetry by the SDK.

A traditional environment variable is set using a command like `set
OTEL_SERVICE_NAME=MyService` on Windows or `export OTEL_SERVICE_NAME=MyService`
on Linux.

That works as expected but the OpenTelemetry .NET SDK is actually looking for
the `OTEL_SERVICE_NAME` key in `IConfiguration` which means it may also be
configured in any configuration source registered with the
`IConfigurationBuilder` used to create the final configuration for the host.

Below are two examples of configuring the `OTEL_SERVICE_NAME` setting beyond
environment variables.

* Using appsettings.json:

```json
{
"OTEL_SERVICE_NAME": "MyService"
}
```

* Using command-line:

```sh
dotnet run --OTEL_SERVICE_NAME "MyService"
```

**Note** The [.NET
Configuration](https://learn.microsoft.com/dotnet/core/extensions/configuration)
pattern is hierarchical meaning the order of registered configuration sources
controls which value will seen by the SDK when it is defined in multiple
sources.

### Using the .NET Options pattern to configure the SDK

Options are typically simple classes containing only properties with public
"getters" and "setters" (aka POCOs) and have "Options" at the end of the class
name. These options classes are primarily used when interacting with the
`TracerProviderBuilder` to control settings and features of the different SDK
components.

Options classes can always be configured through code but users typically want to
control key settings through configuration.

The following example shows how to configure `JaegerExporterOptions` by binding
to an `IConfiguration` section.

Json config file (usually appsettings.json):

```json
{
"OpenTelemetry": {
"Jaeger": {
"Protocol": "UdpCompactThrift"
"AgentHost": "localhost",
"AgentPort": 6831,
"BatchExportProcessorOptions": {
"ScheduledDelayMilliseconds": 5000
}
}
}
}
```

Code:

```csharp
var appBuilder = WebApplication.CreateBuilder(args);

appBuilder.Services.Configure<JaegerExporterOptions>(
appBuilder.Configuration.GetSection("OpenTelemetry:Jaeger"));

appBuilder.Services.AddOpenTelemetryTracing(
builder => builder.AddJaegerExporter());
```

The OpenTelemetry .NET SDK supports running multiple `TracerProvider`s inside
the same application and it also supports registering multiple similar
components such as exporters into a single `TracerProvider`. In order to allow
users to target configuration at specific components a "name" parameter is
typically supported on configuration extensions to control the options instance
used for the component being registered.

The below example shows how to configure two `JaegerExporter` instances inside a
single `TracerProvider` sending to different ports.

Json config file (usually appsettings.json):

```json
{
"OpenTelemetry": {
"JaegerPrimary": {
"AgentPort": 1818
},
"JaegerSecondary": {
"AgentPort": 8818
}
}
}
```

Code:

```csharp
var appBuilder = WebApplication.CreateBuilder(args);

appBuilder.Services.Configure<JaegerExporterOptions>(
"JaegerPrimary",
appBuilder.Configuration.GetSection("OpenTelemetry:JaegerPrimary"));

appBuilder.Services.Configure<JaegerExporterOptions>(
"JaegerSecondary",
appBuilder.Configuration.GetSection("OpenTelemetry:JaegerSecondary"));

appBuilder.Services.AddOpenTelemetryTracing(builder => builder
.AddJaegerExporter(name: "JaegerPrimary", configure: null)
.AddJaegerExporter(name: "JaegerSecondary", configure: null));
```
123 changes: 72 additions & 51 deletions docs/trace/extending-the-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,40 +119,44 @@ Contrib](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/mai
If you are writing an instrumentation library yourself, use the following
guidelines.

### Writing own instrumentation library
### Writing a custom instrumentation library

This section describes the steps required to write your own instrumentation
This section describes the steps required to write a custom instrumentation
library.

*If you are writing a new library or modifying an existing library, the
recommendation is to use [ActivitySource API/OpenTelemetry
**Note** If you are writing a new library or modifying an existing library the
recommendation is to use the [ActivitySource API/OpenTelemetry
API](../../../src/OpenTelemetry.Api/README.md#introduction-to-opentelemetry-net-tracing-api)
to instrument it and emit activity/span. If a library is instrumented using
ActivitySource API, then there is no need of writing a separate instrumentation
library, as instrumented and instrumentation library become same in this case.
For applications to collect traces from this library, all that is needed is to
enable the ActivitySource for the library using `AddSource` method of the
`TracerProviderBuilder`. The following section is applicable only if you are
writing an instrumentation library for an instrumented library which you cannot
modify to emit activities directly.*
to emit activity/span instances directly. If a library is instrumented using the
`ActivitySource` API then there isn't a need for a separate instrumentation
library to exist. Users simply need to configure the OpenTelemetry SDK to listen
to the `ActivitySource` used by the library by calling `AddSource` on the
`TracerProviderBuilder` being configured. The following section is applicable
only if you are writing an instrumentation library for something you cannot
modify to emit activity/span instances directly.

Writing an instrumentation library typically involves 3 steps.

1. First step involves "hijacking" into the target library. The exact mechanism
of this depends on the target library itself. For example,
System.Data.SqlClient for .NET Framework, which publishes events using
`EventSource`. The [SqlClient instrumentation
library](../../../src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlEventSourceListener.netfx.cs),
in this case subscribes to the `EventSource` callbacks.

2. Second step is to emit activities using the [ActivitySource
API](../../../src/OpenTelemetry.Api/README.md#introduction-to-opentelemetry-net-tracing-api).
In this step, the instrumentation library emits activities *on behalf of* the
target instrumented library. Irrespective of the actual mechanism used in
first step, this should be uniform across all instrumentation libraries. The
`ActivitySource` must be created using the name and version of the
instrumentation library (eg: "OpenTelemetry.Instrumentation.Http") and *not*
the instrumented library (eg: "System.Net.Http")
1. The first step involves attaching to the target library. The exact attachment
mechanism will depend on the implementation details of the target library
itself. For example, System.Data.SqlClient when running on .NET Framework
happens to publish events using an `EventSource` which the [SqlClient
instrumentation
library](../../../src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlEventSourceListener.netfx.cs)
listens to in order to trigger code as Sql commands are executed. The [.NET
Framework HttpWebRequest
instrumentation](../../../src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs)
patches the runtime code (using reflection) and swaps a static reference that
gets invoked as requests are processed for custom code. Every library will be
different.

2. The second step is to emit activity instances using the [ActivitySource
API](../../../src/OpenTelemetry.Api/README.md#introduction-to-opentelemetry-net-tracing-api)
**on behalf of** the target library. Irrespective of the actual mechanism
used in first step, this should be uniform across all instrumentation
libraries. The `ActivitySource` must be created using the name and version of
the instrumentation library (eg: "OpenTelemetry.Instrumentation.Http") and
**NOT** the instrumented library (eg: "System.Net.Http")
1. [Context
Propagation](../../../src/OpenTelemetry.Api/README.md#context-propagation):
If your library initiates out of process requests or accepts them, the
Expand All @@ -166,33 +170,42 @@ Writing an instrumentation library typically involves 3 steps.
GrpcClient, this is already provided to you and **do not require**
injecting/extracting `PropagationContext` explicitly again.)

3. Third step is an optional step, and involves providing extension methods on
`TracerProviderBuilder`, to enable the instrumentation. This is optional, and
the below guidance must be followed:

1. If the instrumentation library requires state management tied to that of
`TracerProvider`, then it must register itself with the provider with the
`AddInstrumentation` method on the `TracerProviderBuilder`. This causes
the instrumentation to be created and disposed along with
`TracerProvider`. If the above is required, then it must provide an
extension method on `TracerProviderBuilder`. Inside this extension
method, it should call the `AddInstrumentation` method, and `AddSource`
method to enable its ActivitySource for the provider. An example
instrumentation using this approach is [SqlClient
3. The third step is an optional step, and involves providing extension methods
on `TracerProviderBuilder` and/or `IServiceCollection` to enable the
instrumentation. For help in choosing see: [Registration extension method
guidance for library
authors](#registration-extension-method-guidance-for-library-authors). This
is optional, and the below guidance should be followed:

* If the instrumentation library requires state management tied to that of
`TracerProvider` then it should:

* Implement `IDisposable`.

* Provide an extension method which calls `AddSource` (to enable its
`ActivitySource`) and `AddInstrumentation` (to enable state management)
on the `TracerProviderBuilder` being configured.

An example instrumentation using this approach is [SqlClient
instrumentation](../../../src/OpenTelemetry.Instrumentation.SqlClient/TracerProviderBuilderExtensions.cs).
**CAUTION**: The instrumentation libraries requiring state management
are usually hard to auto-instrument. Therefore, they take the risk of not
being supported by [OpenTelemetry .NET Automatic Instrumentation](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation).

2. If the instrumentation library does not requires any state management
tied to that of `TracerProvider`, then providing `TracerProviderBuilder`
extension method is optional. If provided, then it must call `AddSource`
to enable its ActivitySource for the provider.
**CAUTION**: The instrumentation libraries requiring state management are
usually hard to auto-instrument. Therefore, they take the risk of not
being supported by [OpenTelemetry .NET Automatic
Instrumentation](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation).

3. If instrumentation library does not require state management, and is not
providing extension method, then the name of the `ActivitySource` used by
the instrumented library must be documented so that end users can enable
it using `AddSource` method on `TracerProviderBuilder`.
* If the instrumentation library does not require any state management, then
providing an extension method is optional.

* If an extension is provided it should call `AddSource` on the
`TracerProviderBuilder` being configured to enable its
`ActivitySource`.

* If an extension is not provided, then the name of the `ActivitySource`
used by the instrumented library must be documented so that end users
can enable it by calling `AddSource` on the `TracerProviderBuilder`
being configured. **Note** Changing the name of the source should be
considered a breaking change.

### Special case : Instrumentation for libraries producing legacy Activity

Expand Down Expand Up @@ -375,6 +388,14 @@ When providing registration extensions:
from starting. The OpenTelemetry SDK is allowed to crash if it cannot be
started. It **MUST NOT** crash once running.

**Note** The SDK implementation of `TracerProviderBuilder` ensures that the
[.NET
Configuration](https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration)
engine is always available by creating a root `IConfiguration` from environment
variables if it does not already exist in the `IServiceCollection` containing
the `TracerProvider`. Library authors can rely on `IConfiguration` always being
present in the final `IServiceProvider`.

### TracerProviderBuilder extension methods

When registering pipeline components (for example samplers, exporters, or
Expand Down

0 comments on commit 1daa2e3

Please sign in to comment.