The work for Milestone M0, introduced in Section 6 of the implementation plan.
M0 composes upstream OpenTelemetry packages, adds a browser-native Azure Monitor
exporter, and adopts upstream browser instrumentation. It does not build our own
SDK - the multi-instance provider and per-instance lifecycle proven in
poc/ are M2 onward. It is single-instance and ES2022 ESM-only.
Each bullet below is one task. Its sub-bullets are the checklist that goes in the task description. Items marked Follow-up are out of M0.
Tasks are grouped into four phases:
| Phase | Areas |
|---|---|
| P1 Setup | Repository, build, and tooling; test frameworks |
| P2 Build | Distribution core; exporter; instrumentation; tracing |
| P3 Harden | Bundle size; release pipeline; legal and compliance |
| P4 Ship | Documentation and publication; Rayfin end-to-end validation |
- Confirm the beta requirements with Rayfin.
- Occurrence set, destination, browser support matrix, integration model.
- Confirm explicitly that single-instance and ES2022/modern-browser-only are acceptable for the beta.
- Stand up the workspace and build.
- Package boundaries: distribution core, Azure Monitor exporter, web analytics instrumentation, test utilities.
- TypeScript at ES2022. ESM-only output with
exportsmaps, nomain/module, declaration and sourcemap emit. - Pin exact upstream OpenTelemetry versions including the
0.xlogs line, and document the pins and the upgrade policy. - Shared lint and format configuration.
- Set up CI and dependency governance.
- Build, lint, unit tests and browser tests on every pull request, with branch protection.
- Component Governance, automated dependency updates, license scanning on every build.
- Set up API surface review.
- API Extractor or equivalent producing a reviewed API report, with a CI gate on unreviewed public API changes.
- The beta surface is the compatibility commitment, so it is reviewed before it ships.
- Stand up the test runners.
- Unit test runner with coverage reporting and an initial threshold.
- Playwright browser harness with the existing PoC fixtures ported across.
- Browser matrix (Chromium, Firefox, WebKit, Edge) against the confirmed support matrix.
- Build the test fixtures.
- Mock Azure Monitor ingestion endpoint asserting envelope shape without a network dependency.
- Shared utilities: in-memory exporters, fake clock, deterministic session and ID generation.
- Follow-up. Flake policy and CI retry/quarantine behavior for browser tests.
- Define the public initialization surface.
- One
initialize(config)returning a handle, with configuration passed through to upstream rather than re-modelled. - Confirm a single instance is the degenerate case of the M2 instance model - confirm only, build nothing for it.
- One
- Implement initialization.
- Construct the upstream tracer provider, logger provider, processors and exporters from configuration.
- Assemble the resource from upstream semantic conventions plus service, SDK and browser attributes.
- Configure upstream
BatchSpanProcessorandBatchLogRecordProcessorwith browser-appropriate defaults.
- Implement lifecycle and diagnostics.
- Page-lifecycle flush on
pagehide/visibilitychange. forceFlush()andshutdown()on the handle, delegating to both upstream providers and idempotent across them.- Diagnostics through the upstream
diagAPI, including visibility into dropped or rejected telemetry.
- Page-lifecycle flush on
- Test the core.
- Unit and browser tests for initialization, flush, shutdown and configuration pass-through.
- Follow-up. Detect and report a pre-existing global OTel SDK registration without overwriting it.
A new browser exporter written in this repository. Neither existing
implementation can be depended on: @azure/monitor-opentelemetry-exporter is
Node-only (package.json declares no browser field, src/platform/ contains
only nodejs/, and it depends on @azure/core-rest-pipeline,
@azure/core-client, @azure-rest/core-client and @azure/core-process), and
ApplicationInsights-JS is a browser SDK with its own telemetry model rather
than an OTel exporter. Both are first-party and are used as references.
| Concern | Reference |
|---|---|
| OTel span and log record to envelope mapping | @azure/monitor-opentelemetry-exporter (spanUtils, logUtils) |
| Envelope shape and field names | Both |
| Connection string and sovereign cloud endpoints | Both |
Browser transport: fetch, sendBeacon, unload behavior |
ApplicationInsights-JS sender |
| Breeze response codes, retry, backoff, throttle, redirect | Both |
| PageView and CustomEvent envelopes | ApplicationInsights-JS - no equivalent in the Node exporter |
- Spike the browser ingestion path.
- CORS preflight,
keepalivepayload limits,sendBeaconsize caps, gzip support.
- CORS preflight,
- Implement the envelope mapping.
- Envelope types and the connection string parser, including sovereign clouds.
- Span exporter:
ReadableSpanto envelope, including attribute, status and duration mapping. - Log record exporter: log record with
eventNameto envelope, including severity and attribute mapping. - PageView and CustomEvent envelopes.
- Implement the browser sender.
fetchwithkeepalive,sendBeaconfallback on page unload, payload size limits, gzip if supported.- Retry, backoff, throttle and redirect handling per the ingestion response contract.
- Test and measure the exporter.
- Unit tests for mapping and transport, plus a browser test exporting end to end against the mock ingestion endpoint.
- Cross-check envelope output against the two reference implementations.
- Measure the standalone gzipped size and assert no
@azure/core-*package is reachable from the bundle.
- Follow-up. Sampling field and instrumentation key population on the envelope.
Out of M0: offline persistence, AAD authentication, live metrics, sampling beyond a fixed rate.
Adopted from @opentelemetry/browser-instrumentation, not written here. See the
reference tables for what each module emits and how far semantic
conventions actually cover it.
- Settle the event catalogue.
- Follow
exceptionandbrowser.web_vitalwhere conventions exist. - For page view, where no convention and no upstream module exists, define the event name and attributes and document it as a candidate for upstream contribution.
- Confirm the set against the ApplicationInsights-JS occurrence set. Decide adopt-versus-build here.
- Follow
- Adopt the M0 instrumentation modules.
experimental/navigation: decide the Navigation API path (useNavigationApiIfAvailable), verify SPA soft-navigation coverage against Rayfin's routing. It captures route changes by default, the opposite ofenableAutoRouteTracking.experimental/errors: confirm stack capture andapplyCustomAttributesmeet the Azure Monitor exception envelope needs. It captures unhandled rejections by default and recovers cross-origin"Script error.". ProducingparsedStackis ours.- Pin the module versions and record the breaking-change exposure:
@opentelemetry/browser-instrumentationis0.8.1and every module is behind an./experimental/*subpath.
- Build the instrumentation registration surface.
- Each instrumentation individually enableable and individually importable.
- Browser tests asserting the emitted event shape for every shipped occurrence type.
- Document what each instrumentation collects and the available opt-outs, as input to the privacy review.
- Follow-up. Adopt the remaining upstream modules.
experimental/navigation-timing. Its config interface is empty, so there is no way to trim its 24 attributes or tag records.experimental/resource-timing, using itsinitiatorTypesandignoreUrlsfilters to control volume.experimental/web-vitals, which wraps Google'sweb-vitalspackage.experimental/user-actionfor click capture. Cost is the privacy-safe configuration and deciding the content model versusclickanalytics-js.
- No page view concept upstream at all - not in the modules, not in semantic conventions. No page view name, no page view duration.
- Page view performance has the numbers but the wrong shape.
browser.navigation_timingcarries a superset of the Application Insights timing fields, but as a standalone flat event with no page identity. - The click content model differs.
user-actionreadsdata-otel-*intobrowser.element.attributes- a different prefix fromdata-, with no ancestor walk or content-name extraction. - No user identity in either the instrumentation or the SDK package, and no cookie manager.
- No metrics and no page visit time.
The adopt-versus-build decision is tracked in §7 of the implementation plan.
experimental/fetch and experimental/xhr are wired, not written. Both emit
SpanKind.CLIENT spans with stable HTTP semantic conventions and perform
CORS-aware propagation.inject.
- Wire HTTP instrumentation and propagation.
experimental/fetchandexperimental/xhrwith browser-appropriate defaults, a URL filter and thesanitizeUrlhook.- W3C Trace Context and Baggage propagation, including the allowed-origin list for header injection.
- Implement session and page context.
- Session ID generation, storage, timeout and renewal; apply
session.idto both signals. No upstream equivalent exists. - Document and page context processor applying URL and page attributes to both spans and log records.
- Session ID generation, storage, timeout and renewal; apply
- Test context handling.
- Browser tests for propagation round-trip, session continuity and context stamping on both signals.
- Document the async context limitation and the supported explicit-context pattern.
Size is measured by automation in the repository, not written down in a document
that goes stale. The PoC already ships a harness (poc/size/measure.mjs,
npm run size) that runs a real production build per scenario and regenerates
../poc/SIZE_REPORT.md; M0 promotes it to a repository
tool that runs in CI.
- Promote the size harness into a repository tool.
- Measure every published entry point tree-shaken through a real bundler, reported in gzip.
- Measure per layer, not just totals: API only, plus SDK, plus the distribution, plus the exporter, plus each instrumentation.
- Per-package deltas must not be summed - shared dependencies are counted once, so report measured totals alongside deltas.
- Regenerate the size report as a build artifact on every run.
- Report and gate size in CI.
- Size report comparing against the base branch, report-only initially.
- Publish the numbers from each
mainbuild so regressions show as a trend. - Include reference points measured the same way:
ApplicationInsights-JSand upstream@opentelemetry/browser-sdk. - Set initial absolute budgets per entry point and decide whether the gate blocks or reports for the beta.
- Lint rule banning barrel imports from
@opentelemetry/semantic-conventions; individual constants are effectively free, the barrel is not.
- Follow-up. Tree-shaking verification and tuning.
- Verify
sideEffects: falseempirically: a bare side-effect import must bundle to zero bytes. - Assert importing one instrumentation does not pull in the others.
- Size tuning pass once the full feature set is in.
- Verify
- Define the release strategy.
- Prerelease versioning,
betadist-tag, changelog generation, stability contract. - Reserve the npm scope and package names; confirm ownership and publish rights for the publishing identity.
- Prerelease versioning,
- Build the release pipeline.
- Build, test, pack, sign off, publish, with an approval gate and no local publishing.
verify-packvalidation: install the real tarball and load everyexportsentry, pluspublintand are-the-types-wrong checks.- SBOM for each published package, attached to the release.
- Follow-up. Package provenance and signing on publish, and a release runbook covering rollback, deprecation and hotfix.
Queue-driven work owned by other teams, filed at the start rather than when the code is ready.
- File open-source release approval.
- New public repository and package.
- Confirm the license, add
LICENSE, and add the contribution and code-of-conduct files required for public release. - Third-party dependency license review and attribution: produce
NOTICEand confirm every transitive dependency is on an approved license.
- File security and privacy review.
- Threat model covering browser data collection, connection-string handling, transport, and the supply chain of the published package.
- Privacy review and data-collection documentation.
- File naming, export and governance approvals.
- Package name and branding approval, including trademark review of the name and npm scope.
- Export control classification for the published package.
- Public repository governance: security policy, issue templates, support statement, maintainer list.
- Write the beta documentation.
- Getting-started guide, configuration reference and event catalogue.
- Known beta limitations: single instance, no pre-ES2015 browser support,
development-stability semantic conventions, no offline persistence, no AAD, async context constraints, deferred instrumentations.
- Validate the beta build.
- End-to-end in a real browser against a real Application Insights resource, covering every shipped occurrence type and manual telemetry.
- Browser compatibility across the confirmed matrix.
- Publish the beta.
- Publish to npm at the
betadist-tag. - Capture feedback and file follow-up work against M1 onward.
- Publish to npm at the
- Follow-up. Sample application and a performance sanity check.
- Sample demonstrating installation, configuration, manual telemetry and shutdown.
- Initialization cost, steady-state overhead, export volume under a realistic page.
In Rayfin's own application, against their own Application Insights resource, on their browser matrix. The mock ingestion endpoint and our own browser tests do not substitute for it.
- Agree the validation plan and environment.
- Which application and environment, which routes and user journeys, which Application Insights resource, which browsers, who runs it and who signs off.
- Provision the Application Insights resource, connection string handling, and confirm ingestion is reachable from their origin, CORS and CSP included.
- Run the integration.
- Get a pre-publish build into Rayfin's hands - private dist-tag or tarball - so the integration is exercised before the public publish.
- Install the package, call
initialize()in their bootstrap, configure the connection string, wire shutdown and flush into their page lifecycle. - Capture friction points as documentation or API defects.
- Verify and sign off.
- Query their Application Insights resource for: page views on hard load and
SPA route change, exceptions with a usable
parsedStack, fetch/XHR dependencies, manual events and traces,session.idcontinuity across a journey, and correlation between a browser dependency and its server-side request. - Validate across their browser matrix and measure the bundle-size impact on their real production build.
- Run an acceptance checklist against the agreed plan, record the result, file every defect and gap found.
- Query their Application Insights resource for: page views on hard load and
SPA route change, exceptions with a usable
- Follow-up. Telemetry volume and cost review once the integration is running against real traffic.
Verified facts the work items above depend on.
@opentelemetry/browser-instrumentation 0.8.1. Every module is behind an
./experimental/<name> subpath and imported individually. Seven of the nine emit
log records via the Logs API with eventName set.
| Occurrence | Module | Emits |
|---|---|---|
| Page load + SPA soft navigation | experimental/navigation |
log records, browser.navigation |
| Unhandled errors + promise rejections, with stack | experimental/errors |
log records, exception semconv |
| Navigation and document load timing | experimental/navigation-timing |
log records, browser.navigation_timing, 24 attributes |
| Resource timing | experimental/resource-timing |
log records, browser.resource_timing |
| Web vitals | experimental/web-vitals |
log records, browser.web_vital |
| Click / user action | experimental/user-action |
log records, browser.user_action.click, data-otel-* model |
| Console | experimental/console |
log records, browser.console |
| fetch / XHR | experimental/fetch, experimental/xhr |
SpanKind.CLIENT spans |
The opentelemetry-js-contrib browser instrumentations are not used. The browser
SIG has stated that browser packages spread across opentelemetry-js and
opentelemetry-js-contrib "will be migrated here or deprecated", and that the
span-based user-interaction instrumentation "will be deprecated in favor of
event-based user action telemetry".
Verified against open-telemetry/semantic-conventions. Only exception is
stable; everything else we emit targets a development convention that can
change, and page view has no convention at all.
| Occurrence | Convention | Stability |
|---|---|---|
| Exceptions | exception |
stable |
| Web vitals | browser.web_vital + browser.web_vital.* |
development |
| Clicks | app.screen.click, app.widget.click |
development |
| Session start/end | session.start, session.end |
development |
| Crash, jank | app.crash, app.jank |
development |
| Browser context | browser.brands, browser.platform, browser.mobile, browser.language, browser.document.url.full |
development |
| Page view | none | - |
browser.navigation, browser.navigation_timing, browser.resource_timing,
browser.user_action.click and browser.console are defined by the
instrumentation package, not by the spec.