Add multi-instance SDK support via delegating providers#177
Add multi-instance SDK support via delegating providers#177JacksonWeber wants to merge 2 commits into
Conversation
Introduce createMicrosoftOpenTelemetryInstance to run multiple isolated SDK instances in one Node.js runtime. Parent (delegating) Tracer/Meter/Logger providers route per-call to the current child instance, resolved via an AsyncLocalStorage-backed ambient context (runWithInstance) with a default fallback. Each instance owns its own resource, sampler, processors, readers, and exporters. Additive and opt-in; the existing useMicrosoftOpenTelemetry single-instance path is unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds an opt-in multi-instance Microsoft OpenTelemetry SDK mode that allows multiple isolated telemetry pipelines to coexist in a single Node.js process by registering global delegating providers that route per-call to an “ambient” (AsyncLocalStorage-bound) instance, falling back to a default instance.
Changes:
- Introduces
createMicrosoftOpenTelemetryInstance()/runWithMicrosoftOpenTelemetryInstance()and theMicrosoftOpenTelemetryInstancehandle type. - Implements multi-instance runtime: instance registry + id binding, one-time global setup (context manager/propagator + parent providers), and delegating tracer/meter/logger providers.
- Adds functional coverage for isolation and ambient routing; promotes
@opentelemetry/context-async-hooksto a direct dependency.
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| test/internal/functional/multiInstance.test.ts | New functional tests validating per-instance isolation and ambient routing via global API |
| src/types.ts | Adds MicrosoftOpenTelemetryInstance public interface type |
| src/index.ts | Re-exports new multi-instance APIs/types from the package entrypoint |
| src/distro/multiInstance/instanceRegistry.ts | Registry + AsyncLocalStorage-backed “current instance” binding and resolution |
| src/distro/multiInstance/instance.ts | Builds per-instance child providers/pipelines and lifecycle methods (flush/shutdown) |
| src/distro/multiInstance/index.ts | Exposes multi-instance APIs from the distro layer |
| src/distro/multiInstance/globalSetup.ts | One-time process-global setup for context manager/propagator + parent providers |
| src/distro/multiInstance/delegatingProviders.ts | Parent providers + delegating tracer/meter/logger implementations |
| src/distro/index.ts | Surfaces multi-instance exports through src/distro |
| package.json | Adds @opentelemetry/context-async-hooks direct dependency |
| package-lock.json | Locks the new dependency for npm ci installs |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- withInstance: skip binding unknown/stale ids so resolution falls back to the default instance instead of producing silent no-op telemetry. - instance.shutdown: wrap disposers via Promise.resolve().then so a synchronous throw is captured and does not abort the rest of shutdown. - multiInstance test: also disable the logs provider and the global context manager in afterEach to prevent cross-test contamination. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rads-1996
left a comment
There was a problem hiding this comment.
Is it possible to break the PR into small chunks, so it is easier to review? Or does it make logical sense to have everything together?
I don't believe that will work well for this PR as it implements a single contained feature. If a single component of it is split off it won't function. Only way I can think to split it is per signal, but I'm not sure that'd make it much easier to review. |
Summary
Adds opt-in support for running multiple isolated Microsoft OpenTelemetry SDK instances in a single Node.js runtime. Today
useMicrosoftOpenTelemetry()registers global providers per signal, so a second initialization clobbers the first. This PR introduces a parent/child delegating-provider architecture so independent pipelines (e.g. two Application Insights resources with different connection strings) can coexist.GA constraint respected: all changes are additive and non-breaking. The existing single-instance
useMicrosoftOpenTelemetry()path is untouched.What's new
createMicrosoftOpenTelemetryInstance(options, { makeDefault? })returns aMicrosoftOpenTelemetryInstancehandle (getTracer/getMeter/getLogger,runWithInstance,forceFlush,shutdown).runWithMicrosoftOpenTelemetryInstance(id, fn)binds an ambientcurrent instanceso code using the global OTel API routes to the right pipeline.Design
src/distro/multiInstance/:current instance.NodeTracerProvider/MeterProvider/LoggerProvider(noNodeSDK.start(), no global registration), wiring Azure Monitor handlers + caller processors + console fallback, then registers it.Routing model: parents resolve a single current instance (ambient via
runWithInstance→ default fallback) and delegate, so telemetry stays isolated per instance rather than fanned out.Testing
test/internal/functional/multiInstance.test.ts: two instances with distinct (fake) connection strings; asserts each instance's spans reach only its own exporter, and that global-API spans insiderunWithInstanceroute to the bound instance./v2.1/trackrequests returned HTTP 200).Dependencies
@opentelemetry/context-async-hooksfrom transitive to a direct dependency (now imported directly to register the context manager, since the multi-instance path does not callNodeSDK.start()). Nooverridesused.