Two routes. Automatic instrumentation needs no code changes and suits an app you would rather not rebuild. The SDK route is a few lines in Program.cs and gives finer control. Both send the same OTLP data.

Option A — automatic, no code changes

Option B — the SDK

AddOtlpExporter() with no arguments reads OTEL_EXPORTER_OTLP_ENDPOINT, _HEADERS and _PROTOCOL from the environment, so the same build works in every deployment.

Custom spans

.NET uses System.Diagnostics.Activity as its span type. An ActivitySource whose name is not registered with AddSource() produces null activities and no data — the usual reason custom spans go missing.