diff --git a/DEVGUIDE.md b/DEVGUIDE.md index 11995f91f80..0af0c55e85c 100644 --- a/DEVGUIDE.md +++ b/DEVGUIDE.md @@ -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 diff --git a/docs/release-notes/.VisualStudio/18.vNext.md b/docs/release-notes/.VisualStudio/18.vNext.md index ba03f663967..8749505bd8d 100644 --- a/docs/release-notes/.VisualStudio/18.vNext.md +++ b/docs/release-notes/.VisualStudio/18.vNext.md @@ -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)) diff --git a/vsintegration/Vsix/VisualFSharpFull/Properties/launchSettings.json b/vsintegration/Vsix/VisualFSharpFull/Properties/launchSettings.json index 5731d86cdd6..7638dfe6832 100644 --- a/vsintegration/Vsix/VisualFSharpFull/Properties/launchSettings.json +++ b/vsintegration/Vsix/VisualFSharpFull/Properties/launchSettings.json @@ -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" } } } diff --git a/vsintegration/src/FSharp.Editor/Common/DebugHelpers.fs b/vsintegration/src/FSharp.Editor/Common/DebugHelpers.fs index fa8303b7e60..c0ec81b8e8a 100644 --- a/vsintegration/src/FSharp.Editor/Common/DebugHelpers.fs +++ b/vsintegration/src/FSharp.Editor/Common/DebugHelpers.fs @@ -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 + | 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 @@ -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