Skip to content

Make Debug OTLP export opt-in via FSHARP_OTEL_EXPORT - #20468

Open
xperiandri wants to merge 1 commit into
dotnet:mainfrom
xperiandri:otel-export-opt-in
Open

Make Debug OTLP export opt-in via FSHARP_OTEL_EXPORT#20468
xperiandri wants to merge 1 commit into
dotnet:mainfrom
xperiandri:otel-export-opt-in

Conversation

@xperiandri

@xperiandri xperiandri commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Problem

FSharpPackage's Debug-only constructor unconditionally starts an OTLP trace/metric exporter against a hardcoded http://127.0.0.1:4317. When nothing is listening there — the common case when just debugging the extension, without Jaeger/Prometheus running in a container — this is silent noise and a stall:

  • the metric exporter retries against the closed port every second for the whole session;
  • Dispose calls tracerProvider.ForceFlush(5000), so closing the experimental VS instance blocks for up to 5 seconds every time.

There was no way to turn this off short of editing the source and rebuilding.

Fix

FSharpServiceTelemetry.otelExport now only starts the OTLP exporter when FSHARP_OTEL_EXPORT is set to an absolute http/https URI with a non-empty host. A value like localhost:4317 parses as an absolute URI too (scheme localhost, no host), so it's treated the same as an empty or unparseable value and falls back to the historical default endpoint rather than being handed to the exporter as-is.

The default VisualFSharpDebug launch profile explicitly clears FSHARP_OTEL_EXPORT (rather than just omitting it), so a first F5 never surprises anyone with OTEL noise or the shutdown stall — including if the variable happened to be set in the ambient shell environment devenv.exe inherits from. A new OTEL Export profile in the same launchSettings.json sets it to opt back in — pick it from the run-button dropdown, no rebuild needed.

DEVGUIDE.md documents the switch next to the existing F5 instructions, and the VS release notes get an entry.

Testing

  • dotnet fantomas --check on the changed .fs file.
  • The endpoint-selection logic (unset/empty → no export; 1/localhost:4317 → default endpoint; a real http/https URI → used as-is) was exercised standalone via dotnet fsi against a stand-in copy of the logic.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

❗ Release notes required

You can open this PR in browser to add release notes: open in github.dev


✅ Found changes and release notes in following paths:

Change path Release notes path Description
`vsintegration/src` docs/release-notes/.VisualStudio/18.vNext.md

@github-actions github-actions Bot added the AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files label Sep 6, 2026
@xperiandri

Copy link
Copy Markdown
Contributor Author

/azp run

WindowsCompressedMetadata transparent_compiler_release reports canceled, not a real failure: FSharp.Compiler.ComponentTests finished green at 10:55:46 (+8706/x0/?247), then the job produced no further log output for 74 minutes until AzDo force-canceled it on the overall job timeout. Unrelated to this PR's diff (vsintegration debug helper, launchSettings.json, docs only). Re-running.

@azure-pipelines

Copy link
Copy Markdown
Command 'run

`WindowsCompressedMetadata' is not supported by Azure Pipelines.



Supported commands

  • help:
    • Get descriptions, examples and documentation about supported commands
    • Example: help "command_name"
  • list:
    • List all pipelines for this repository using a comment.
    • Example: "list"
  • run:
    • Run all pipelines or specific pipelines for this repository using a comment. Use this command by itself to trigger all related pipelines, or specify specific pipelines to run.
    • Example: "run" or "run pipeline_name, pipeline_name, pipeline_name"
  • where:
    • Report back the Azure DevOps orgs that are related to this repository and org
    • Example: "where"

See additional documentation.

@xperiandri

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Commenter does not have sufficient privileges for PR 20468 in repo dotnet/fsharp

"FSharpPreferAnyCpuTools": "true",
"Fsc_NetFramework_ToolPath": "$(FSharpCompilerPathForDebuggingLocally)",
"Fsc_NetFramework_AnyCpu_ToolExe": "fscAnyCpu.exe",
"FSHARP_OTEL_EXPORT": "http://127.0.0.1:4317"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖🕵️⏱️🔥

{"FSHARP_OTEL_EXPORT": ""}

Default F5 must opt out. Use an explicit export profile and clear inherited values in No OTEL.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Flipped it: VisualFSharpDebug is now the default and explicitly clears FSHARP_OTEL_EXPORT (""), so an ambient value in the shell devenv.exe inherits from can't leak through either. Export moved to a new, explicitly-named OTEL Export profile.

| null
| "" -> ValueNone
| value ->
match Uri.TryCreate(value, UriKind.Absolute) with

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖🕵️

$env:FSHARP_OTEL_EXPORT = 'localhost:4317'

UriKind.Absolute accepts this. Require http/https and a non-empty host.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — confirmed Uri.TryCreate("localhost:4317", UriKind.Absolute) returns true with Scheme = "localhost" and an empty Host. Added a guard requiring http/https and a non-empty host; anything else (including this case) now falls back to the default endpoint instead of being handed to the exporter.

FSharpPackage unconditionally started an OTLP trace/metric exporter against
http://127.0.0.1:4317 in Debug builds. Without a collector listening there,
the metric exporter retries in the background for the whole session and
Dispose blocks on a 5s ForceFlush, so closing the experimental VS instance
stalls every time.

otelExport now only starts the exporter when FSHARP_OTEL_EXPORT is set to an
absolute http/https URI with a host - a value like "localhost:4317" parses as
an absolute URI too but has no host, so it is treated the same as an empty or
unparseable value and falls back to the default endpoint. The default
VisualFSharpDebug launch profile clears the variable so a first F5 never
surprises anyone with OTEL noise, even if it leaked in from an ambient shell
environment; the new "OTEL Export" profile sets it to opt back in.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

2 participants