Add usage telemetry for worker custom metrics and Azure Monitor diagnostic logging - #12034
Open
Rohit Ranjan (RohitRanjanMS) wants to merge 3 commits into
Open
Rohit Ranjan (RohitRanjanMS) wants to merge 3 commits into
Rohit Ranjan (RohitRanjanMS) wants to merge 3 commits into
Conversation
We had no way to tell whether worker custom metrics or the AzureMonitorDiagnosticLogger are actually used by customers, which made it risky to change or deprecate either. Neither emitted anything to the Kusto metrics table. Emit a metric event for each through the existing IMetricsLogger pipeline, following the pattern already used for OpenTelemetryOtlpEnabled: - host.azuremonitor.enabled: once per app init, from ScriptHost.PreInitialize - host.worker.custommetric: once per worker channel, on first custom metric The two features have opposite detectability, so one mechanism cannot cover both. Azure Monitor is decided at startup from configuration. Custom metrics are only observable at runtime, so they use a one-shot Interlocked flag placed before the _executingInvocations lookup, ensuring metrics that arrive outside an invocation (and are dropped) still register as usage. IsAzureMonitorEnabled() returns true when the AzureMonitor categories variable is absent, so it overcounts and cannot answer the usage question. Add IsAzureMonitorExplicitlyEnabled() alongside it, which requires an explicit category subscription. The original method is left untouched so the real provider registration gate is unaffected. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4b780f55-8b0d-4544-b99d-a07dab44ee80
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4b780f55-8b0d-4544-b99d-a07dab44ee80
Resolves a conflict in WorkerChannel.Log: dev changed the signature from Log(GrpcEvent) to Log(StreamingMessage), so rpcLog is now read from msg.RpcLog instead of msg.Message.RpcLog. Kept the custom metric usage check and adapted it to the new accessor. Updated the CreateRpcLogEvent test helper to return StreamingMessage to match. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4b780f55-8b0d-4544-b99d-a07dab44ee80
Rohit Ranjan (RohitRanjanMS)
marked this pull request as ready for review
September 21, 2026 17:51
Rohit Ranjan (RohitRanjanMS)
requested a review
from a team
as a code owner
September 21, 2026 17:51
Copilot started reviewing on behalf of
Rohit Ranjan (RohitRanjanMS)
September 21, 2026 17:51
View session
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Add ScriptHost initialization coverage and correct the release-note issue reference.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (1)
What changed in this PR
Adds telemetry for Azure Monitor diagnostic logging and worker custom metrics.
Changes:
- Detects explicit Azure Monitor subscriptions.
- Records first custom metric usage per worker channel.
- Adds tests and release notes.
| File | Summary |
|---|---|
test/WebJobs.Script.Tests/Workers/Rpc/GrpcWorkerChannelTests.cs |
Tests one-shot worker metric telemetry. |
test/WebJobs.Script.Tests/Extensions/EnvironmentExtensionsTests.cs |
Tests Azure Monitor detection. |
src/WebJobs.Script/Host/ScriptHost.cs |
Emits Azure Monitor usage telemetry. |
src/WebJobs.Script/Environment/EnvironmentExtensions.cs |
Adds explicit subscription detection. |
src/WebJobs.Script/Diagnostics/MetricEventNames.cs |
Defines telemetry event names. |
src/WebJobs.Script.Grpc/Channel/WorkerChannel.cs |
Tracks first custom metric per channel. |
release_notes.md |
Documents the telemetry change. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+479
to
+481
| if (_environment.IsAzureMonitorExplicitlyEnabled()) | ||
| { | ||
| _metricsLogger.LogEvent(MetricEventNames.AzureMonitorEnabled); |
Brett Samblanet (brettsam)
requested changes
Sep 24, 2026
|
|
||
| // Record that this app uses worker custom metrics. Emitted once per channel, before the | ||
| // invocation lookup below, so metrics arriving outside an invocation are still counted. | ||
| if (rpcLog.LogCategory == RpcLogCategory.CustomMetric && Interlocked.Exchange(ref _customMetricUsageLogged, 1) == 0) |
There was a problem hiding this comment.
minor improvement so we don't have to call Exchange every time on this hot path:
if (rpcLog.LogCategory == RpcLogCategory.CustomMetric &&
_customMetricUsageLogged == 0 &&
Interlocked.Exchange(ref _customMetricUsageLogged, 1) == 0)
{
_metricsLogger.LogEvent(MetricEventNames.WorkerCustomMetric);
}
A small benchmark shows this is more efficient after _customMetricLogged is set.
| Scenario | No tracking | Current condition | Read before exchange |
|---|---|---|---|
| One thread | 1.6 ns/op | 2.5 ns/op | 1.9 ns/op |
| Four threads sharing one flag | 0.4 ns/op | 18–19 ns/op | 0.5 ns/op |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.

Issue describing the changes in this PR
resolves #12033
Summary
We had no way to tell whether worker custom metrics or the
AzureMonitorDiagnosticLoggerare actually used by customers. Neither emitted anything to the Kusto metrics table.This adds one metric event per feature through the existing
IMetricsLogger→MetricsEventManagerpipeline, following the pattern already used byMetricEventNames.OpenTelemetryOtlpEnabled. These flush viaLogFunctionMetricEvent, so usage becomes queryable per app/subscription.host.azuremonitor.enabledScriptHost.PreInitialize()host.worker.custommetricWorkerChannel.Log()Why two different mechanisms
The two features have opposite detectability, so a single approach can't cover both:
Interlocked.Exchangeper channel.The custom-metric check is placed before the
_executingInvocationslookup inLog(). Metrics that arrive without a live invocation are dropped there, but they still represent a customer using the feature, so they should count.The Azure Monitor overcounting problem
IEnvironment.IsAzureMonitorEnabled()returnstruewhenWEBSITE_FUNCTIONS_AZUREMONITOR_CATEGORIESis absent, not only when a customer subscribed:Reusing it directly would sweep in every app where the platform never set the variable, which would not answer the question being asked. This PR adds
IsAzureMonitorExplicitlyEnabled()next to it, requiring a non-null value plus an actual category match:WEBSITE_FUNCTIONS_AZUREMONITOR_CATEGORIESIsAzureMonitorEnabledIsAzureMonitorExplicitlyEnabledtruefalseNonefalsefalseFoo,BarfalsefalseFunctionAppLogstruetrueFoo,FunctionAppLogs,BartruetrueIsAzureMonitorEnabled()is deliberately left untouched — it still gates realAzureMonitorDiagnosticLoggerProviderregistration inWebScriptHostBuilderExtension, and narrowing it would change actual logging behaviour rather than just telemetry.Interpreting the data
Both signals are presence indicators, not volume:
host.azuremonitor.enabledfires once per app init.host.worker.custommetricfires once per worker channel, so a multi-worker app aggregates toCount = N. TreatCountas worker count, not usage volume.Notes for reviewers
_customMetricUsageLoggedisintrather thanboolbecauseInterlocked.Exchangehas nobooloverload. This is the standard one-shot pattern, not an oversight.PreInitialize, andMetricsEventManager.WriteMetricEventsreadsAppServiceOptionsat flush time, so an event queued before specialization can be stamped with the specialized app name. The existingApplicationInsightsEnabled/ OpenTelemetry events already behave this way, so this is consistent — worth knowing when writing the Kusto query.Pull request checklist
IMPORTANT: Currently, changes must be backported to the
in-procbranch to be included in Core Tools and non-Flex deployments.in-procbranch is not requiredrelease_notes.mdAdditional information
Internal telemetry only — no customer-facing behaviour change.
Tests added:
EnvironmentExtensionsTests.IsAzureMonitorExplicitlyEnabled_ReturnsExpectedResult— 6-case theory covering unset / empty /None/ matching / non-matching / multi-category, mirroring the existingIsAzureMonitorEnabledtheory beside it.GrpcWorkerChannelTests.Log_CustomMetric_LogsUsageMetricOncePerChannel— verifies the one-shot guarantee across repeated custom metrics and that aUser-category log does not trigger it.Verified:
ScriptHostTests,GrpcWorkerChannelTests,EnvironmentExtensionsTests,UtilityTests— 566 passed, 0 failed.