Summary
MAUI's MauiApp implements IHostApplicationBuilder but does not run IHostedService instances (no IHost.StartAsync loop). OpenTelemetry.Extensions.Hosting registers a TelemetryHostedService whose StartAsync is what materializes the TracerProvider, attaches its ActivityListeners, and begins exporting. On MAUI, that service never runs — so:
- Logs still work (they hook
ILoggerFactory synchronously during AddOpenTelemetry()).
- Auto-instrumentation (
AddHttpClientInstrumentation) does not — no listener → no spans → no traceparent header injection → API sees every request as a root span and correlation joins return zero rows in Application Insights.
Evidence from this session
OpenTelemetry.Instrumentation.Http 1.15.0 is present, .AddHttpClientInstrumentation() has been on the TracerProvider since commit 216a2da1 (March 4).
- Ran a trim-disabled (
<MtouchLink>None</MtouchLink>) Release build on DX24; bundle 136 MB / 333 DLLs — trimming disproven.
- KQL showed 127 mobile traces with empty
operation_Id on every entry → no ambient Activity ever existed on the device.
- Existing
OpenTelemetryInitializer : IMauiInitializeService used nullable services.GetService<TracerProvider>() — may have silently returned null.
Workaround shipped
PR #XXX (replace) adds an ApiActivityHandler DelegatingHandler and a dedicated ActivitySource (SentenceStudio.Mobile.HttpClient) explicitly registered with the TracerProvider. It also hardens OpenTelemetryInitializer to use GetRequiredService<TracerProvider>() to force materialization. This makes correlation work today but does not address the root cause.
Root-cause fix (this issue)
Decide between:
- Explicitly call
TelemetryHostedService.StartAsync from OpenTelemetryInitializer — get every auto-instrumentation listener attached for free, no per-call handler needed.
- Build TracerProvider directly via
Sdk.CreateTracerProviderBuilder() instead of going through services.AddOpenTelemetry().WithTracing(...), bypassing the hosted-service dependency entirely.
- Upstream fix — ask the OpenTelemetry maintainers for a non-
IHostedService bootstrap path for IHostApplicationBuilder implementations that don't run hosted services (MAUI, some WPF/WinForms IHost scenarios).
If option 1 or 2 lands, the ApiActivityHandler can stay (it's still a correct Client span for each API call) and the AddHttpClientInstrumentation() spans become additive. No code needs to be reverted.
Acceptance criteria
- On a clean install of the mobile app, a single API call produces:
- At least one
dependencies row in App Insights from the mobile side.
- A matching
requests row on the server with equal operation_Id to that dependency.
- Server
operation_Id != operation_ParentId (i.e., mobile traceparent actually propagated).
- Works without the manual
ApiActivityHandler being the only source.
Links
Summary
MAUI's
MauiAppimplementsIHostApplicationBuilderbut does not runIHostedServiceinstances (noIHost.StartAsyncloop).OpenTelemetry.Extensions.Hostingregisters aTelemetryHostedServicewhoseStartAsyncis what materializes theTracerProvider, attaches itsActivityListeners, and begins exporting. On MAUI, that service never runs — so:ILoggerFactorysynchronously duringAddOpenTelemetry()).AddHttpClientInstrumentation) does not — no listener → no spans → notraceparentheader injection → API sees every request as a root span and correlation joins return zero rows in Application Insights.Evidence from this session
OpenTelemetry.Instrumentation.Http 1.15.0is present,.AddHttpClientInstrumentation()has been on the TracerProvider since commit216a2da1(March 4).<MtouchLink>None</MtouchLink>) Release build on DX24; bundle 136 MB / 333 DLLs — trimming disproven.operation_Idon every entry → no ambient Activity ever existed on the device.OpenTelemetryInitializer : IMauiInitializeServiceused nullableservices.GetService<TracerProvider>()— may have silently returned null.Workaround shipped
PR #XXX (replace) adds an
ApiActivityHandlerDelegatingHandlerand a dedicatedActivitySource(SentenceStudio.Mobile.HttpClient) explicitly registered with the TracerProvider. It also hardensOpenTelemetryInitializerto useGetRequiredService<TracerProvider>()to force materialization. This makes correlation work today but does not address the root cause.Root-cause fix (this issue)
Decide between:
TelemetryHostedService.StartAsyncfromOpenTelemetryInitializer— get every auto-instrumentation listener attached for free, no per-call handler needed.Sdk.CreateTracerProviderBuilder()instead of going throughservices.AddOpenTelemetry().WithTracing(...), bypassing the hosted-service dependency entirely.IHostedServicebootstrap path forIHostApplicationBuilderimplementations that don't run hosted services (MAUI, some WPF/WinForms IHost scenarios).If option 1 or 2 lands, the
ApiActivityHandlercan stay (it's still a correctClientspan for each API call) and theAddHttpClientInstrumentation()spans become additive. No code needs to be reverted.Acceptance criteria
dependenciesrow in App Insights from the mobile side.requestsrow on the server with equaloperation_Idto that dependency.operation_Id != operation_ParentId(i.e., mobiletraceparentactually propagated).ApiActivityHandlerbeing the only source.Links
.squad/decisions/inbox/wash-server-appinsights-shipped.md(merged)