You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Fix telemetry regressions from the applicationinsights 3.x upgrade - #2843
Since Azurite 3.37.0 (released 24 August 2026), Application Insights has been receiving far more Azurite telemetry, and it arrives as unknown_service:node with no version. The cause is the applicationinsights upgrade from 2.9.6 to 3.x (#2660, then 3.16.0 in #2726).
Version 3 is a compatibility layer over OpenTelemetry. It accepts several settings Azurite used without errors but ignores them. The existing unit tests called the helper functions directly rather than going through the SDK, so they didn't catch this.
What went wrong in 3.x
Setting Azurite relied on
Behaviour in 3.x
Effect
context.tags[cloudRole] = "Azurite_V1.0"
No longer sets the role. It is copied into every item as a custom field instead.
cloud_RoleName = unknown_service:node and a stray ai.cloud.role custom dimension
Version read automatically from package.json
Removed
application_Version is blank
samplingPercentage = 1
setup()/start() and the default global client use the distro's default 5 traces/second rate limiter, which replaces the 1% ratio
Instances below 5 requests/s send almost every request, about 100× the previous volume (for example Q_Messages_Dequeue polling)
addTelemetryProcessor(removeRoleInstance)
No-op (it only logs a warning)
The raw machine name is sent as cloud_RoleInstance
Global setup()/start()
Installs global OpenTelemetry providers, HTTP instrumentation, Live Metrics and host/OS resource detection in the process
host.id, host.name and os.version are sent in a per-batch _OTELRESOURCE_ metric. Live Metrics pings are sent. Global OpenTelemetry state is replaced inside the VS Code extension host.
maxBatchSize = 0
Not supported; telemetry is batched
The Stop event can be lost at exit
Changes (src/common/Telemetry.ts)
Isolated clients: Create clients with new TelemetryClient(connectionString, { useGlobalProviders: false }) and stop calling setup()/start(). Each client gets its own providers and its own sampling ratio, and nothing global is installed.
Role, version and instance: Set them through the OpenTelemetry resource:
service.name = Azurite_V1.0, which keeps existing queries and dashboards working.
service.version = the Azurite version.
service.instance.id = SHA-256 of the hostname, matching the previous privacy-review behaviour.
Resource metric: Disable the per-batch _OTELRESOURCE_ metric. The environment variable is set only while the exporters are constructed, then restored.
Sampling: Track requests under ROOT_CONTEXT, so another OpenTelemetry setup in the same process (for example another VS Code extension) can't make its sampled span the parent and bypass 1% sampling.
Request counts: Set microsoft.sample_rate on requests so the envelope sampleRate is 1 again. itemCount then still estimates total requests, as it did with 2.x.
Stop event: Flush the clients when it is sent.
authorization property: Only report known scheme names (SharedKey, SharedKeyLite, Bearer). Anything else is reported as Other, so a malformed header can't leak a credential. Normal values are unchanged.
Dependencies: Add @opentelemetry/api and @opentelemetry/resources as direct dependencies. Both were already installed transitively at the same versions (1.9.1 and 2.10.0).
Evidence
I ran the built Azurite against a local fake ingestion endpoint (only the Azure Monitor HTTPS calls were redirected), with real Blob, Queue and Table SDK traffic:
Scenario
Before
After
~4 req/s queue polling, 100 dequeues: request records sent
97
2–3 (≈1%)
903-request burst: request records sent
6 (rate-limited)
9–12 (≈1%)
cloud_RoleName
unknown_service:C:\Program Files\nodejs\node.exe
Azurite_V1.0
application_Version
(blank)
3.37.0
cloud_RoleInstance
raw hostname
SHA-256 hash
_OTELRESOURCE_ metric (host.id, host.name, os.*)
sent
not sent
Live Metrics pings
sent
none
Stop event delivered (polling run)
no
yes
Custom dimensions are back to Azurite's own properties. ai.cloud.role and _MS.ProcessedByMetricExtractors no longer appear.
Tests
tests/common/Telemetry.test.ts now sends telemetry through the real applicationinsights SDK to a local HTTPS ingestion server. It checks:
role, version and hashed instance
sampleRate is 1
no _OTELRESOURCE_ metric is sent
the environment variable is restored
A separate test sends 200 requests inside another library's sampled span and checks that only about 1% are sent. With the root-context isolation removed, all 200 were sent and the test failed. The PR also adds tests for the authorization allow-list.
mocha tests/common/Telemetry.test.ts: 9 passing
npm run build and npm run lint pass
Not changed (follow-ups)
source: still the raw user-agent header, which can include an app-provided prefix. This behaviour predates 3.x.
url and responseCode: still empty or 0 on request records, as they have been since 3.x. The 3.x exporter only copies them for spans with an HTTP method attribute, and trackRequest only sets one when no name is given. Azurite always sets a name, so this needs a separate change.
After merge
Ingestion should drop once users update to the next release, not before. After that, check that cloud_RoleName == "Azurite_V1.0" and application_Version are set, and that daily volume is back under the cap.
applicationinsights 3.x is an OpenTelemetry shim that silently ignores
several settings Azurite relied on. Since 3.37.0, telemetry reported
unknown_service:node with no version, sent close to every request instead
of 1% (a default 5 traces/second limiter replaced the ratio), sent the raw
machine name plus host and OS details, and installed global OpenTelemetry
providers in the Azurite and VS Code extension host process.
- Use isolated TelemetryClient providers instead of setup()/start().
- Set service.name (Azurite_V1.0), service.version and a hashed
service.instance.id on the resource.
- Disable the per-batch _OTELRESOURCE_ metric.
- Track requests from the root context so other OpenTelemetry
instrumentation can't bypass sampling, and set microsoft.sample_rate
so itemCount keeps estimating total requests.
- Flush when the stop event is sent.
- Only report known authorization scheme names.
- Test through the real SDK with a local ingestion endpoint.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assert TraceRequest sample rate and internal property cleanup
tests/common/Telemetry.test.ts:182
This SDK-level test manually supplies microsoft.sample_rate, so it still passes if TraceRequest stops adding the attribute or adds the wrong value. The sampling test below calls TraceRequest but only checks the number of envelopes, not their sampleRate. Route a production TraceRequest through the capture client and assert its exported envelope has sampleRate === 1 and no leaked internal property.
- Preserve a pre-existing resource metric env var value in the test
instead of assuming it starts unset.
- Verify TraceRequest itself sets the 1% sample rate and doesn't leak it
as a custom property.
- Verify TraceStopEvent flushes both clients.
- Describe stop event delivery accurately in the changelog.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Re the Copilot finding "Assert TraceRequest sample rate and internal property cleanup" (tests/common/Telemetry.test.ts): fixed in 013b965. A new test sends a request through TraceRequest to the local ingestion server and checks the exported envelope has sampleRate === 1, with no microsoft.sample_rate or _MS.* custom property.
I also checked that each new test catches the bug it's meant to: with the wrong sample-rate attribute, the flush removed from TraceStopEvent, or the env var not restored, the matching test fails.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Since Azurite 3.37.0 (released 24 August 2026), Application Insights has been receiving far more Azurite telemetry, and it arrives as
unknown_service:nodewith no version. The cause is theapplicationinsightsupgrade from 2.9.6 to 3.x (#2660, then 3.16.0 in #2726).Version 3 is a compatibility layer over OpenTelemetry. It accepts several settings Azurite used without errors but ignores them. The existing unit tests called the helper functions directly rather than going through the SDK, so they didn't catch this.
What went wrong in 3.x
context.tags[cloudRole] = "Azurite_V1.0"cloud_RoleName=unknown_service:nodeand a strayai.cloud.rolecustom dimensionpackage.jsonapplication_Versionis blanksamplingPercentage = 1setup()/start()and the default global client use the distro's default 5 traces/second rate limiter, which replaces the 1% ratioQ_Messages_Dequeuepolling)addTelemetryProcessor(removeRoleInstance)cloud_RoleInstancesetup()/start()host.id,host.nameandos.versionare sent in a per-batch_OTELRESOURCE_metric. Live Metrics pings are sent. Global OpenTelemetry state is replaced inside the VS Code extension host.maxBatchSize = 0Changes (
src/common/Telemetry.ts)new TelemetryClient(connectionString, { useGlobalProviders: false })and stop callingsetup()/start(). Each client gets its own providers and its own sampling ratio, and nothing global is installed.service.name=Azurite_V1.0, which keeps existing queries and dashboards working.service.version= the Azurite version.service.instance.id= SHA-256 of the hostname, matching the previous privacy-review behaviour._OTELRESOURCE_metric. The environment variable is set only while the exporters are constructed, then restored.ROOT_CONTEXT, so another OpenTelemetry setup in the same process (for example another VS Code extension) can't make its sampled span the parent and bypass 1% sampling.microsoft.sample_rateon requests so the envelopesampleRateis 1 again.itemCountthen still estimates total requests, as it did with 2.x.authorizationproperty: Only report known scheme names (SharedKey,SharedKeyLite,Bearer). Anything else is reported asOther, so a malformed header can't leak a credential. Normal values are unchanged.@opentelemetry/apiand@opentelemetry/resourcesas direct dependencies. Both were already installed transitively at the same versions (1.9.1 and 2.10.0).Evidence
I ran the built Azurite against a local fake ingestion endpoint (only the Azure Monitor HTTPS calls were redirected), with real Blob, Queue and Table SDK traffic:
cloud_RoleNameunknown_service:C:\Program Files\nodejs\node.exeAzurite_V1.0application_Version3.37.0cloud_RoleInstance_OTELRESOURCE_metric (host.id,host.name,os.*)Custom dimensions are back to Azurite's own properties.
ai.cloud.roleand_MS.ProcessedByMetricExtractorsno longer appear.Tests
tests/common/Telemetry.test.tsnow sends telemetry through the realapplicationinsightsSDK to a local HTTPS ingestion server. It checks:sampleRateis 1_OTELRESOURCE_metric is sentA separate test sends 200 requests inside another library's sampled span and checks that only about 1% are sent. With the root-context isolation removed, all 200 were sent and the test failed. The PR also adds tests for the
authorizationallow-list.mocha tests/common/Telemetry.test.ts: 9 passingnpm run buildandnpm run lintpassNot changed (follow-ups)
source: still the rawuser-agentheader, which can include an app-provided prefix. This behaviour predates 3.x.urlandresponseCode: still empty or 0 on request records, as they have been since 3.x. The 3.x exporter only copies them for spans with an HTTP method attribute, andtrackRequestonly sets one when nonameis given. Azurite always sets a name, so this needs a separate change.After merge
Ingestion should drop once users update to the next release, not before. After that, check that
cloud_RoleName == "Azurite_V1.0"andapplication_Versionare set, and that daily volume is back under the cap.