diff --git a/Directory.Build.props b/Directory.Build.props index 38d655380..511914bd1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,7 +4,7 @@ FsAutoComplete FsAutoComplete Apache-2.0 - 3186 + 3186,0042 true $(MSBuildThisFileDirectory)CHANGELOG.md diff --git a/README.md b/README.md index 1b39b3e9c..18d811474 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,41 @@ This repository is prepared to use Gitpod for a web-based VSCode-style IDE. Clic * push this commit and tag to main * the CI pipeline will publish a release from the tag. + +## OpenTelemetry + +FsAutocomplete is using [System.Diagnostics.Activity](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/distributed-tracing-instrumentation-walkthroughs) to create traces. + +To export traces, run [Jaeger](https://www.jaegertracing.io/) + +```bash +docker run -d --name jaeger \ + -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \ + -e COLLECTOR_OTLP_ENABLED=true \ + -p 6831:6831/udp \ + -p 6832:6832/udp \ + -p 5778:5778 \ + -p 16686:16686 \ + -p 4317:4317 \ + -p 4318:4318 \ + -p 14250:14250 \ + -p 14268:14268 \ + -p 14269:14269 \ + -p 9411:9411 \ + jaegertracing/all-in-one:latest +``` +Then configure your [environment](https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/) + +```bash +OTEL_EXPORTER_OTLP_ENDPOINT = "http://localhost:4317" +``` +Start FsAutocomplete (either by `code .` or `dotnet fsautocomplete`) + +Do some actions like opening documents, saving, getting tooltips, etc. + +Then open `http://localhost:16686/` to inspect traces. + + ## Communication protocol FsAutoComplete supports [LSP](https://microsoft.github.io/language-server-protocol/) as a communication protocol. diff --git a/paket.dependencies b/paket.dependencies index f5ce9d954..aa930516b 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -8,7 +8,9 @@ source https://api.nuget.org/v3/index.json #source: ./libs storage: none -github TheAngryByrd/FsLibLog:f81cba440bf0476bb4e2262b57a067a0d6ab78a7 src/FsLibLog/FsLibLog.fs + +github TheAngryByrd/FsLibLog:64f118ae8df2f2944ef69758052cb3b148b87e79 src/FsLibLog/FsLibLog.fs +github TheAngryByrd/FsOpenTelemetry src/FsOpenTelemetry/FsOpenTelemetry.fs nuget Fantomas.Client nuget FSharp.Compiler.Service @@ -52,6 +54,7 @@ nuget AltCover nuget GitHubActionsTestLogger nuget Ionide.LanguageServerProtocol nuget Microsoft.Extensions.Caching.Memory +nuget OpenTelemetry.Exporter.OpenTelemetryProtocol group Build source https://api.nuget.org/v3/index.json diff --git a/paket.lock b/paket.lock index 8115bfe4d..f820c92d7 100644 --- a/paket.lock +++ b/paket.lock @@ -35,9 +35,10 @@ NUGET FSharp.Core (>= 6.0.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) McMaster.NETCore.Plugins (>= 1.4) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) FSharp.Compiler.Service (43.7.200) - FSharp.Core (7.0) + FSharp.Core (7.0.200) System.Buffers (>= 4.5.1) System.Collections.Immutable (>= 6.0) + System.Diagnostics.DiagnosticSource (>= 6.0) System.Memory (>= 4.5.5) System.Reflection.Emit (>= 4.7) System.Reflection.Metadata (>= 6.0) @@ -45,10 +46,10 @@ NUGET FSharp.Control.AsyncSeq (3.2.1) FSharp.Core (>= 4.7.2) Microsoft.Bcl.AsyncInterfaces (>= 5.0) - FSharp.Control.Reactive (5.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) + FSharp.Control.Reactive (5.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) FSharp.Core (>= 4.7.2) System.Reactive (>= 5.0 < 6.0) - FSharp.Core (7.0) - content: none + FSharp.Core (7.0.200) - content: none FSharp.Data.Adaptive (1.2.13) FSharp.Core (>= 4.7) System.Reflection.Emit.Lightweight (>= 4.6) @@ -76,6 +77,21 @@ NUGET FSharp.Core (>= 4.7.2) GitHubActionsTestLogger (2.0.1) Microsoft.TestPlatform.ObjectModel (>= 17.2) + Google.Protobuf (3.22) + System.Memory (>= 4.5.3) - restriction: || (&& (== net6.0) (>= net45)) (&& (== net6.0) (< net5.0)) (&& (== net6.0) (< netstandard2.0)) (&& (== net7.0) (>= net45)) (&& (== net7.0) (< net5.0)) (&& (== net7.0) (< netstandard2.0)) (== netstandard2.0) + System.Runtime.CompilerServices.Unsafe (>= 4.5.2) - restriction: || (&& (== net6.0) (< net5.0)) (&& (== net7.0) (< net5.0)) (== netstandard2.0) + Grpc (2.46.6) - restriction: || (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net462)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + Grpc.Core (>= 2.46.6) + Grpc.Core (2.46.6) - restriction: || (&& (== net6.0) (>= net45)) (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net45)) (&& (== net7.0) (>= net462)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + Grpc.Core.Api (>= 2.46.6) + System.Memory (>= 4.5.3) + Grpc.Core.Api (2.51) + System.Memory (>= 4.5.3) + Grpc.Net.Client (2.51) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netstandard2.1)) + Grpc.Net.Common (>= 2.51) + Microsoft.Extensions.Logging.Abstractions (>= 3.0.3) + Grpc.Net.Common (2.51) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netstandard2.1)) + Grpc.Core.Api (>= 2.51) ICSharpCode.Decompiler (7.2.1.6856) Microsoft.Win32.Registry (>= 5.0) System.Collections.Immutable (>= 5.0) @@ -85,25 +101,25 @@ NUGET FSharp.Core (>= 6.0) Newtonsoft.Json (>= 13.0.1) StreamJsonRpc (>= 2.10.44) - Ionide.ProjInfo (0.61) + Ionide.ProjInfo (0.61.2) FSharp.Core (>= 6.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - Ionide.ProjInfo.Sln (>= 0.61) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) + Ionide.ProjInfo.Sln (>= 0.61.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) Microsoft.Build (>= 17.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) Microsoft.Build.Framework (>= 17.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) SemanticVersioning (>= 2.0.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - Ionide.ProjInfo.FCS (0.61) - FSharp.Compiler.Service (>= 41.0.5 < 42.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) + Ionide.ProjInfo.FCS (0.61.2) + FSharp.Compiler.Service (>= 41.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) FSharp.Core (>= 6.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - Ionide.ProjInfo (>= 0.61) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - Ionide.ProjInfo.ProjectSystem (0.61) - FSharp.Compiler.Service (>= 41.0.5 < 42.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) + Ionide.ProjInfo (>= 0.61.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) + Ionide.ProjInfo.ProjectSystem (0.61.2) + FSharp.Compiler.Service (>= 41.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) FSharp.Control.Reactive (>= 5.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) FSharp.Core (>= 6.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - Ionide.ProjInfo (>= 0.61) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - Ionide.ProjInfo.FCS (>= 0.61) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - Ionide.ProjInfo.Sln (>= 0.61) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) + Ionide.ProjInfo (>= 0.61.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) + Ionide.ProjInfo.FCS (>= 0.61.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) + Ionide.ProjInfo.Sln (>= 0.61.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) Newtonsoft.Json (>= 13.0.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - Ionide.ProjInfo.Sln (0.61) + Ionide.ProjInfo.Sln (0.61.2) McMaster.NETCore.Plugins (1.4) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) Microsoft.DotNet.PlatformAbstractions (>= 3.1.6) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) Microsoft.Extensions.DependencyModel (>= 5.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) @@ -165,6 +181,18 @@ NUGET Microsoft.Extensions.Logging.Abstractions (>= 6.0) Microsoft.Extensions.Options (>= 6.0) Microsoft.Extensions.Primitives (>= 6.0) + Microsoft.Extensions.Configuration (6.0.1) + Microsoft.Extensions.Configuration.Abstractions (>= 6.0) + Microsoft.Extensions.Primitives (>= 6.0) + Microsoft.Extensions.Configuration.Abstractions (6.0) + Microsoft.Extensions.Primitives (>= 6.0) + Microsoft.Extensions.Configuration.Binder (6.0) + Microsoft.Extensions.Configuration.Abstractions (>= 6.0) + Microsoft.Extensions.DependencyInjection (6.0.1) + Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0) + System.Runtime.CompilerServices.Unsafe (>= 6.0) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) Microsoft.Extensions.DependencyInjection.Abstractions (6.0) Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) @@ -174,13 +202,35 @@ NUGET System.Runtime.CompilerServices.Unsafe (>= 6.0) System.Text.Encodings.Web (>= 6.0) System.Text.Json (>= 6.0) + Microsoft.Extensions.Logging (6.0) + Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + Microsoft.Extensions.DependencyInjection (>= 6.0) + Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0) + Microsoft.Extensions.Logging.Abstractions (>= 6.0) + Microsoft.Extensions.Options (>= 6.0) + System.Diagnostics.DiagnosticSource (>= 6.0) Microsoft.Extensions.Logging.Abstractions (6.0.2) System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (== netstandard2.0) System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (== netstandard2.0) + Microsoft.Extensions.Logging.Configuration (6.0) + Microsoft.Extensions.Configuration (>= 6.0) + Microsoft.Extensions.Configuration.Abstractions (>= 6.0) + Microsoft.Extensions.Configuration.Binder (>= 6.0) + Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0) + Microsoft.Extensions.Logging (>= 6.0) + Microsoft.Extensions.Logging.Abstractions (>= 6.0) + Microsoft.Extensions.Options (>= 6.0) + Microsoft.Extensions.Options.ConfigurationExtensions (>= 6.0) Microsoft.Extensions.Options (6.0) Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0) Microsoft.Extensions.Primitives (>= 6.0) System.ComponentModel.Annotations (>= 5.0) - restriction: || (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + Microsoft.Extensions.Options.ConfigurationExtensions (6.0) + Microsoft.Extensions.Configuration.Abstractions (>= 6.0) + Microsoft.Extensions.Configuration.Binder (>= 6.0) + Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0) + Microsoft.Extensions.Options (>= 6.0) + Microsoft.Extensions.Primitives (>= 6.0) Microsoft.Extensions.Primitives (6.0) System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) System.Runtime.CompilerServices.Unsafe (>= 6.0) @@ -233,6 +283,20 @@ NUGET System.Runtime.CompilerServices.Unsafe (>= 5.0) Newtonsoft.Json (13.0.2) NuGet.Frameworks (6.3) - copy_local: false + OpenTelemetry (1.3.2) + Microsoft.Extensions.Logging (>= 3.1) + Microsoft.Extensions.Logging.Configuration (>= 3.1) + OpenTelemetry.Api (>= 1.3.2) + System.Collections.Immutable (>= 1.4) + System.Reflection.Emit.Lightweight (>= 4.7) + OpenTelemetry.Api (1.3.2) + System.Diagnostics.DiagnosticSource (>= 6.0 < 8.0) + System.Reflection.Emit.Lightweight (>= 4.7) + OpenTelemetry.Exporter.OpenTelemetryProtocol (1.3.2) + Google.Protobuf (>= 3.19.4 < 4.0) + Grpc (>= 2.44 < 3.0) - restriction: || (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net462)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + Grpc.Net.Client (>= 2.43 < 3.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netstandard2.1)) + OpenTelemetry (>= 1.3.2) SemanticVersioning (2.0.2) Serilog (2.11) Serilog.Sinks.Async (1.5) @@ -279,7 +343,7 @@ NUGET System.Numerics.Vectors (>= 4.4) - restriction: || (&& (== net6.0) (< netcoreapp2.0)) (&& (== net7.0) (< netcoreapp2.0)) (== netstandard2.0) System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (&& (== net7.0) (>= monotouch)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp2.0)) (&& (== net7.0) (< netcoreapp2.1)) (&& (== net7.0) (< netstandard1.1)) (&& (== net7.0) (< netstandard2.0)) (&& (== net7.0) (>= uap10.1)) (&& (== net7.0) (>= xamarinios)) (&& (== net7.0) (>= xamarinmac)) (&& (== net7.0) (>= xamarintvos)) (&& (== net7.0) (>= xamarinwatchos)) (== netstandard2.0) System.Numerics.Vectors (4.5) - restriction: || (&& (== net6.0) (< netcoreapp2.0)) (&& (== net7.0) (< netcoreapp2.0)) (== netstandard2.0) - System.Reactive (5.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) + System.Reactive (5.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) System.Reflection.Emit (4.7) System.Reflection.Emit.ILGeneration (>= 4.7) - restriction: || (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (&& (== net7.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard1.1)) (&& (== net7.0) (< netstandard2.0)) (&& (== net7.0) (>= uap10.1)) (== netstandard2.0) System.Reflection.Emit.ILGeneration (4.7) - restriction: || (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (&& (== net7.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard1.1)) (&& (== net7.0) (< netstandard2.0)) (&& (== net7.0) (>= uap10.1)) (== netstandard2.0) @@ -327,7 +391,9 @@ NUGET System.Collections.Immutable (>= 6.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) GITHUB remote: TheAngryByrd/FsLibLog - src/FsLibLog/FsLibLog.fs (f81cba440bf0476bb4e2262b57a067a0d6ab78a7) + src/FsLibLog/FsLibLog.fs (64f118ae8df2f2944ef69758052cb3b148b87e79) + remote: TheAngryByrd/FsOpenTelemetry + src/FsOpenTelemetry/FsOpenTelemetry.fs (fb24e8d4dc8ad14187b9b4631854fc8f17fb244d) GROUP Build STORAGE: NONE RESTRICTION: || (== net6.0) (== net7.0) diff --git a/src/FsAutoComplete.Core/Utils.fs b/src/FsAutoComplete.Core/Utils.fs index f0b410b7e..5699b192c 100644 --- a/src/FsAutoComplete.Core/Utils.fs +++ b/src/FsAutoComplete.Core/Utils.fs @@ -10,6 +10,14 @@ open FSharp.Compiler.CodeAnalysis open FSharp.UMX open FSharp.Compiler.Symbols + +/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. +let dispose (d: #IDisposable) = d.Dispose() + +/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources asynchronously. +/// A task that represents the asynchronous dispose operation. +let disposeAsync (d: #IAsyncDisposable) = d.DisposeAsync() + module Map = /// Combine two maps of identical types by starting with the first map and overlaying the second one. /// Because map updates shadow, any keys in the second map will have priority. @@ -148,6 +156,7 @@ let projectOptionsToParseOptions (checkOptions: FSharpProjectOptions) = { FSharpParsingOptions.Default with SourceFiles = files } + [] module Option = @@ -516,6 +525,9 @@ module List = [] [] module String = + /// Concatenates all the elements of a string array, using the specified separator between each element. + let inline join (separator: string) (items: string seq) = String.Join(separator, items) + let inline toCharArray (str: string) = str.ToCharArray() let lowerCaseFirstChar (str: string) = @@ -822,3 +834,81 @@ type FSharpSymbol with | :? FSharpStaticParameter as s -> s.DeclarationLocation | :? FSharpParameter as p -> p.DeclarationLocation | _ -> failwith $"cannot fetch DefinitionRange for unknown FSharpSymbol subtype {x.GetType().FullName}" + +module Tracing = + + open System.Diagnostics + open FsOpenTelemetry + open StreamJsonRpc + open System.Collections.Generic + + module SemanticConventions = + /// + /// From F# compiler + /// + module FCS = + [] + let fileName = "fileName" + + [] + let project = "project" + + [] + let qualifiedNameOfFile = "qualifiedNameOfFile" + + [] + let userOpName = "userOpName" + + [] + let fsac_sourceCodePath = "fsac.sourceCodePath" + + [] + let projectFilePath = "fsac.projectFilePath" + + [] + let serviceName = "FsAutoComplete" + + /// + /// From F# compiler + /// + [] + let fscServiceName = "fsc" + + let fsacActivitySource = new ActivitySource(serviceName, Version.info().Version) + + /// + /// StreamJsonRpcTracingStrategy participates in and propagates trace context in vs-streamjsonrpc + /// + /// + /// + /// vs-streamjsonrpc tracecontext documentation + /// + type StreamJsonRpcTracingStrategy(activitySource: ActivitySource) = + interface IActivityTracingStrategy with + member this.ApplyInboundActivity(request: Protocol.JsonRpcRequest) : IDisposable = + let tags = + [ "rpc.system", box "jsonrpc" + "rpc.jsonrpc.is_notification", box request.IsNotification + "rpc.jsonrpc.is_response_expected", box request.IsResponseExpected + "rpc.jsonrpc.version", box request.Version + "rpc.jsonrpc.request_id", box request.RequestId + "rpc.method", box request.Method ] + |> Seq.map KeyValuePair + + let activity = + activitySource.StartActivity(ActivityKind.Server, name = request.Method, tags = tags) + + if activity <> null then + activity.TraceStateString <- request.TraceState + + if request.TraceParent <> null then + activity.SetParentId(request.TraceParent) |> ignore + + activity + + member this.ApplyOutboundActivity(request: Protocol.JsonRpcRequest) : unit = + if Activity.Current <> null && Activity.Current.IdFormat = ActivityIdFormat.W3C then + request.TraceParent <- Activity.Current.Id + request.TraceState <- Activity.Current.TraceStateString + + () diff --git a/src/FsAutoComplete.Logging/FsAutoComplete.Logging.fsproj b/src/FsAutoComplete.Logging/FsAutoComplete.Logging.fsproj index a0fdf42e3..603d10d14 100644 --- a/src/FsAutoComplete.Logging/FsAutoComplete.Logging.fsproj +++ b/src/FsAutoComplete.Logging/FsAutoComplete.Logging.fsproj @@ -1,10 +1,14 @@  - netstandard2.0 + net6.0 false + + True + paket-files/FsOpenTelemetry.fs + True paket-files/FsLibLog.fs diff --git a/src/FsAutoComplete.Logging/paket.references b/src/FsAutoComplete.Logging/paket.references index 6252aa3a0..9e581c8fb 100644 --- a/src/FsAutoComplete.Logging/paket.references +++ b/src/FsAutoComplete.Logging/paket.references @@ -1,6 +1,7 @@ FSharp.Core File: FsLibLog.fs +File: FsOpenTelemetry.fs Microsoft.NETFramework.ReferenceAssemblies -Ionide.KeepAChangelog.Tasks \ No newline at end of file +Ionide.KeepAChangelog.Tasks diff --git a/src/FsAutoComplete/LspHelpers.fs b/src/FsAutoComplete/LspHelpers.fs index 2aa996b30..69a6b945c 100644 --- a/src/FsAutoComplete/LspHelpers.fs +++ b/src/FsAutoComplete/LspHelpers.fs @@ -600,6 +600,10 @@ type InlineValueDto = { Enabled: bool option Prefix: string option } +type NotificationsDto = + { Trace: bool option + TraceNamespaces: string array option } + type DebugDto = { DontCheckRelatedFiles: bool option CheckFileDebouncerTimeout: int option @@ -643,6 +647,7 @@ type FSharpConfigDto = CodeLenses: CodeLensConfigDto option PipelineHints: InlineValueDto option InlayHints: InlayHintDto option + Notifications: NotificationsDto option Debug: DebugDto option } type FSharpConfigRequest = { FSharp: FSharpConfigDto } @@ -673,6 +678,23 @@ type InlineValuesConfig = { Enabled = Some true Prefix = Some "//" } +type NotificationsConfig = + { Trace: bool + TraceNamespaces: string array } + + static member Default = + { Trace = false + TraceNamespaces = [||] } + + static member FromDto(dto: NotificationsDto) : NotificationsConfig = + { Trace = defaultArg dto.Trace NotificationsConfig.Default.Trace + TraceNamespaces = defaultArg dto.TraceNamespaces NotificationsConfig.Default.TraceNamespaces } + + + member this.AddDto(dto: NotificationsDto) : NotificationsConfig = + { Trace = defaultArg dto.Trace this.Trace + TraceNamespaces = defaultArg dto.TraceNamespaces this.TraceNamespaces } + type DebugConfig = { DontCheckRelatedFiles: bool CheckFileDebouncerTimeout: int @@ -722,6 +744,7 @@ type FSharpConfig = CodeLenses: CodeLensConfig InlayHints: InlayHintsConfig InlineValues: InlineValuesConfig + Notifications: NotificationsConfig Debug: DebugConfig } static member Default: FSharpConfig = @@ -761,6 +784,7 @@ type FSharpConfig = CodeLenses = CodeLensConfig.Default InlayHints = InlayHintsConfig.Default InlineValues = InlineValuesConfig.Default + Notifications = NotificationsConfig.Default Debug = DebugConfig.Default } static member FromDto(dto: FSharpConfigDto) : FSharpConfig = @@ -825,7 +849,10 @@ type FSharpConfig = | Some ivDto -> { Enabled = ivDto.Enabled |> Option.defaultValue true |> Some Prefix = ivDto.Prefix |> Option.defaultValue "//" |> Some } - + Notifications = + dto.Notifications + |> Option.map NotificationsConfig.FromDto + |> Option.defaultValue NotificationsConfig.Default Debug = match dto.Debug with | None -> DebugConfig.Default @@ -908,6 +935,10 @@ type FSharpConfig = InlineValues = { Enabled = defaultArg (dto.PipelineHints |> Option.map (fun n -> n.Enabled)) x.InlineValues.Enabled Prefix = defaultArg (dto.PipelineHints |> Option.map (fun n -> n.Prefix)) x.InlineValues.Prefix } + Notifications = + dto.Notifications + |> Option.map x.Notifications.AddDto + |> Option.defaultValue NotificationsConfig.Default Debug = match dto.Debug with | None -> DebugConfig.Default diff --git a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs index 176ad6cea..3756cc247 100644 --- a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs +++ b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs @@ -16,6 +16,8 @@ open Newtonsoft.Json.Linq open Ionide.ProjInfo.ProjectSystem open FsToolkit.ErrorHandling +open FsOpenTelemetry +open FsAutoComplete.Utils.Tracing open FSharp.UMX open FSharp.Compiler.Text @@ -176,6 +178,8 @@ type AdaptiveWorkspaceChosen = type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FSharpLspClient) = + let thisType = typeof + let disposables = new System.Reactive.Disposables.CompositeDisposable() let rootPath = cval None @@ -192,6 +196,16 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar /// in the future let selectProject projs = projs |> List.tryHead + let mutable traceNotifications: ProgressListener option = None + + let replaceTraceNotification shouldTrace traceNamespaces = + traceNotifications |> Option.iter dispose + + if shouldTrace then + traceNotifications <- Some(new ProgressListener(lspClient, traceNamespaces)) + else + traceNotifications <- None + let mutableConfigChanges = let toCompilerToolArgument (path: string) = sprintf "--compilertool:%s" path @@ -200,6 +214,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar and! checker = checker and! rootPath = rootPath + replaceTraceNotification config.Notifications.Trace config.Notifications.TraceNamespaces + checker.SetFSIAdditionalArguments [| yield! config.FSICompilerToolLocations |> Array.map toCompilerToolArgument yield! config.FSIExtraParameters |] @@ -303,7 +319,6 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let fileChecked = Event() - do disposables.Add <| fileParsed.Publish.Subscribe(fun (parseResults, proj, ct) -> @@ -821,8 +836,6 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar | (true, v) -> Some v | _ -> None - - let openFiles = cmap, cval> () let openFilesReadOnly = openFiles |> AMap.map (fun _ x -> x :> aval<_>) @@ -1131,102 +1144,102 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar } - static let semaphore = new SemaphoreSlim(1, 1) - let parseAndCheckFile (checker: FSharpCompilerServiceChecker) (file: VolatileFile) opts config = async { - try - logger.info ( - Log.setMessage "Getting typecheck results for {file} - {hash} - {date}" - >> Log.addContextDestructured "file" file.Lines.FileName - >> Log.addContextDestructured "hash" (file.Lines.GetHashCode()) - >> Log.addContextDestructured "date" (file.Touched) - ) + let tags = + [ SemanticConventions.fsac_sourceCodePath, box (UMX.untag file.Lines.FileName) + SemanticConventions.projectFilePath, box (opts.ProjectFileName) ] - let! ct = Async.CancellationToken - do! semaphore.WaitAsync(ct) |> Async.AwaitTask + use _ = fsacActivitySource.StartActivityForType(thisType, tags = tags) - use pgl = new ProgressListener(lspClient) - use progressReport = new ServerProgressReport(lspClient) - use _ = ct.Register(fun () -> pgl.Dispose()) + logger.info ( + Log.setMessage "Getting typecheck results for {file} - {hash} - {date}" + >> Log.addContextDestructured "file" file.Lines.FileName + >> Log.addContextDestructured "hash" (file.Lines.GetHashCode()) + >> Log.addContextDestructured "date" (file.Touched) + ) - let simpleName = Path.GetFileName(UMX.untag file.Lines.FileName) - do! progressReport.Begin($"Typechecking {simpleName}", message = $"{file.Lines.FileName}") + let! ct = Async.CancellationToken + use progressReport = new ServerProgressReport(lspClient) - // HACK: Insurance for a bug where FCS invalidates graph nodes incorrectly and seems to typecheck forever - use cts = new CancellationTokenSource() - cts.CancelAfter(TimeSpan.FromSeconds(60.)) + let simpleName = Path.GetFileName(UMX.untag file.Lines.FileName) + do! progressReport.Begin($"Typechecking {simpleName}", message = $"{file.Lines.FileName}") - let! result = - checker.ParseAndCheckFileInProject(file.Lines.FileName, (file.Lines.GetHashCode()), file.Lines, opts) - |> Async.withCancellation cts.Token - |> Debug.measureAsync $"checker.ParseAndCheckFileInProject - {file.Lines.FileName}" - do! progressReport.End($"Typechecked {file.Lines.FileName}") + // HACK: Insurance for a bug where FCS invalidates graph nodes incorrectly and seems to typecheck forever + use cts = new CancellationTokenSource() + cts.CancelAfter(TimeSpan.FromSeconds(60.)) - notifications.Trigger(NotificationEvent.FileParsed(file.Lines.FileName), ct) + let! result = + checker.ParseAndCheckFileInProject(file.Lines.FileName, (file.Lines.GetHashCode()), file.Lines, opts) + |> Async.withCancellation cts.Token + |> Debug.measureAsync $"checker.ParseAndCheckFileInProject - {file.Lines.FileName}" - match result with - | Error e -> - logger.info ( - Log.setMessage "Typecheck failed for {file} with {error}" - >> Log.addContextDestructured "file" file - >> Log.addContextDestructured "error" e - ) + do! progressReport.End($"Typechecked {file.Lines.FileName}") - return failwith e - | Ok parseAndCheck -> - logger.info ( - Log.setMessage "Typecheck completed successfully for {file}" - >> Log.addContextDestructured "file" file.Lines.FileName - ) + notifications.Trigger(NotificationEvent.FileParsed(file.Lines.FileName), ct) - Async.Start( - async { + match result with + | Error e -> + logger.info ( + Log.setMessage "Typecheck failed for {file} with {error}" + >> Log.addContextDestructured "file" file + >> Log.addContextDestructured "error" e + ) - fileParsed.Trigger(parseAndCheck.GetParseResults, opts, ct) - fileChecked.Trigger(parseAndCheck, file, ct) - let checkErrors = parseAndCheck.GetParseResults.Diagnostics - let parseErrors = parseAndCheck.GetCheckResults.Diagnostics + return failwith e + | Ok parseAndCheck -> + logger.info ( + Log.setMessage "Typecheck completed successfully for {file}" + >> Log.addContextDestructured "file" file.Lines.FileName + ) - let errors = - Array.append checkErrors parseErrors - |> Array.distinctBy (fun e -> - e.Severity, e.ErrorNumber, e.StartLine, e.StartColumn, e.EndLine, e.EndColumn, e.Message) + Async.Start( + async { - notifications.Trigger(NotificationEvent.ParseError(errors, file.Lines.FileName), ct) - }, - ct - ) + fileParsed.Trigger(parseAndCheck.GetParseResults, opts, ct) + fileChecked.Trigger(parseAndCheck, file, ct) + let checkErrors = parseAndCheck.GetParseResults.Diagnostics + let parseErrors = parseAndCheck.GetCheckResults.Diagnostics - Async.Start(analyzeFile config (file.Lines.FileName, file.Version, file.Lines, parseAndCheck), ct) + let errors = + Array.append checkErrors parseErrors + |> Array.distinctBy (fun e -> + e.Severity, e.ErrorNumber, e.StartLine, e.StartColumn, e.EndLine, e.EndColumn, e.Message) - return parseAndCheck - finally - try - semaphore.Release() |> ignore - with :? SemaphoreFullException -> - () + notifications.Trigger(NotificationEvent.ParseError(errors, file.Lines.FileName), ct) + }, + ct + ) + + Async.Start(analyzeFile config (file.Lines.FileName, file.Version, file.Lines, parseAndCheck), ct) + + return parseAndCheck } /// Bypass Adaptive checking and tell the checker to check a file - let bypassAdaptiveTypeCheck f opts = + let bypassAdaptiveTypeCheck (filePath: string) opts = async { + try - logger.info (Log.setMessage "Forced Check : {file}" >> Log.addContextDestructured "file" f) + logger.info ( + Log.setMessage "Forced Check : {file}" + >> Log.addContextDestructured "file" filePath + ) + let checker = checker |> AVal.force let config = config |> AVal.force - match forceFindOpenFileOrRead f with + match forceFindOpenFileOrRead filePath with | Ok(fileInfo) -> return! parseAndCheckFile checker fileInfo opts config |> Async.Ignore | _ -> () with e -> logger.warn ( Log.setMessage "Forced Check error : {file}" - >> Log.addContextDestructured "file" f + >> Log.addContextDestructured "file" filePath >> Log.addExn e ) } @@ -1315,12 +1328,12 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> AVal.force |> Result.ofOption (fun () -> $"No typecheck results for {filePath}") - let forceGetTypeCheckResults filePath = + let forceGetTypeCheckResults (filePath: string) = getTypeCheckResults (filePath) |> AVal.force |> Result.ofOption (fun () -> $"No typecheck results for {filePath}") - let forceGetTypeCheckResultsStale filePath = + let forceGetTypeCheckResultsStale (filePath: string) = aval { let! checker = checker @@ -1635,8 +1648,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar Seq.toList allDependents - let bypassAdaptiveAndCheckDepenenciesForFile filePath = + let bypassAdaptiveAndCheckDepenenciesForFile (filePath: string) = async { + let tags = [ SemanticConventions.fsac_sourceCodePath, box (UMX.untag filePath) ] + use _ = fsacActivitySource.StartActivityForType(thisType, tags = tags) let dependentFiles = getDependentFilesForFile filePath let dependentProjects = @@ -1661,7 +1676,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let checksToPerform = let innerChecks = Array.concat [| dependentFiles; dependentProjects |] - |> Array.filter (fun (_, file) -> file.Contains "AssemblyInfo.fs" |> not) + |> Array.filter (fun (_, file) -> + file.Contains "AssemblyInfo.fs" |> not + && file.Contains "AssemblyAttributes.fs" |> not) let checksToPerformLength = innerChecks.Length @@ -1696,7 +1713,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar percentage = percentage 0 checksToPerform.Length ) - do! Async.Parallel(checksToPerform, 2) |> Async.Ignore + let maxConcurrency = + Math.Max(1.0, Math.Floor((float System.Environment.ProcessorCount) * 0.75)) + + do! Async.Parallel(checksToPerform, int maxConcurrency) |> Async.Ignore } @@ -1713,9 +1733,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar getProjectOptionsForFile file |> AVal.force let getProjectOptionsForFsproj file = - loadedProjectOptions - |> AVal.force - |> Seq.tryFind (fun x -> x.ProjectFileName = file) + forceLoadProjects () |> Seq.tryFind (fun x -> x.ProjectFileName = file) let getDeclarationLocation (symUse, text) = @@ -1763,7 +1781,12 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar handleFormattedRange: (NamedText * string * FormatSelectionRange) -> TextEdit[] ) : Async>> = asyncResult { + let tags = [ SemanticConventions.fsac_sourceCodePath, box fileName ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try + + let! res = action () |> AsyncResult.ofStringErr let rootPath = rootPath |> AVal.force @@ -1893,6 +1916,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! LspResult.internalError "Fantomas install not found." | (FormatDocumentResponse.Error ex) -> return! LspResult.internalError ex with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore logger.error (Log.setMessage "HandleFormatting Request Errored {p}" >> Log.addExn e) return! LspResult.internalError (string e) } @@ -1905,6 +1929,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override _.Initialize(p: InitializeParams) = asyncResult { + let tags = [ "InitializeParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info (Log.setMessage "Initialize Request {p}" >> Log.addContextDestructured "p" p) @@ -1980,6 +2007,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar Capabilities = defaultSettings } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "Initialize Request Errored {p}" >> Log.addContextDestructured "p" p @@ -1991,6 +2020,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.Initialized(p: InitializedParams) = async { + let tags = [ "InitializedParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info (Log.setMessage "Initialized request {p}" >> Log.addContextDestructured "p" p) // Starts getting project options to cache @@ -1998,6 +2030,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return () with e -> + + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "Initialized Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2009,7 +2044,12 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.TextDocumentDidOpen(p: DidOpenTextDocumentParams) = async { + let tags = [ "InitializedParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try + trace.SetTagSafe("DidOpenTextDocumentParams", p) |> ignore + logger.info ( Log.setMessage "TextDocumentDidOpen Request: {parms}" >> Log.addContextDestructured "parms" p @@ -2027,6 +2067,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar forceGetTypeCheckResults filePath |> ignore return () with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentDidOpen Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2038,6 +2080,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.TextDocumentDidClose(p: DidCloseTextDocumentParams) = async { + let tags = [ "DidCloseTextDocumentParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentDidClose Request: {parms}" @@ -2049,6 +2094,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return () with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentDidClose Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2060,6 +2107,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.TextDocumentDidChange(p: DidChangeTextDocumentParams) = async { + let tags = [ "DidChangeTextDocumentParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentDidChange Request: {parms}" @@ -2076,6 +2126,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return () with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentDidChange Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2085,9 +2137,13 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return () } - override __.TextDocumentDidSave(p) = + override __.TextDocumentDidSave(p: DidSaveTextDocumentParams) = async { + let tags = [ "DidSaveTextDocumentParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try + logger.info ( Log.setMessage "TextDocumentDidSave Request: {parms}" >> Log.addContextDestructured "parms" p @@ -2121,6 +2177,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return () with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentDidSave Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2131,8 +2189,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar } override __.TextDocumentCompletion(p: CompletionParams) = - Debug.measureAsync "TextDocumentCompletion" - <| asyncResult { + asyncResult { + let tags = [ "CompletionParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentCompletion Request: {parms}" @@ -2251,6 +2311,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar success (Some completionList) with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentCompletion Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2324,8 +2386,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar CoreResponse.Res(HelpText.Full(sym, tip, n)) - Debug.measureAsync "CompletionItemResolve" - <| asyncResult { + + asyncResult { + let tags = [ "CompletionItem", box ci ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "CompletionItemResolve Request: {parms}" @@ -2342,6 +2407,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> success with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "CompletionItemResolve Request Errored {p}" >> Log.addContextDestructured "p" ci @@ -2353,6 +2420,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.TextDocumentSignatureHelp(p: SignatureHelpParams) = asyncResult { + let tags = [ "SignatureHelpParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentSignatureHelp Request: {parms}" @@ -2402,6 +2472,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! success (Some res) with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentSignatureHelp Request: {parms}" >> Log.addContextDestructured "parms" p @@ -2413,6 +2485,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.TextDocumentHover(p: TextDocumentPositionParams) = asyncResult { + let tags = [ "TextDocumentPositionParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentHover Request: {parms}" @@ -2480,9 +2555,12 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! LspResult.internalError $"No TryGetToolTipEnhanced results for {filePath}" | Error e -> + trace.RecordError(e, "TextDocumentHover.Error") |> ignore logger.error (Log.setMessage "Failed with {error}" >> Log.addContext "error" e) return! LspResult.internalError e with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentHover Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2494,6 +2572,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.TextDocumentRename(p: RenameParams) = asyncResult { + let tags = [ "RenameParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentRename Request: {parms}" @@ -2537,6 +2618,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let clientCapabilities = clientCapabilities |> AVal.force |> Option.get return WorkspaceEdit.Create(documentChanges, clientCapabilities) |> Some with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentRename Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2548,6 +2631,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.TextDocumentDefinition(p: TextDocumentPositionParams) = asyncResult { + let tags = [ "TextDocumentPositionParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentDefinition Request: {parms}" @@ -2562,6 +2648,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! decl = tyRes.TryFindDeclaration pos lineStr |> AsyncResult.ofStringErr return decl |> findDeclToLspLocation |> GotoResult.Single |> Some with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentDefinition Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2573,6 +2661,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.TextDocumentTypeDefinition(p: TextDocumentPositionParams) = asyncResult { + let tags = [ "TextDocumentPositionParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentTypeDefinition Request: {parms}" @@ -2587,6 +2678,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! decl = tyRes.TryFindTypeDeclaration pos lineStr |> AsyncResult.ofStringErr return decl |> findDeclToLspLocation |> GotoResult.Single |> Some with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentTypeDefinition Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2598,6 +2691,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.TextDocumentReferences(p: ReferenceParams) = asyncResult { + let tags = [ "ReferenceParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentReferences Request: {parms}" @@ -2627,6 +2723,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> Seq.toArray |> Some with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentReferences Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2636,8 +2734,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! LspResult.internalError (string e) } - override x.TextDocumentDocumentHighlight(p) = + override x.TextDocumentDocumentHighlight(p: TextDocumentPositionParams) = asyncResult { + let tags = [ "TextDocumentPositionParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentDocumentHighlight Request: {parms}" @@ -2658,6 +2759,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar Kind = None }) |> Some with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentDocumentHighlight Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2668,8 +2771,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar } - override x.TextDocumentImplementation(p) = + override x.TextDocumentImplementation(p: TextDocumentPositionParams) = asyncResult { + let tags = [ "TextDocumentPositionParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentImplementation Request: {parms}" @@ -2720,6 +2826,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar | [| single |] -> return Some(GotoResult.Single single) | multiple -> return Some(GotoResult.Multiple multiple) with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentImplementation Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2729,8 +2837,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! LspResult.internalError (string e) } - override __.TextDocumentDocumentSymbol(p) = + override __.TextDocumentDocumentSymbol(p: DocumentSymbolParams) = asyncResult { + let tags = [ "DocumentSymbolParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentDocumentSymbol Request: {parms}" @@ -2752,6 +2863,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> Some | None -> return! LspResult.internalError $"No declarations for {fn}" with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentDocumentSymbol Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2764,6 +2877,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.WorkspaceSymbol(symbolRequest: WorkspaceSymbolParams) = asyncResult { + let tags = [ "WorkspaceSymbolParams", box symbolRequest ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "WorkspaceSymbol Request: {parms}" @@ -2786,6 +2902,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return res with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "WorkspaceSymbol Request Errored {p}" >> Log.addContextDestructured "p" symbolRequest @@ -2797,6 +2915,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.TextDocumentFormatting(p: DocumentFormattingParams) = asyncResult { + let tags = [ "DocumentFormattingParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try let doc = p.TextDocument let fileName = doc.GetFilePath() |> Utils.normalizePath @@ -2823,6 +2944,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! x.HandleFormatting(fileName, action, handlerFormattedDoc, (fun (_, _, _) -> [||])) with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentFormatting Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2834,6 +2957,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.TextDocumentRangeFormatting(p: DocumentRangeFormattingParams) = asyncResult { + let tags = [ "DocumentRangeFormattingParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try let doc = p.TextDocument let fileName = doc.GetFilePath() |> Utils.normalizePath @@ -2871,6 +2997,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! x.HandleFormatting(fileName, action, (fun (_, _) -> [||]), handlerFormattedRangeDoc) with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentRangeFormatting Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2883,6 +3011,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.TextDocumentCodeAction(codeActionParams: CodeActionParams) = asyncResult { + let tags = [ "CodeActionParams", box codeActionParams ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentCodeAction Request: {parms}" @@ -2931,6 +3062,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> List.toArray |> Some with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentCodeAction Request Errored {p}" >> Log.addContextDestructured "p" codeActionParams @@ -2942,6 +3075,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.TextDocumentCodeLens(p: CodeLensParams) = asyncResult { + let tags = [ "CodeLensParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentCodeLens Request: {parms}" @@ -2964,6 +3100,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return Some res with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentCodeLens Request Errored {p}" >> Log.addContextDestructured "p" p @@ -2973,7 +3111,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! LspResult.internalError (string e) } - override __.CodeLensResolve(p) = + override __.CodeLensResolve(p: CodeLens) = // JB:TODO see how to reuse existing code logger.info ( Log.setMessage "CodeLensResolve Request: {parms}" @@ -2982,6 +3120,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let handler (f) (arg: CodeLens) : Async> = asyncResult { + + let tags = [ "CodeLens", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + let pos = protocolPosToPos arg.Range.Start let data = arg.Data.Value.ToObject() @@ -3027,6 +3169,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return codeLens with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "CodeLensResolve - Operation failed on {file}" >> Log.addContextDestructured "file" filePath @@ -3139,8 +3283,12 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar }) p - override __.WorkspaceDidChangeWatchedFiles(p) = + override __.WorkspaceDidChangeWatchedFiles(p: DidChangeWatchedFilesParams) = async { + + let tags = [ "DidChangeWatchedFilesParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "WorkspaceDidChangeWatchedFiles Request: {parms}" @@ -3154,6 +3302,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ()) with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "WorkspaceDidChangeWatchedFiles Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3163,6 +3313,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.WorkspaceDidChangeConfiguration(p: DidChangeConfigurationParams) = async { + let tags = [ "DidChangeConfigurationParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "WorkspaceDidChangeConfiguration Request: {parms}" @@ -3175,6 +3328,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar updateConfig c with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "WorkspaceDidChangeConfiguration Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3184,6 +3339,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.TextDocumentFoldingRange(rangeP: FoldingRangeParams) = asyncResult { + let tags = [ "FoldingRangeParams", box rangeP ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentFoldingRange Request: {parms}" @@ -3202,6 +3360,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! scopes = Commands.scopesForFile getParseResultsForFile file |> AsyncResult.ofStringErr return scopes |> Seq.map Structure.toFoldingRange |> Set.ofSeq |> List.ofSeq |> Some with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentFoldingRange Request Errored {p}" >> Log.addContextDestructured "p" rangeP @@ -3213,6 +3373,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.TextDocumentSelectionRange(selectionRangeP: SelectionRangeParams) = asyncResult { + let tags = [ "SelectionRangeParams", box selectionRangeP ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentSelectionRange Request: {parms}" @@ -3243,6 +3406,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return ranges |> List.choose mkSelectionRanges |> Some with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentSelectionRange Request Errored {p}" >> Log.addContextDestructured "p" selectionRangeP @@ -3254,6 +3419,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.TextDocumentSemanticTokensFull(p: SemanticTokensParams) : AsyncLspResult = asyncResult { + let tags = [ "SemanticTokensParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentSemanticTokensFull request: {parms}" @@ -3264,6 +3432,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! x.handleSemanticTokens fn None with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentSemanticTokensFull Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3277,6 +3447,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.TextDocumentSemanticTokensRange(p: SemanticTokensRangeParams) : AsyncLspResult = asyncResult { + let tags = [ "SemanticTokensRangeParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentSemanticTokensRange request: {parms}" @@ -3287,6 +3460,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let fcsRange = protocolRangeToRange (UMX.untag fn) p.Range return! x.handleSemanticTokens fn (Some fcsRange) with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentSemanticTokensRange Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3298,6 +3473,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.TextDocumentInlayHint(p: InlayHintParams) : AsyncLspResult = asyncResult { + let tags = [ "InlayHintParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentInlayHint Request: {parms}" @@ -3387,6 +3565,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return (Some hints) with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentInlayHint Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3396,8 +3576,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! LspResult.internalError (string e) } - override x.TextDocumentInlineValue(p) = + override x.TextDocumentInlineValue(p: InlineValueParams) = asyncResult { + let tags = [ "InlineValueParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "TextDocumentInlineValue Request: {parms}" @@ -3424,6 +3607,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return hints with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "TextDocumentInlineValue Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3604,6 +3789,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.FSharpSignature(p: TextDocumentPositionParams) = asyncResult { + + let tags = [ "TextDocumentPositionParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FSharpSignature Request: {parms}" @@ -3619,6 +3808,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return { Content = CommandResponse.typeSig FsAutoComplete.JsonSerializer.writeJson tip } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpSignature Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3630,6 +3821,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.FSharpSignatureData(p: TextDocumentPositionParams) = asyncResult { + let tags = [ "TextDocumentPositionParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FSharpSignatureData Request: {parms}" @@ -3650,6 +3844,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar { Content = CommandResponse.signatureData FsAutoComplete.JsonSerializer.writeJson (typ, parms, generics) } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpSignatureData Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3662,6 +3858,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.FSharpDocumentationGenerator(p: OptionallyVersionedTextDocumentPositionParams) = asyncResult { + let tags = [ "OptionallyVersionedTextDocumentPositionParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FSharpDocumentationGenerator Request: {parms}" @@ -3694,6 +3893,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return () with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpDocumentationGenerator Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3703,8 +3904,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! LspResult.internalError (string e) } - override __.FSharpLineLense(p) = + override __.FSharpLineLense(p: ProjectParms) = asyncResult { + let tags = [ "ProjectParms", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FSharpLineLense Request: {parms}" @@ -3721,6 +3925,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return { Content = CommandResponse.declarations FsAutoComplete.JsonSerializer.writeJson decls } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpLineLense Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3730,8 +3936,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! LspResult.internalError (string e) } - override __.FSharpCompilerLocation(p) = + override __.FSharpCompilerLocation(p: obj) = asyncResult { + use trace = fsacActivitySource.StartActivityForType(thisType) + try logger.info ( Log.setMessage "FSharpCompilerLocation Request: {parms}" @@ -3750,6 +3958,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar msbuild (sdk |> Option.map (fun (di: DirectoryInfo) -> di.FullName)) } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpCompilerLocation Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3761,6 +3971,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.FSharpWorkspaceLoad(p: WorkspaceLoadParms) = asyncResult { + let tags = [ "WorkspaceLoadParms", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FSharpWorkspaceLoad Request: {parms}" @@ -3778,6 +3991,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return { Content = CommandResponse.workspaceLoad FsAutoComplete.JsonSerializer.writeJson true } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpWorkspaceLoad Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3791,6 +4006,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.FSharpWorkspacePeek(p: WorkspacePeekRequest) = asyncResult { + let tags = [ "WorkspacePeekRequest", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FSharpWorkspacePeek Request: {parms}" @@ -3811,6 +4029,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! res with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpWorkspacePeek Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3820,8 +4040,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! LspResult.internalError (string e) } - override __.FSharpProject(p) = + override __.FSharpProject(p: ProjectParms) = asyncResult { + let tags = [ "ProjectParms", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FSharpProject Request: {parms}" @@ -3850,6 +4073,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! Helpers.notImplemented with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpWorkspacePeek Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3870,6 +4095,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.FSharpDotnetNewList(p: DotnetNewListRequest) = asyncResult { + let tags = [ "DotnetNewListRequest", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FSharpDotnetNewList Request: {parms}" @@ -3879,6 +4107,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! funcs = Commands.DotnetNewList() |> AsyncResult.ofCoreResponse return { Content = CommandResponse.dotnetnewlist FsAutoComplete.JsonSerializer.writeJson funcs } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpDotnetNewList Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3890,6 +4120,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.FSharpDotnetNewRun(p: DotnetNewRunRequest) = asyncResult { + let tags = [ "DotnetNewRunRequest", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FSharpDotnetNewRun Request: {parms}" @@ -3902,6 +4135,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return { Content = "" } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpDotnetNewRun Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3913,6 +4148,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.FSharpDotnetAddProject(p: DotnetProjectRequest) = asyncResult { + let tags = [ "DotnetProjectRequest", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FSharpDotnetAddProject Request: {parms}" @@ -3923,6 +4161,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar forceLoadProjects () |> ignore return { Content = "" } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpDotnetAddProject Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3934,6 +4174,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.FSharpDotnetRemoveProject(p: DotnetProjectRequest) = asyncResult { + let tags = [ "DotnetProjectRequest", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FSharpDotnetRemoveProject Request: {parms}" @@ -3944,6 +4187,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar forceLoadProjects () |> ignore return { Content = "" } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpDotnetRemoveProject Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3955,6 +4200,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.FSharpDotnetSlnAdd(p: DotnetProjectRequest) = asyncResult { + let tags = [ "DotnetProjectRequest", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FSharpDotnetSlnAdd Request: {parms}" @@ -3965,6 +4213,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar forceLoadProjects () |> ignore return { Content = "" } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpDotnetSlnAdd Request Errored {p}" >> Log.addContextDestructured "p" p @@ -3976,6 +4226,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.FSharpHelp(p: TextDocumentPositionParams) = asyncResult { + let tags = [ "TextDocumentPositionParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FSharpHelp Request: {parms}" @@ -3989,6 +4242,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! t = Commands.Help tyRes pos lineStr |> Result.ofCoreResponse return { Content = CommandResponse.help FsAutoComplete.JsonSerializer.writeJson t } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpHelp Request Errored {p}" >> Log.addContextDestructured "p" p @@ -4000,6 +4255,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.FSharpDocumentation(p: TextDocumentPositionParams) = asyncResult { + let tags = [ "TextDocumentPositionParams", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FSharpDocumentation Request: {parms}" @@ -4014,6 +4272,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! t = Commands.FormattedDocumentation tyRes pos lineStr |> Result.ofCoreResponse return { Content = CommandResponse.formattedDocumentation FsAutoComplete.JsonSerializer.writeJson t } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpDocumentation Request Errored {p}" >> Log.addContextDestructured "p" p @@ -4025,6 +4285,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override x.FSharpDocumentationSymbol(p: DocumentationForSymbolReuqest) = asyncResult { + let tags = [ "DocumentationForSymbolReuqest", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FSharpDocumentationSymbol Request: {parms}" @@ -4055,6 +4318,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar xmldoc (signature, footer, cn) } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpDocumentationSymbol Request Errored {p}" >> Log.addContextDestructured "p" p @@ -4065,15 +4330,34 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar } override __.LoadAnalyzers(path) = - logger.info ( - Log.setMessage "LoadAnalyzers Request: {parms}" - >> Log.addContextDestructured "parms" path - ) + async { - Helpers.notImplemented + use trace = fsacActivitySource.StartActivityForType(thisType) + + logger.info ( + Log.setMessage "LoadAnalyzers Request: {parms}" + >> Log.addContextDestructured "parms" path + ) + + try + // since the analyzer state handling code is in `updateConfig`, re-trigger it here + transact + <| fun () -> + config.MarkOutdated() + updateConfig config.Value + + return LspResult.success () + with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + Loggers.analyzers.error (Log.setMessage "Loading failed" >> Log.addExn e) + return LspResult.success () + } override x.FSharpPipelineHints(p: FSharpPipelineHintRequest) = asyncResult { + let tags = [ "FSharpPipelineHintRequest", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FSharpPipelineHints Request: {parms}" @@ -4086,6 +4370,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return { Content = CommandResponse.pipelineHint FsAutoComplete.JsonSerializer.writeJson res } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FSharpPipelineHints Request Errored {p}" >> Log.addContextDestructured "p" p @@ -4097,6 +4383,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.FsProjMoveFileUp(p: DotnetFileRequest) = asyncResult { + let tags = [ "DotnetFileRequest", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FsProjMoveFileUp Request: {parms}" @@ -4110,6 +4399,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar forceLoadProjects () |> ignore return { Content = "" } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FsProjMoveFileUp Request Errored {p}" >> Log.addContextDestructured "p" p @@ -4122,6 +4413,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.FsProjMoveFileDown(p: DotnetFileRequest) = asyncResult { + let tags = [ "DotnetFileRequest", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FsProjMoveFileDown Request: {parms}" @@ -4135,6 +4429,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar forceLoadProjects () |> ignore return { Content = "" } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FsProjMoveFileDown Request Errored {p}" >> Log.addContextDestructured "p" p @@ -4147,6 +4443,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.FsProjAddFileAbove(p: DotnetFile2Request) = asyncResult { + let tags = [ "DotnetFile2Request", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FsProjAddFileAbove Request: {parms}" @@ -4160,6 +4459,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar forceLoadProjects () |> ignore return { Content = "" } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FsProjAddFileAbove Request Errored {p}" >> Log.addContextDestructured "p" p @@ -4171,6 +4472,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.FsProjAddFileBelow(p: DotnetFile2Request) = asyncResult { + let tags = [ "DotnetFile2Request", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FsProjAddFileBelow Request: {parms}" @@ -4184,6 +4488,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar forceLoadProjects () |> ignore return { Content = "" } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FsProjAddFileBelow Request Errored {p}" >> Log.addContextDestructured "p" p @@ -4196,6 +4502,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.FsProjAddFile(p: DotnetFileRequest) = asyncResult { + let tags = [ "DotnetFileRequest", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FsProjAddFile Request: {parms}" @@ -4206,6 +4515,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar forceLoadProjects () |> ignore return { Content = "" } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FsProjAddFile Request Errored {p}" >> Log.addContextDestructured "p" p @@ -4217,6 +4528,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override _.FsProjRemoveFile(p: DotnetFileRequest) = asyncResult { + let tags = [ "DotnetFileRequest", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FsProjRemoveFile Request: {parms}" @@ -4231,6 +4545,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return { Content = "" } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FsProjRemoveFile Request Errored {p}" >> Log.addContextDestructured "p" p @@ -4242,6 +4558,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override _.FsProjAddExistingFile(p: DotnetFileRequest) = asyncResult { + let tags = [ "DotnetFileRequest", box p ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + try logger.info ( Log.setMessage "FsProjAddExistingFile Request: {parms}" @@ -4255,6 +4574,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar forceLoadProjects () |> ignore return { Content = "" } with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + logger.error ( Log.setMessage "FsProjAddExistingFile Request Errored {p}" >> Log.addContextDestructured "p" p @@ -4268,10 +4589,24 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar member this.WorkDoneProgessCancel(token: ProgressToken) : Async = async { - logger.info ( - Log.setMessage "WorkDoneProgessCancel Request: {parms}" - >> Log.addContextDestructured "parms" token - ) + + let tags = [ "ProgressToken", box token ] + use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) + + try + logger.info ( + Log.setMessage "WorkDoneProgessCancel Request: {parms}" + >> Log.addContextDestructured "parms" token + ) + + with e -> + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + + logger.error ( + Log.setMessage "WorkDoneProgessCancel Request Errored {p}" + >> Log.addContextDestructured "token" token + >> Log.addExn e + ) return () } @@ -4294,7 +4629,9 @@ module AdaptiveFSharpLspServer = None | _ -> None - { new JsonRpc(handler) with + let strategy = StreamJsonRpcTracingStrategy(Tracing.fsacActivitySource) + + { new JsonRpc(handler, ActivityTracingStrategy = strategy) with member this.IsFatalException(ex: Exception) = match ex with | HandleableException -> false diff --git a/src/FsAutoComplete/LspServers/FSharpLspClient.fs b/src/FsAutoComplete/LspServers/FSharpLspClient.fs index 919b28fd3..2bebff8bb 100644 --- a/src/FsAutoComplete/LspServers/FSharpLspClient.fs +++ b/src/FsAutoComplete/LspServers/FSharpLspClient.fs @@ -8,6 +8,7 @@ open Ionide.LanguageServerProtocol.Types open FsAutoComplete.LspHelpers open System open System.Threading.Tasks +open FsAutoComplete.Utils type FSharpLspClient(sendServerNotification: ClientNotificationSender, sendServerRequest: ClientRequestSender) = @@ -143,74 +144,157 @@ type ServerProgressReport(lspClient: FSharpLspClient, ?token: ProgressToken) = open System.Diagnostics.Tracing open System.Collections.Concurrent +open System.Diagnostics +open Ionide.ProjInfo.Logging + + +/// listener for the the events generated from the fsc ActivitySource +type ProgressListener(lspClient: FSharpLspClient, traceNamespace: string array) = + + let isOneOf list string = + list |> Array.exists (fun f -> f string) + + let strEquals (other: string) (this: string) = + this.Equals(other, StringComparison.InvariantCultureIgnoreCase) + + let strContains (substring: string) (str: string) = str.Contains(substring) + + let interestingActivities = traceNamespace |> Array.map strContains + + let logger = LogProvider.getLoggerByName "Compiler" -/// listener for the the events generated by the `FSharp.Compiler.FSharpCompilerEventSource` -type ProgressListener(lspClient) = - inherit EventListener() let mutable isDisposed = false - let tryDispose (d: #IDisposable) = - try - d.Dispose() - with _ -> - () - - let mutable source = null - - let mutable inflightEvents = ConcurrentDictionary<_, ServerProgressReport>() - - member _.EnableEvents(eventSource, level, matchAnyKeyword) = - ``base``.EnableEvents(eventSource, level, matchAnyKeyword) - - override x.OnEventSourceCreated newSource = - lock x - <| fun () -> - if newSource.Name = "FSharpCompiler" then - x.EnableEvents(newSource, EventLevel.LogAlways, EventKeywords.All) - source <- newSource - - override x.OnEventWritten eventArgs = - - lock x - <| fun () -> - try - if isDisposed then - () - else - let message = - match eventArgs.EventId with - | 5 -> - let progressReport = new ServerProgressReport(lspClient) - let message = eventArgs.Payload.[0] |> string - let fileName = IO.Path.GetFileName message - - if inflightEvents.TryAdd(eventArgs.Task, progressReport) then - progressReport.Begin($"Dependent Typecheck {fileName}", message = message) - |> Async.Start - | 6 -> - let message = eventArgs.Payload.[0] |> string - - match inflightEvents.TryRemove(eventArgs.Task) with - | true, report -> - report.End($"Finished {message}") |> Async.Start - tryDispose report - () - | false, _ -> - - () - | other -> () - - message - with e -> - () - - member _.DisableEvents(source) = ``base``.DisableEvents(source) - - interface System.IDisposable with - member this.Dispose() = - lock this - <| fun () -> - if isNull source then () else this.DisableEvents(source) - isDisposed <- true - inflightEvents.Values |> Seq.iter (tryDispose) - inflightEvents <- null + let inflightEvents = + ConcurrentDictionary<_, System.Diagnostics.Activity * ServerProgressReport>() + + let isStopped (activity: Activity) = +#if NET7_0_OR_GREATER + activity.IsStopped + || +#endif + // giving this 2 seconds to report something, otherwise assume it's a dead activity + ((DateTime.UtcNow - activity.StartTimeUtc) > TimeSpan.FromSeconds(2.) + && activity.Duration = TimeSpan.Zero) + + let getTagItemSafe key (a: Activity) = a.GetTagItem key |> Option.ofObj + + let getFileName = + getTagItemSafe Tracing.SemanticConventions.FCS.fileName + >> Option.map string + >> Option.map IO.Path.GetFileName + >> Option.defaultValue String.Empty + + let getProject = + getTagItemSafe Tracing.SemanticConventions.FCS.project + >> Option.map string + >> Option.map IO.Path.GetFileName + >> Option.defaultValue String.Empty + + let getUserOpName = + getTagItemSafe Tracing.SemanticConventions.FCS.userOpName + >> Option.map string + >> Option.defaultValue String.Empty + + let mbp = + MailboxProcessor.Start(fun inbox -> + async { + while not isDisposed do + for (a, p) in inflightEvents.Values do + // We don't get always get a corresponding ActivityStopped for a given Activity event so this looks at anything that hasn't had a duration incresed at all + // We don't seem to get a corresponding ActivityStopped on cancelled typechecks. + if isStopped a then + do! p.End() + inflightEvents.TryRemove(a.Id) |> ignore + else + // FSC doesn't start their spans with tags so we have to see if it's been added later https://github.com/dotnet/fsharp/issues/14776 + let message = + [ getFileName a; getProject a; getUserOpName a ] + |> List.filter (String.IsNullOrEmpty >> not) + |> String.join " - " + + do! p.Report(message = message) + + match! inbox.TryReceive(250) with + | None -> + // if we don't get an event in 250 ms just loop again so we can analyze inflightEvents + () + | Some(action, activity: Activity, reply: AsyncReplyChannel) -> + + match action with + | "start" -> + let fileName = getFileName activity + let userOpName = getUserOpName activity + + logger.trace ( + Log.setMessageI + $"Started : {activity.DisplayName:DisplayName} - {userOpName:UserOpName} - {fileName:fileName}" + ) + + if + activity.DisplayName |> isOneOf interestingActivities + && not (isStopped activity) + then + let progressReport = new ServerProgressReport(lspClient) + + if inflightEvents.TryAdd(activity.Id, (activity, progressReport)) then + + do! progressReport.Begin($"{activity.DisplayName}") + + | "stop" -> + let fileName = getFileName activity + let userOpName = getUserOpName activity + let duration = activity.Duration.ToString() + + logger.trace ( + Log.setMessageI + $"Finished : {activity.DisplayName:DisplayName} - {userOpName:UserOpName} - {fileName:fileName} - took {duration:duration}" + ) + + if activity.DisplayName |> isOneOf interestingActivities then + match inflightEvents.TryRemove(activity.Id) with + | true, (old, progressReport) -> do! progressReport.End() + | _ -> () + + | _ -> () + + reply.Reply() + }) + + + let shouldListenTo (act: ActivitySource) = act.Name = Tracing.fscServiceName + + let activityStarted (act: Activity) = + mbp.PostAndReply(fun reply -> "start", act, reply) + + let activityStopped (act: Activity) = + mbp.PostAndReply(fun reply -> "stop", act, reply) + + let listener = + new ActivityListener( + ShouldListenTo = shouldListenTo, + Sample = (fun _ -> ActivitySamplingResult.AllDataAndRecorded), + ActivityStarted = activityStarted, + ActivityStopped = activityStopped + ) + + do ActivitySource.AddActivityListener listener + + interface IDisposable with + member this.Dispose() : unit = + (this :> IAsyncDisposable).DisposeAsync().GetAwaiter().GetResult() + + interface IAsyncDisposable with + member this.DisposeAsync() : ValueTask = + // was getting a compile error for the statemachine in CI to `task` + async { + if not isDisposed then + isDisposed <- true + dispose listener + + for (a, p) in inflightEvents.Values do + do! (disposeAsync p).AsTask() |> Async.AwaitTask + inflightEvents.TryRemove(a.Id) |> ignore + } + |> Async.StartImmediateAsTask + |> ValueTask diff --git a/src/FsAutoComplete/Parser.fs b/src/FsAutoComplete/Parser.fs index 4739e4b38..655a3a466 100644 --- a/src/FsAutoComplete/Parser.fs +++ b/src/FsAutoComplete/Parser.fs @@ -12,9 +12,17 @@ open Serilog.Filters open System.Runtime.InteropServices open System.Threading.Tasks open FsAutoComplete.Lsp +open OpenTelemetry +open OpenTelemetry.Resources +open OpenTelemetry.Trace module Parser = open FsAutoComplete.Core + open System.Diagnostics + + let mutable tracerProvider = Unchecked.defaultof<_> + + [] type Pos = { Line: int; Column: int } @@ -89,6 +97,12 @@ module Parser = "Enable LSP Server based on FSharp.Data.Adaptive. Should be more stable, but is experimental." ) + let otelTracingOption = + Option( + "--otel-exporter-enabled", + "Enabled OpenTelemetry exporter. See https://opentelemetry.io/docs/reference/specification/protocol/exporter/ for environment variables to configure for the exporter." + ) + let stateLocationOption = Option( "--state-directory", @@ -109,6 +123,7 @@ module Parser = rootCommand.AddOption adaptiveLspServerOption rootCommand.AddOption logLevelOption rootCommand.AddOption stateLocationOption + rootCommand.AddOption otelTracingOption @@ -170,6 +185,27 @@ module Parser = next.Invoke(ctx)) + let configureOTel = + Invocation.InvocationMiddleware(fun ctx next -> + + if ctx.ParseResult.GetValueForOption otelTracingOption then + let serviceName = FsAutoComplete.Utils.Tracing.serviceName + let version = FsAutoComplete.Utils.Version.info().Version + + tracerProvider <- + Sdk + .CreateTracerProviderBuilder() + .AddSource(serviceName, Tracing.fscServiceName) + .SetResourceBuilder( + ResourceBuilder + .CreateDefault() + .AddService(serviceName = serviceName, serviceVersion = version) + ) + .AddOtlpExporter() + .Build() + + next.Invoke(ctx)) + let configureLogging = Invocation.InvocationMiddleware(fun ctx next -> let isCategory (category: string) (e: LogEvent) = @@ -275,4 +311,5 @@ module Parser = .AddMiddleware(immediateAttach) .AddMiddleware(serilogFlush) .AddMiddleware(configureLogging) + .AddMiddleware(configureOTel) .Build() diff --git a/src/FsAutoComplete/paket.references b/src/FsAutoComplete/paket.references index a5df50b03..5abb22b62 100644 --- a/src/FsAutoComplete/paket.references +++ b/src/FsAutoComplete/paket.references @@ -22,3 +22,4 @@ System.CommandLine System.Configuration.ConfigurationManager FSharp.Data.Adaptive Microsoft.Extensions.Caching.Memory +OpenTelemetry.Exporter.OpenTelemetryProtocol diff --git a/test/FsAutoComplete.Tests.Lsp/Helpers.fs b/test/FsAutoComplete.Tests.Lsp/Helpers.fs index 62e5bbc99..e3d8e315a 100644 --- a/test/FsAutoComplete.Tests.Lsp/Helpers.fs +++ b/test/FsAutoComplete.Tests.Lsp/Helpers.fs @@ -269,6 +269,7 @@ let defaultConfigDto: FSharpConfigDto = Some { Enabled = Some true Prefix = Some "//" } + Notifications = None Debug = None } let clientCaps: ClientCapabilities =