OpenTelemetry
OpenTelemetry
Section titled “OpenTelemetry”Joinery.Dovetail.OpenTelemetry exposes OpenTelemetry’s configuration surface — the top-level IOpenTelemetryBuilder, the
resource builder, and the tracer, meter and logger provider builders — as ordinary Dovetail joints. Any
library can contribute an activity source, a meter, a log processor, or a resource attribute without taking a direct dependency on
how the host wires up OpenTelemetry. Coming from LaunchPad.Telemetry? Its parity check maps each of
its conventions to a joint here.
Turning it on
Section titled “Turning it on”An application turns OpenTelemetry on by referencing Joinery.Dovetail.OpenTelemetry along with Joinery.Dovetail.Hosting.
Joinery.Dovetail.Sdk adds Joinery.Dovetail.OpenTelemetry to every project that references OpenTelemetry or
OpenTelemetry.Extensions.Hosting, and to every file-based app (dotnet run app.cs) along with OpenTelemetry.Extensions.Hosting;
set DovetailEnableOpenTelemetry to false to leave OpenTelemetry to the application.
Joinery.Dovetail.Hosting then emits joints into the application that add OpenTelemetry to its services, apply every
OpenTelemetry joint, and start it. OpenTelemetry only listens to activity sources and meters once its providers are resolved,
which OpenTelemetry.Extensions.Hosting does from a hosted service, so what an application needs depends on whether its host runs
hosted services:
| Host | Runs hosted services | Needs OpenTelemetry.Extensions.Hosting | Started by |
|---|---|---|---|
| Generic host: workers, ASP.NET Core | yes | yes | its hosted service |
Uno Platform (Uno.Extensions.Hosting) | once started: NavigateAsync<TShell>() starts the host, ConfigureDovetail() doesn’t | no | DovetailOpenTelemetryUnoStartJoint, once the host is built |
.NET MAUI (MauiAppBuilder) | no | no | DovetailOpenTelemetryMauiStartJoint, once the app is built |
| Blazor WebAssembly | from .NET 11 | no | DovetailOpenTelemetryWebAssemblyStartJoint, once the host is built (and on .NET 11 the hosted service too, when referenced) |
- With
OpenTelemetry.Extensions.Hosting, whatever the host,DovetailOpenTelemetryJointcallsservices.AddOpenTelemetry(). On .NET 11 it also traces ASP.NET Core’sMicrosoft.AspNetCoreactivity source. - Without it,
DovetailOpenTelemetryServicesJointadds OpenTelemetry to the services directly, for the hosts that don’t need it. - A start joint resolves the meter, tracer and logger providers right after the host is built, as the hosted service does when the host starts, so OpenTelemetry runs on those hosts without a package the application wouldn’t otherwise use.
- Only an application whose hosts all need
OpenTelemetry.Extensions.Hosting(the generic host, with no Uno, MAUI or WebAssembly host) and that doesn’t reference it fails to compile, with an#error(CS1029) that says to add it. Packages that build on it, such as the Azure Monitor distro or .NET Aspire’s service defaults, bring it along, so they count.
OpenTelemetry runs in the browser too: in single-threaded Blazor WebAssembly its batch export processor and periodic metric reader run on tasks instead of threads.
Only applications and test projects get the joint, as an internal class: a library’s copy would be exported to every application that references it. A test project that imports its application’s joints gets the application’s copy as well as its own, and only the first one to run applies OpenTelemetry.
Every [DovetailExport] joint, and anything registered with a DovetailContextBuilder method such as
ConfigureTracerProvider(...), is then applied with nothing else to call:
var host = await Host .CreateApplicationBuilder(args) .ConfigureDovetail(dovetail => dovetail.ConfigureTracerProvider((_, tracing) => tracing.AddConsoleExporter()));The joints
Section titled “The joints”Joinery.Dovetail.OpenTelemetry declares one <DovetailJoint>, OpenTelemetry, for OpenTelemetry.IOpenTelemetryBuilder:
IOpenTelemetryJoint, its OpenTelemetryJointDelegate, the ConfigureOpenTelemetry(...) builder method and the
ApplyOpenTelemetry extension that the emitted joint calls. Narrower interfaces derive from IOpenTelemetryJoint, each for one
of OpenTelemetry’s builders:
| Joint interface | Builder | DovetailContextBuilder method |
|---|---|---|
IOpenTelemetryJoint | OpenTelemetry.IOpenTelemetryBuilder | ConfigureOpenTelemetry |
IResourceBuilderJoint | OpenTelemetry.Resources.ResourceBuilder | ConfigureResourceBuilder |
ITracerProviderJoint | OpenTelemetry.Trace.TracerProviderBuilder | ConfigureTracerProvider |
IMeterProviderBuilderJoint | OpenTelemetry.Metrics.MeterProviderBuilder | ConfigureMeterProvider |
ILoggerProviderBuilderJoint | OpenTelemetry.Logs.LoggerProviderBuilder | ConfigureLoggerProvider |
Since they’re all OpenTelemetry joints, they run together in the executor’s usual priority order. A library that only wants to
register an activity source implements ITracerProviderJoint without knowing anything about WithMetrics, WithLogging or the
resource — the same narrow, composable extension point the rest of Dovetail follows.
Example: contributing an activity source
Section titled “Example: contributing an activity source”A library adds a System.Diagnostics.ActivitySource to tracing by implementing ITracerProviderJoint and marking the class
[DovetailExport], the same pattern used for any other discovered Dovetail joint:
using System.Diagnostics;using Joinery.Dovetail;using Joinery.Dovetail.Attributes;using Joinery.Dovetail.OpenTelemetry.Joints;using OpenTelemetry.Trace;
namespace MyLibrary;
[DovetailExport]public class MyLibraryTracing : ITracerProviderJoint{ public static readonly ActivitySource ActivitySource = new("MyLibrary");
public void Register(IDovetailContext context, TracerProviderBuilder builder) => builder.AddSource(ActivitySource.Name);}What this package does not do
Section titled “What this package does not do”Joinery.Dovetail.OpenTelemetry only opens the extension points — it does not ship any exporters or instrumentation. In
particular, it does not include:
- The console exporter, the OTLP exporter, or any other exporter package.
- ASP.NET Core, HttpClient, or other instrumentation packages.
- Any default sampler, resource attribute, or processor configuration of its own. The OpenTelemetry SDK’s default resource still applies, and already has the telemetry SDK attributes and the environment variable detector.
Consumers add exporters and instrumentation the same way they add anything else to a provider: by writing their own
ITracerProviderJoint/IMeterProviderBuilderJoint/ILoggerProviderBuilderJoint implementation (or a plain
Configure{Kind}(...) delegate) that calls the relevant AddConsoleExporter(), AddOtlpExporter(),
AddAspNetCoreInstrumentation(), etc. This keeps Joinery.Dovetail.OpenTelemetry itself free of exporter dependencies, which also
keeps it friendly to trimming/AOT — see
Non-Goals in the OpenTelemetry design doc
for the full rationale.