Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DEVGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,8 @@ Then, use the **f5** or **ctrl+f5** keyboard shortcuts to test your tooling chan
> [!TIP]
> If the F# extension fails to load when you F5 (because the Roslyn version in the repo is ahead of the one in your Visual Studio), launch VS with `.\start-vs-VisualFSharpSln.ps1` instead of opening the solution directly. It builds the extension against the Roslyn your VS actually ships, so you don't need an internal/nightly VS or a local Roslyn build. Requires VS with Roslyn 5.10 or newer.

Debug builds export traces and metrics over OTLP only when `FSHARP_OTEL_EXPORT` is set, so the default `VisualFSharpDebug` profile stays quiet without a collector running. Pick the `OTEL Export` launch profile - it points `FSHARP_OTEL_EXPORT` at `http://127.0.0.1:4317` - to debug against Jaeger, Prometheus or another OTLP collector listening there.

Alternatively, you can do this entirely via the command line if you prefer that:

```shell
Expand Down
1 change: 1 addition & 0 deletions docs/release-notes/.VisualStudio/18.vNext.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
* Unused analyzers: disable in VS when file has errors ([PR #19892](https://github.com/dotnet/fsharp/pull/19892))
* Move to Roslyn's unified ExternalAccess library ([PR #20099](https://github.com/dotnet/fsharp/pull/20099))
* Remove trailing whitespace from source files. No functional change: whitespace inside string literals and inactive `#if` regions is preserved. ([PR #20355](https://github.com/dotnet/fsharp/pull/20355))
* The Debug build no longer exports OpenTelemetry unless `FSHARP_OTEL_EXPORT` asks it to (the variable can also carry the collector endpoint), so the default `VisualFSharpDebug` launch profile no longer stalls or retries against a missing collector; the new `OTEL Export` profile turns it back on. ([PR #20468](https://github.com/dotnet/fsharp/pull/20468))
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,23 @@
"FSharp_Shim_Present": "true",
"FSharpPreferAnyCpuTools": "true",
"Fsc_NetFramework_ToolPath": "$(FSharpCompilerPathForDebuggingLocally)",
"Fsc_NetFramework_AnyCpu_ToolExe": "fscAnyCpu.exe"
"Fsc_NetFramework_AnyCpu_ToolExe": "fscAnyCpu.exe",
"FSHARP_OTEL_EXPORT": ""
}
},
"OTEL Export": {
"commandName": "Executable",
"executablePath": "$(DevEnvDir)devenv.exe",
"commandLineArgs": "/rootsuffix $(VSSDKTargetPlatformRegRootSuffix) /log",
"environmentVariables": {
"FSharpCompilerPath": "$(FSharpCompilerPathForDebuggingLocally)",
"DisableAutoSetFscCompilerPath": "true",
"FSharpPreferNetFrameworkTools": "true",
"FSharp_Shim_Present": "true",
"FSharpPreferAnyCpuTools": "true",
"Fsc_NetFramework_ToolPath": "$(FSharpCompilerPathForDebuggingLocally)",
"Fsc_NetFramework_AnyCpu_ToolExe": "fscAnyCpu.exe",
"FSHARP_OTEL_EXPORT": "http://127.0.0.1:4317"
}
}
}
Expand Down
29 changes: 25 additions & 4 deletions vsintegration/src/FSharp.Editor/Common/DebugHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,26 @@ module FSharpServiceTelemetry =
open OpenTelemetry.Trace
open OpenTelemetry.Metrics

let otelExport () =
// On Windows forwarding localhost to wsl2 docker container sometimes does not work. Use IP address instead.
let otlpEndpoint = Uri("http://127.0.0.1:4317")

// Nothing listening on the endpoint means an exporter retrying in the background and a 5s flush
// on shutdown, so exporting is opt-in: FSHARP_OTEL_EXPORT enables it and can carry the endpoint.
let private otelEndpoint =
match Environment.GetEnvironmentVariable "FSHARP_OTEL_EXPORT" with
| 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.

| true, uri when
(uri.Scheme = Uri.UriSchemeHttp || uri.Scheme = Uri.UriSchemeHttps)
&& not (String.IsNullOrEmpty uri.Host)
->
ValueSome uri
// "localhost:4317" parses as an absolute URI too - scheme "localhost", no host - so an
// http(s) scheme and a host are required; anything else falls back to the default, same as
// an unparseable value. On Windows forwarding localhost to wsl2 docker sometimes does not
// work, so that default is an IP address rather than the name.
| _ -> ValueSome(Uri "http://127.0.0.1:4317")

let private startOtelExport (otlpEndpoint: Uri) =
let meterProvider =
// Configure OpenTelemetry metrics. Metrics can be viewed in Prometheus or other compatible tools.
OpenTelemetry.Sdk
Expand Down Expand Up @@ -170,5 +186,10 @@ module FSharpServiceTelemetry =
tracerProvider.Dispose()
meterProvider.Dispose()

let otelExport () =
match otelEndpoint with
| ValueNone -> ignore
| ValueSome endpoint -> startOtelExport endpoint

let listenToAll () = listen ""
#endif
Loading