From 37a137862e215443b3e9f33fbf6938230b2b84ce Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Fri, 12 May 2023 15:11:35 +0200 Subject: [PATCH 01/10] =?UTF-8?q?A=20na=C3=AFve=20telemetry=20duration=20i?= =?UTF-8?q?mplementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FSharp.Editor/CodeFix/CodeFixHelpers.fs | 26 ++++----- .../src/FSharp.Editor/Hints/RoslynAdapter.fs | 2 +- .../LanguageService/LanguageService.fs | 24 ++++----- .../LanguageService/SymbolHelpers.fs | 8 +-- .../Telemetry/TelemetryReporter.fs | 53 +++++++++++++------ 5 files changed, 70 insertions(+), 43 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs index e8af59ad674..b13f8de7dfd 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs @@ -17,21 +17,23 @@ open Microsoft.VisualStudio.FSharp.Editor.Telemetry [] module internal CodeFixHelpers = - let private reportCodeFixTelemetry (diagnostics: ImmutableArray) (doc: Document) (staticName: string) (additionalProps) = + let private reportCodeFixTelemetry (diagnostics: ImmutableArray) (doc: Document) (staticName: string) (additionalProps: (string * obj) array) = let ids = diagnostics |> Seq.map (fun d -> d.Id) |> Seq.distinct |> String.concat "," - let props: (string * obj) list = - additionalProps - @ [ - "name", staticName - "ids", ids - "context.document.project.id", doc.Project.Id.Id.ToString() - "context.document.id", doc.Id.Id.ToString() + let defaultProps: (string * obj) array = + [| + "name", staticName; + "ids", ids; + "context.document.project.id", doc.Project.Id.Id.ToString(); + "context.document.id", doc.Id.Id.ToString(); "context.diagnostics.count", diagnostics.Length - ] + |] - TelemetryReporter.reportEvent "codefixactivated" props + let props: (string * obj) array = + Array.concat [additionalProps; defaultProps] + + TelemetryReporter.ReportSingleEvent ("codefixactivated", props) let createFixAllProvider name getChanges = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> @@ -46,7 +48,7 @@ module internal CodeFixHelpers = allDiagnostics doc name - [ "scope", fixAllCtx.Scope.ToString(); "elapsedMs", sw.ElapsedMilliseconds ] + [| "scope", fixAllCtx.Scope.ToString(); "elapsedMs", sw.ElapsedMilliseconds |] return doc }) @@ -58,7 +60,7 @@ module internal CodeFixHelpers = backgroundTask { let! sourceText = context.Document.GetTextAsync(cancellationToken) let doc = context.Document.WithText(sourceText.WithChanges(changes)) - reportCodeFixTelemetry context.Diagnostics context.Document name [] + reportCodeFixTelemetry context.Diagnostics context.Document name [||] return doc }), name diff --git a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs b/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs index 53820400d94..3e553dfbbe1 100644 --- a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs +++ b/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs @@ -27,7 +27,7 @@ type internal RoslynAdapter [] (settings: EditorOptions) = return ImmutableArray.Empty else let hintKindsSerialized = hintKinds |> Set.map Hints.serialize |> String.concat "," - TelemetryReporter.reportEvent "hints" [ ("hints.kinds", hintKindsSerialized) ] + TelemetryReporter.ReportSingleEvent ("hints", [| ("hints.kinds", hintKindsSerialized) |]) let! sourceText = document.GetTextAsync cancellationToken |> Async.AwaitTask let! nativeHints = HintService.getHintsForDocument sourceText document hintKinds userOpName cancellationToken diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index c9b25aad44d..4458c428caa 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -118,7 +118,7 @@ type internal FSharpWorkspaceServiceFactory [ let checker = lazy - TelemetryReporter.reportEvent "languageservicestarted" [] + TelemetryReporter.ReportSingleEvent("languageservicestarted", [||]) let editorOptions = workspace.Services.GetService() @@ -162,18 +162,18 @@ type internal FSharpWorkspaceServiceFactory [ diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index 1fcf662bdc0..acb0fd8aaf3 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -72,11 +72,13 @@ module internal SymbolHelpers = | firstProject :: _ -> let isFastFindReferencesEnabled = firstProject.IsFastFindReferencesEnabled + // TODO: this needs to use already boxed boolean instead of boxing it every time. let props = - [ nameof isFastFindReferencesEnabled, isFastFindReferencesEnabled :> obj ] + [| nameof isFastFindReferencesEnabled, isFastFindReferencesEnabled :> obj |] backgroundTask { - TelemetryReporter.reportEvent "getSymbolUsesInProjectsStarted" props + // TODO: this needs to be a single event with a duration + TelemetryReporter.ReportSingleEvent("getSymbolUsesInProjectsStarted", props) do! projects @@ -84,7 +86,7 @@ module internal SymbolHelpers = Task.Run(fun () -> project.FindFSharpReferencesAsync(symbol, onFound, "getSymbolUsesInProjects", ct))) |> Task.WhenAll - TelemetryReporter.reportEvent "getSymbolUsesInProjectsFinished" props + TelemetryReporter.ReportSingleEvent("getSymbolUsesInProjectsFinished", props) } let findSymbolUses (symbolUse: FSharpSymbolUse) (currentDocument: Document) (checkFileResults: FSharpCheckFileResults) onFound = diff --git a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs index 1cfe1c42d6f..bdba744fc10 100644 --- a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs +++ b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs @@ -3,25 +3,48 @@ namespace Microsoft.VisualStudio.FSharp.Editor.Telemetry open Microsoft.VisualStudio.Telemetry +open System +open System.Diagnostics -module TelemetryReporter = - - let private eventPrefix = "dotnet/fsharp/" - let private propPrefix = "dotnet.fsharp." - - let private getFullEventName name = eventPrefix + name - let private getFullPropName name = propPrefix + name - - let private createEvent name (props: (string * obj) list) = - let event = TelemetryEvent(getFullEventName name) +#nowarn "3220" // Ignore warning about direct tuple items access. - props - |> List.map (fun (k, v) -> getFullPropName k, v) - |> List.iter event.Properties.Add +[] +module TelemetryReporter = + let [] eventPrefix = "dotnet/fsharp/" + let [] propPrefix = "dotnet.fsharp." + + // This should always be inlined. + let inline createEvent name (props: (string * obj) array) = + let eventName = eventPrefix + name + let event = TelemetryEvent(eventName) + + // TODO: need to carefully review the code, since it will be a hot path when we are sending telemetry + // This particular approach is here to avoid alocations for properties, which is likely the case if we destructing them. + for prop in props do + event.Properties.Add(propPrefix + prop.Item1, prop.Item2) event - let reportEvent name props = +[] +type TelemetryReporter private (name: string, props: (string * obj) array, stopwatch: Stopwatch) = + + static member ReportSingleEvent (name, props) = let session = TelemetryService.DefaultSession - let event = createEvent name props + let event = TelemetryReporter.createEvent name props session.PostEvent event + + // A naïve implementation using stopwatch and returning an IDisposable + // TODO: needs a careful review, since it will be a hot path when we are sending telemetry + static member ReportSingleEventWithDuration (name, props) : IDisposable = + let stopwatch = Stopwatch() + stopwatch.Start() + new TelemetryReporter(name, props, stopwatch) + + interface IDisposable with + member _.Dispose() = + let session = TelemetryService.DefaultSession + stopwatch.Stop() + let event = TelemetryReporter.createEvent name (Array.concat [props; [| "vs_event_duration_ms", stopwatch.ElapsedMilliseconds |]]) + session.PostEvent event + + \ No newline at end of file From a97cefb51b2e9e1d8bf3a6c086b273e200c6ebcb Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Fri, 12 May 2023 16:55:44 +0200 Subject: [PATCH 02/10] Throttled event sending for heavy ops --- .../Classification/ClassificationService.fs | 20 ++++++- .../FSharp.Editor/CodeFix/CodeFixHelpers.fs | 2 +- .../src/FSharp.Editor/Hints/RoslynAdapter.fs | 2 +- .../LanguageService/LanguageService.fs | 4 +- .../LanguageService/SymbolHelpers.fs | 4 +- .../Telemetry/TelemetryReporter.fs | 56 +++++++++++++++++-- 6 files changed, 73 insertions(+), 15 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index 43673355abc..5c5c73f3336 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -20,6 +20,7 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Classification open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.EditorServices open FSharp.Compiler.Tokenization +open Microsoft.VisualStudio.FSharp.Editor.Telemetry // IEditorClassificationService is marked as Obsolete, but is still supported. The replacement (IClassificationService) // is internal to Microsoft.CodeAnalysis.Workspaces which we don't have internals visible to. Rather than add yet another @@ -183,7 +184,10 @@ type internal FSharpClassificationService [] () = // For closed documents, only get classification for the text within the span. // This may be inaccurate for multi-line tokens such as string literals, but this is ok for now // as it's better than having to tokenize a big part of a file which in return will allocate a lot and hurt find all references performance. - if not (document.Project.Solution.Workspace.IsDocumentOpen document.Id) then + let isOpenDocument = document.Project.Solution.Workspace.IsDocumentOpen document.Id + let eventProps = [| "isOpenDocument", isOpenDocument :> obj; "textSpanLength", textSpan.Length |] + use _eventDuration = TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSyntacticCalssifications, eventProps) + if not isOpenDocument then result.AddRange(getLexicalClassifications (document.FilePath, defines, sourceText, textSpan, cancellationToken)) else result.AddRange( @@ -200,7 +204,7 @@ type internal FSharpClassificationService [] () = } |> RoslynHelpers.StartAsyncUnitAsTask cancellationToken - member this.AddSemanticClassificationsAsync + member _.AddSemanticClassificationsAsync ( document: Document, textSpan: TextSpan, @@ -215,16 +219,26 @@ type internal FSharpClassificationService [] () = // If we are trying to get semantic classification for a document that is not open, get the results from the background and cache it. // We do this for find all references when it is populating results. // We cache it temporarily so we do not have to continously call into the checker and perform a background operation. - if not (document.Project.Solution.Workspace.IsDocumentOpen document.Id) then + let isOpenDocument = document.Project.Solution.Workspace.IsDocumentOpen document.Id + if not isOpenDocument then match! semanticClassificationCache.TryGetValueAsync document with | ValueSome classificationDataLookup -> + let eventProps = [| "isOpenDocument", isOpenDocument :> obj; "textSpanLength", textSpan.Length; "cacheHit", true |] + use _eventDuration = TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps) + addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result | _ -> + let eventProps = [| "isOpenDocument", isOpenDocument :> obj; "textSpanLength", textSpan.Length; "cacheHit", false |] + use _eventDuration = TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps) + let! classificationData = document.GetFSharpSemanticClassificationAsync(nameof (FSharpClassificationService)) let classificationDataLookup = toSemanticClassificationLookup classificationData do! semanticClassificationCache.SetAsync(document, classificationDataLookup) addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result else + let eventProps = [| "isOpenDocument", isOpenDocument :> obj; "textSpanLength", textSpan.Length; "cacheHit", false |] + use _eventDuration = TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps) + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof (IFSharpClassificationService)) let targetRange = diff --git a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs index b13f8de7dfd..5ac68419a51 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs @@ -33,7 +33,7 @@ module internal CodeFixHelpers = let props: (string * obj) array = Array.concat [additionalProps; defaultProps] - TelemetryReporter.ReportSingleEvent ("codefixactivated", props) + TelemetryReporter.ReportSingleEvent (TelemetryEvents.CodefixActivated, props) let createFixAllProvider name getChanges = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> diff --git a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs b/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs index 3e553dfbbe1..ff72c93ffa6 100644 --- a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs +++ b/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs @@ -27,7 +27,7 @@ type internal RoslynAdapter [] (settings: EditorOptions) = return ImmutableArray.Empty else let hintKindsSerialized = hintKinds |> Set.map Hints.serialize |> String.concat "," - TelemetryReporter.ReportSingleEvent ("hints", [| ("hints.kinds", hintKindsSerialized) |]) + TelemetryReporter.ReportSingleEvent (TelemetryEvents.Hints, [| ("hints.kinds", hintKindsSerialized) |]) let! sourceText = document.GetTextAsync cancellationToken |> Async.AwaitTask let! nativeHints = HintService.getHintsForDocument sourceText document hintKinds userOpName cancellationToken diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 4458c428caa..8d562c06dd0 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -118,7 +118,7 @@ type internal FSharpWorkspaceServiceFactory [ let checker = lazy - TelemetryReporter.ReportSingleEvent("languageservicestarted", [||]) + TelemetryReporter.ReportSingleEvent(TelemetryEvents.LanguageServiceStarted, [||]) let editorOptions = workspace.Services.GetService() @@ -163,7 +163,7 @@ type internal FSharpWorkspaceServiceFactory [ project.FindFSharpReferencesAsync(symbol, onFound, "getSymbolUsesInProjects", ct))) |> Task.WhenAll - TelemetryReporter.ReportSingleEvent("getSymbolUsesInProjectsFinished", props) + TelemetryReporter.ReportSingleEvent(TelemetryEvents.GetSymbolUsesInProjectsFinished, props) } let findSymbolUses (symbolUse: FSharpSymbolUse) (currentDocument: Document) (checkFileResults: FSharpCheckFileResults) onFound = diff --git a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs index bdba744fc10..d1624b1d0f1 100644 --- a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs +++ b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs @@ -5,18 +5,46 @@ namespace Microsoft.VisualStudio.FSharp.Editor.Telemetry open Microsoft.VisualStudio.Telemetry open System open System.Diagnostics +open System.Collections.Concurrent #nowarn "3220" // Ignore warning about direct tuple items access. +[] +module TelemetryEvents = + let [] CodefixActivated = "codefixactivated" + let [] Hints = "hints" + let [] LanguageServiceStarted = "languageservicestarted" + let [] GetSymbolUsesInProjectsStarted = "getSymbolUsesInProjectsStarted" + let [] GetSymbolUsesInProjectsFinished = "getSymbolUsesInProjectsFinished" + let [] AddSyntacticCalssifications = "addsyntacticclassifications" + let [] AddSemanticCalssifications = "addsemanticclassifications" + +// TODO: needs to be something more sophisticated in future +[] +type TelemetryThrottlingStrategy = + | NoThrottling + | Throttle of {| Timeout: TimeSpan |} + // At most, send one event per 3 seconds. + static member Default = Throttle {| Timeout = TimeSpan.FromSeconds(3.0) |} + [] module TelemetryReporter = + let internal noopDisposable = + { new IDisposable with + member _.Dispose() = () } + + let internal lastSentEvents = ConcurrentDictionary() + let [] eventPrefix = "dotnet/fsharp/" let [] propPrefix = "dotnet.fsharp." // This should always be inlined. let inline createEvent name (props: (string * obj) array) = let eventName = eventPrefix + name - let event = TelemetryEvent(eventName) + let event = TelemetryEvent(eventName, TelemetrySeverity.Normal) + + // TODO: + // We need to utilize TelemetryEvent's Correlation id, so we can track (for example) events on one document in the project. // TODO: need to carefully review the code, since it will be a hot path when we are sending telemetry // This particular approach is here to avoid alocations for properties, which is likely the case if we destructing them. @@ -35,16 +63,32 @@ type TelemetryReporter private (name: string, props: (string * obj) array, stopw // A naïve implementation using stopwatch and returning an IDisposable // TODO: needs a careful review, since it will be a hot path when we are sending telemetry - static member ReportSingleEventWithDuration (name, props) : IDisposable = - let stopwatch = Stopwatch() - stopwatch.Start() - new TelemetryReporter(name, props, stopwatch) + static member ReportSingleEventWithDuration (name, props, ?throttlingStrategy) : IDisposable = + let throttlingStrategy = defaultArg throttlingStrategy TelemetryThrottlingStrategy.Default + + match throttlingStrategy with + | TelemetryThrottlingStrategy.NoThrottling -> + let stopwatch = Stopwatch() + stopwatch.Start() + new TelemetryReporter(name, props, stopwatch) + | TelemetryThrottlingStrategy.Throttle s -> + // This is not "atomic" for now, theoretically multiple threads can send the event as for now. + match TelemetryReporter.lastSentEvents.TryGetValue(name) with + | false, lastSent + | true, lastSent when lastSent + s.Timeout < DateTime.UtcNow -> + let stopwatch = Stopwatch() + stopwatch.Start() + new TelemetryReporter(name, props, stopwatch) + | _ -> + TelemetryReporter.noopDisposable interface IDisposable with member _.Dispose() = let session = TelemetryService.DefaultSession stopwatch.Stop() let event = TelemetryReporter.createEvent name (Array.concat [props; [| "vs_event_duration_ms", stopwatch.ElapsedMilliseconds |]]) - session.PostEvent event + session.PostEvent event + // Whenever we send an event, we update the last sent time. + TelemetryReporter.lastSentEvents.AddOrUpdate(name, DateTime.UtcNow, fun _ _ -> DateTime.UtcNow) |> ignore \ No newline at end of file From e09f354d67c5525efad1f85c8ed0d8eeed17b4a3 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Fri, 12 May 2023 17:03:42 +0200 Subject: [PATCH 03/10] Fantomas --- .../Classification/ClassificationService.fs | 45 +++++++++--- .../FSharp.Editor/CodeFix/CodeFixHelpers.fs | 20 +++--- .../src/FSharp.Editor/Hints/RoslynAdapter.fs | 2 +- .../LanguageService/LanguageService.fs | 17 ++--- .../Telemetry/TelemetryReporter.fs | 69 ++++++++++++------- 5 files changed, 104 insertions(+), 49 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index 5c5c73f3336..ddc3ace50c8 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -185,8 +185,13 @@ type internal FSharpClassificationService [] () = // This may be inaccurate for multi-line tokens such as string literals, but this is ok for now // as it's better than having to tokenize a big part of a file which in return will allocate a lot and hurt find all references performance. let isOpenDocument = document.Project.Solution.Workspace.IsDocumentOpen document.Id - let eventProps = [| "isOpenDocument", isOpenDocument :> obj; "textSpanLength", textSpan.Length |] - use _eventDuration = TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSyntacticCalssifications, eventProps) + + let eventProps = + [| "isOpenDocument", isOpenDocument :> obj; "textSpanLength", textSpan.Length |] + + use _eventDuration = + TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSyntacticCalssifications, eventProps) + if not isOpenDocument then result.AddRange(getLexicalClassifications (document.FilePath, defines, sourceText, textSpan, cancellationToken)) else @@ -220,24 +225,46 @@ type internal FSharpClassificationService [] () = // We do this for find all references when it is populating results. // We cache it temporarily so we do not have to continously call into the checker and perform a background operation. let isOpenDocument = document.Project.Solution.Workspace.IsDocumentOpen document.Id + if not isOpenDocument then match! semanticClassificationCache.TryGetValueAsync document with | ValueSome classificationDataLookup -> - let eventProps = [| "isOpenDocument", isOpenDocument :> obj; "textSpanLength", textSpan.Length; "cacheHit", true |] - use _eventDuration = TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps) - + let eventProps = + [| + "isOpenDocument", isOpenDocument :> obj + "textSpanLength", textSpan.Length + "cacheHit", true + |] + + use _eventDuration = + TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps) + addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result | _ -> - let eventProps = [| "isOpenDocument", isOpenDocument :> obj; "textSpanLength", textSpan.Length; "cacheHit", false |] - use _eventDuration = TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps) + let eventProps = + [| + "isOpenDocument", isOpenDocument :> obj + "textSpanLength", textSpan.Length + "cacheHit", false + |] + + use _eventDuration = + TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps) let! classificationData = document.GetFSharpSemanticClassificationAsync(nameof (FSharpClassificationService)) let classificationDataLookup = toSemanticClassificationLookup classificationData do! semanticClassificationCache.SetAsync(document, classificationDataLookup) addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result else - let eventProps = [| "isOpenDocument", isOpenDocument :> obj; "textSpanLength", textSpan.Length; "cacheHit", false |] - use _eventDuration = TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps) + let eventProps = + [| + "isOpenDocument", isOpenDocument :> obj + "textSpanLength", textSpan.Length + "cacheHit", false + |] + + use _eventDuration = + TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps) let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof (IFSharpClassificationService)) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs index 5ac68419a51..924bf6d38fb 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/CodeFixHelpers.fs @@ -17,23 +17,27 @@ open Microsoft.VisualStudio.FSharp.Editor.Telemetry [] module internal CodeFixHelpers = - let private reportCodeFixTelemetry (diagnostics: ImmutableArray) (doc: Document) (staticName: string) (additionalProps: (string * obj) array) = + let private reportCodeFixTelemetry + (diagnostics: ImmutableArray) + (doc: Document) + (staticName: string) + (additionalProps: (string * obj) array) + = let ids = diagnostics |> Seq.map (fun d -> d.Id) |> Seq.distinct |> String.concat "," let defaultProps: (string * obj) array = [| - "name", staticName; - "ids", ids; - "context.document.project.id", doc.Project.Id.Id.ToString(); - "context.document.id", doc.Id.Id.ToString(); + "name", staticName + "ids", ids + "context.document.project.id", doc.Project.Id.Id.ToString() + "context.document.id", doc.Id.Id.ToString() "context.diagnostics.count", diagnostics.Length |] - let props: (string * obj) array = - Array.concat [additionalProps; defaultProps] + let props: (string * obj) array = Array.concat [ additionalProps; defaultProps ] - TelemetryReporter.ReportSingleEvent (TelemetryEvents.CodefixActivated, props) + TelemetryReporter.ReportSingleEvent(TelemetryEvents.CodefixActivated, props) let createFixAllProvider name getChanges = FixAllProvider.Create(fun fixAllCtx doc allDiagnostics -> diff --git a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs b/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs index ff72c93ffa6..e24562031ab 100644 --- a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs +++ b/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs @@ -27,7 +27,7 @@ type internal RoslynAdapter [] (settings: EditorOptions) = return ImmutableArray.Empty else let hintKindsSerialized = hintKinds |> Set.map Hints.serialize |> String.concat "," - TelemetryReporter.ReportSingleEvent (TelemetryEvents.Hints, [| ("hints.kinds", hintKindsSerialized) |]) + TelemetryReporter.ReportSingleEvent(TelemetryEvents.Hints, [| ("hints.kinds", hintKindsSerialized) |]) let! sourceText = document.GetTextAsync cancellationToken |> Async.AwaitTask let! nativeHints = HintService.getHintsForDocument sourceText document hintKinds userOpName cancellationToken diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 8d562c06dd0..c1544bf78c4 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -165,15 +165,16 @@ type internal FSharpWorkspaceServiceFactory [ diff --git a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs index d1624b1d0f1..3896d62880a 100644 --- a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs +++ b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs @@ -11,13 +11,26 @@ open System.Collections.Concurrent [] module TelemetryEvents = - let [] CodefixActivated = "codefixactivated" - let [] Hints = "hints" - let [] LanguageServiceStarted = "languageservicestarted" - let [] GetSymbolUsesInProjectsStarted = "getSymbolUsesInProjectsStarted" - let [] GetSymbolUsesInProjectsFinished = "getSymbolUsesInProjectsFinished" - let [] AddSyntacticCalssifications = "addsyntacticclassifications" - let [] AddSemanticCalssifications = "addsemanticclassifications" + [] + let CodefixActivated = "codefixactivated" + + [] + let Hints = "hints" + + [] + let LanguageServiceStarted = "languageservicestarted" + + [] + let GetSymbolUsesInProjectsStarted = "getSymbolUsesInProjectsStarted" + + [] + let GetSymbolUsesInProjectsFinished = "getSymbolUsesInProjectsFinished" + + [] + let AddSyntacticCalssifications = "addsyntacticclassifications" + + [] + let AddSemanticCalssifications = "addsemanticclassifications" // TODO: needs to be something more sophisticated in future [] @@ -25,27 +38,35 @@ type TelemetryThrottlingStrategy = | NoThrottling | Throttle of {| Timeout: TimeSpan |} // At most, send one event per 3 seconds. - static member Default = Throttle {| Timeout = TimeSpan.FromSeconds(3.0) |} + static member Default = + Throttle + {| + Timeout = TimeSpan.FromSeconds(3.0) + |} [] module TelemetryReporter = let internal noopDisposable = { new IDisposable with - member _.Dispose() = () } + member _.Dispose() = () + } let internal lastSentEvents = ConcurrentDictionary() - let [] eventPrefix = "dotnet/fsharp/" - let [] propPrefix = "dotnet.fsharp." + [] + let eventPrefix = "dotnet/fsharp/" + + [] + let propPrefix = "dotnet.fsharp." // This should always be inlined. let inline createEvent name (props: (string * obj) array) = let eventName = eventPrefix + name let event = TelemetryEvent(eventName, TelemetrySeverity.Normal) - + // TODO: // We need to utilize TelemetryEvent's Correlation id, so we can track (for example) events on one document in the project. - + // TODO: need to carefully review the code, since it will be a hot path when we are sending telemetry // This particular approach is here to avoid alocations for properties, which is likely the case if we destructing them. for prop in props do @@ -54,17 +75,18 @@ module TelemetryReporter = event [] -type TelemetryReporter private (name: string, props: (string * obj) array, stopwatch: Stopwatch) = +type TelemetryReporter private (name: string, props: (string * obj) array, stopwatch: Stopwatch) = - static member ReportSingleEvent (name, props) = + static member ReportSingleEvent(name, props) = let session = TelemetryService.DefaultSession let event = TelemetryReporter.createEvent name props session.PostEvent event // A naïve implementation using stopwatch and returning an IDisposable // TODO: needs a careful review, since it will be a hot path when we are sending telemetry - static member ReportSingleEventWithDuration (name, props, ?throttlingStrategy) : IDisposable = - let throttlingStrategy = defaultArg throttlingStrategy TelemetryThrottlingStrategy.Default + static member ReportSingleEventWithDuration(name, props, ?throttlingStrategy) : IDisposable = + let throttlingStrategy = + defaultArg throttlingStrategy TelemetryThrottlingStrategy.Default match throttlingStrategy with | TelemetryThrottlingStrategy.NoThrottling -> @@ -79,16 +101,17 @@ type TelemetryReporter private (name: string, props: (string * obj) array, stopw let stopwatch = Stopwatch() stopwatch.Start() new TelemetryReporter(name, props, stopwatch) - | _ -> - TelemetryReporter.noopDisposable + | _ -> TelemetryReporter.noopDisposable interface IDisposable with member _.Dispose() = let session = TelemetryService.DefaultSession stopwatch.Stop() - let event = TelemetryReporter.createEvent name (Array.concat [props; [| "vs_event_duration_ms", stopwatch.ElapsedMilliseconds |]]) + + let event = + TelemetryReporter.createEvent name (Array.concat [ props; [| "vs_event_duration_ms", stopwatch.ElapsedMilliseconds |] ]) + session.PostEvent event // Whenever we send an event, we update the last sent time. - TelemetryReporter.lastSentEvents.AddOrUpdate(name, DateTime.UtcNow, fun _ _ -> DateTime.UtcNow) |> ignore - - \ No newline at end of file + TelemetryReporter.lastSentEvents.AddOrUpdate(name, DateTime.UtcNow, (fun _ _ -> DateTime.UtcNow)) + |> ignore From 1d185029cee74d975bfb0efd40c5ad21d9c47a7e Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 15 May 2023 12:42:44 +0200 Subject: [PATCH 04/10] VS option for telemetry + completions + diagnostics telemetry --- .../Completion/CompletionProvider.fs | 13 +++ .../Diagnostics/DocumentDiagnosticAnalyzer.fs | 12 +++ .../src/FSharp.Editor/FSharp.Editor.fsproj | 2 +- .../FSharp.Editor/Options/EditorOptions.fs | 2 + .../Telemetry/TelemetryReporter.fs | 91 +++++++++---------- .../AdvancedOptionsControl.xaml | 4 + .../FSharp.UIResources/Strings.Designer.cs | 18 ++++ .../src/FSharp.UIResources/Strings.resx | 6 ++ .../src/FSharp.UIResources/xlf/Strings.cs.xlf | 10 ++ .../src/FSharp.UIResources/xlf/Strings.de.xlf | 10 ++ .../src/FSharp.UIResources/xlf/Strings.es.xlf | 10 ++ .../src/FSharp.UIResources/xlf/Strings.fr.xlf | 10 ++ .../src/FSharp.UIResources/xlf/Strings.it.xlf | 10 ++ .../src/FSharp.UIResources/xlf/Strings.ja.xlf | 10 ++ .../src/FSharp.UIResources/xlf/Strings.ko.xlf | 10 ++ .../src/FSharp.UIResources/xlf/Strings.pl.xlf | 10 ++ .../FSharp.UIResources/xlf/Strings.pt-BR.xlf | 10 ++ .../src/FSharp.UIResources/xlf/Strings.ru.xlf | 10 ++ .../src/FSharp.UIResources/xlf/Strings.tr.xlf | 10 ++ .../xlf/Strings.zh-Hans.xlf | 10 ++ .../xlf/Strings.zh-Hant.xlf | 10 ++ 21 files changed, 231 insertions(+), 47 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index 963997eb804..498a9761373 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -13,6 +13,7 @@ open Microsoft.CodeAnalysis.Completion open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Completion +open Microsoft.VisualStudio.FSharp.Editor.Telemetry open Microsoft.VisualStudio.Shell open FSharp.Compiler.CodeAnalysis @@ -21,6 +22,7 @@ open FSharp.Compiler.Syntax open FSharp.Compiler.Text open FSharp.Compiler.Tokenization + module Logger = Microsoft.VisualStudio.FSharp.Editor.Logger type internal FSharpCompletionProvider @@ -142,6 +144,7 @@ type internal FSharpCompletionProvider ) = asyncMaybe { + let! parseResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync("ProvideCompletionsAsyncAux") |> liftAsync @@ -298,6 +301,16 @@ type internal FSharpCompletionProvider Logger.LogBlockMessage context.Document.Name LogEditorFunctionId.Completion_ProvideCompletionsAsync let document = context.Document + + let eventProps: (string * obj) array = + [| + "context.document.project.id", document.Project.Id.Id.ToString() + "context.document.id", document.Id.Id.ToString() + |] + + use _eventDuration = + TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.ProvideCompletions, eventProps) + let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let defines, langVersion = document.GetFSharpQuickDefinesAndLangVersion() diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index 9607840abf4..850b5dd06d0 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -15,6 +15,7 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Diagnostics +open Microsoft.VisualStudio.FSharp.Editor.Telemetry [] type internal DiagnosticsType = @@ -53,6 +54,17 @@ type internal FSharpDocumentDiagnosticAnalyzer [] () = static member GetDiagnostics(document: Document, diagnosticType: DiagnosticsType) = async { + + let eventProps: (string * obj) array = + [| + "context.document.project.id", document.Project.Id.Id.ToString() + "context.document.id", document.Id.Id.ToString() + "context.diagnostics.type", match diagnosticType with DiagnosticsType.Syntax -> "syntax" | DiagnosticsType.Semantic -> "semantic" + |] + + use _eventDuration = + TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.GetDiagnosticsForDocument, eventProps) + let! ct = Async.CancellationToken let! parseResults = document.GetFSharpParseResultsAsync("GetDiagnostics") diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 6ba152f9233..fb9f60c0658 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -28,7 +28,6 @@ - @@ -44,6 +43,7 @@ + diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index 9ecda0ff396..137bcce687d 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -105,6 +105,7 @@ type AdvancedOptions = IsInlineParameterNameHintsEnabled: bool IsInlineReturnTypeHintsEnabled: bool IsLiveBuffersEnabled: bool + SendAdditionalTelemetry: bool } static member Default = @@ -115,6 +116,7 @@ type AdvancedOptions = IsInlineParameterNameHintsEnabled = false IsInlineReturnTypeHintsEnabled = false IsLiveBuffersEnabled = FSharpExperimentalFeaturesEnabledAutomatically + SendAdditionalTelemetry = FSharpExperimentalFeaturesEnabledAutomatically } [] diff --git a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs index 3896d62880a..98dab8502d3 100644 --- a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs +++ b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs @@ -6,31 +6,25 @@ open Microsoft.VisualStudio.Telemetry open System open System.Diagnostics open System.Collections.Concurrent +open Microsoft.VisualStudio.Shell +open Microsoft.VisualStudio +open Microsoft.VisualStudio.LanguageServices +open Microsoft.VisualStudio.FSharp.Editor #nowarn "3220" // Ignore warning about direct tuple items access. [] -module TelemetryEvents = - [] - let CodefixActivated = "codefixactivated" - - [] - let Hints = "hints" - - [] - let LanguageServiceStarted = "languageservicestarted" - - [] - let GetSymbolUsesInProjectsStarted = "getSymbolUsesInProjectsStarted" - - [] - let GetSymbolUsesInProjectsFinished = "getSymbolUsesInProjectsFinished" - - [] - let AddSyntacticCalssifications = "addsyntacticclassifications" - - [] - let AddSemanticCalssifications = "addsemanticclassifications" +module TelemetryEvents = + let [] CodefixActivated = "codefixactivated" + let [] Hints = "hints" + let [] LanguageServiceStarted = "languageservicestarted" + let [] GetSymbolUsesInProjectsStarted = "getSymbolUsesInProjectsStarted" + let [] GetSymbolUsesInProjectsFinished = "getSymbolUsesInProjectsFinished" + let [] AddSyntacticCalssifications = "addsyntacticclassifications" + let [] AddSemanticCalssifications = "addsemanticclassifications" + let [] GetDiagnosticsForDocument = "getdiagnosticsfordocument" + let [] ProvideCompletions = "providecompletions" + // TODO: needs to be something more sophisticated in future [] @@ -53,11 +47,8 @@ module TelemetryReporter = let internal lastSentEvents = ConcurrentDictionary() - [] - let eventPrefix = "dotnet/fsharp/" - - [] - let propPrefix = "dotnet.fsharp." + let [] eventPrefix = "dotnet/fsharp/" + let [] propPrefix = "dotnet.fsharp." // This should always be inlined. let inline createEvent name (props: (string * obj) array) = @@ -77,41 +68,49 @@ module TelemetryReporter = [] type TelemetryReporter private (name: string, props: (string * obj) array, stopwatch: Stopwatch) = + static let componentModel = + Package.GetGlobalService(typeof) :?> ComponentModelHost.IComponentModel + static let workspace = componentModel.GetService() + static let settings: EditorOptions = workspace.Services.GetService() + + static let session = TelemetryService.DefaultSession + static member ReportSingleEvent(name, props) = - let session = TelemetryService.DefaultSession let event = TelemetryReporter.createEvent name props session.PostEvent event // A naïve implementation using stopwatch and returning an IDisposable // TODO: needs a careful review, since it will be a hot path when we are sending telemetry static member ReportSingleEventWithDuration(name, props, ?throttlingStrategy) : IDisposable = - let throttlingStrategy = - defaultArg throttlingStrategy TelemetryThrottlingStrategy.Default - - match throttlingStrategy with - | TelemetryThrottlingStrategy.NoThrottling -> - let stopwatch = Stopwatch() - stopwatch.Start() - new TelemetryReporter(name, props, stopwatch) - | TelemetryThrottlingStrategy.Throttle s -> - // This is not "atomic" for now, theoretically multiple threads can send the event as for now. - match TelemetryReporter.lastSentEvents.TryGetValue(name) with - | false, lastSent - | true, lastSent when lastSent + s.Timeout < DateTime.UtcNow -> + + if not settings.Advanced.SendAdditionalTelemetry then + TelemetryReporter.noopDisposable + else + let throttlingStrategy = + defaultArg throttlingStrategy TelemetryThrottlingStrategy.Default + + match throttlingStrategy with + | TelemetryThrottlingStrategy.NoThrottling -> let stopwatch = Stopwatch() stopwatch.Start() new TelemetryReporter(name, props, stopwatch) - | _ -> TelemetryReporter.noopDisposable + | TelemetryThrottlingStrategy.Throttle s -> + // This is not "atomic" for now, theoretically multiple threads can send the event as for now. + match TelemetryReporter.lastSentEvents.TryGetValue(name) with + | false, lastSent + | true, lastSent when lastSent + s.Timeout < DateTime.UtcNow -> + let stopwatch = Stopwatch() + stopwatch.Start() + // Whenever we create an event, we update the last sent time. + TelemetryReporter.lastSentEvents.AddOrUpdate(name, DateTime.UtcNow, (fun _ _ -> DateTime.UtcNow)) |> ignore + new TelemetryReporter(name, props, stopwatch) + | _ -> TelemetryReporter.noopDisposable interface IDisposable with member _.Dispose() = - let session = TelemetryService.DefaultSession stopwatch.Stop() let event = TelemetryReporter.createEvent name (Array.concat [ props; [| "vs_event_duration_ms", stopwatch.ElapsedMilliseconds |] ]) - session.PostEvent event - // Whenever we send an event, we update the last sent time. - TelemetryReporter.lastSentEvents.AddOrUpdate(name, DateTime.UtcNow, (fun _ _ -> DateTime.UtcNow)) - |> ignore + session.PostEvent event \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml index 78afe373f84..d9388204b6a 100644 --- a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml +++ b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml @@ -38,6 +38,10 @@ + + + diff --git a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs index 05c26ae57a3..1ce713052f2 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs +++ b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs @@ -60,6 +60,15 @@ internal Strings() { } } + /// + /// Looks up a localized string similar to Additional performance telemetry (experimental). + /// + public static string AdditionalTelemetry { + get { + return ResourceManager.GetString("AdditionalTelemetry", resourceCulture); + } + } + /// /// Looks up a localized string similar to Always place open statements at the top level. /// @@ -312,6 +321,15 @@ public static string Project_Performance { } } + /// + /// Looks up a localized string similar to Send additional performance telemetry. + /// + public static string Send_Additional_Telemetry { + get { + return ResourceManager.GetString("Send_Additional_Telemetry", resourceCulture); + } + } + /// /// Looks up a localized string similar to Show s_ymbols in unopened namespaces. /// diff --git a/vsintegration/src/FSharp.UIResources/Strings.resx b/vsintegration/src/FSharp.UIResources/Strings.resx index 98d089b053b..12f2ce0f947 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.resx +++ b/vsintegration/src/FSharp.UIResources/Strings.resx @@ -237,6 +237,12 @@ Live Buffers (experimental) + + Additional performance telemetry (experimental) + + + Send additional performance telemetry + Cache parsing results (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf index 8d56d2adbdd..1d431306de6 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf @@ -2,6 +2,11 @@ + + Additional performance telemetry (experimental) + Additional performance telemetry (experimental) + + Always place open statements at the top level Vždy umístit otevřené příkazy na nejvyšší úroveň @@ -92,6 +97,11 @@ Upřednostňovaná šířka popisu ve znacích + + Send additional performance telemetry + Send additional performance telemetry + + Display inline parameter name hints (preview) Zobrazit nápovědy k názvům vložených parametrů (preview) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf index ea47f390c09..0b4a44eaa31 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf @@ -2,6 +2,11 @@ + + Additional performance telemetry (experimental) + Additional performance telemetry (experimental) + + Always place open statements at the top level open-Anweisungen immer an oberster Ebene platzieren @@ -92,6 +97,11 @@ Bevorzugte Beschreibungsbreite in Zeichen + + Send additional performance telemetry + Send additional performance telemetry + + Display inline parameter name hints (preview) Hinweise zu Inlineparameternamen anzeigen (Vorschau) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf index d58237d68ba..b3b7158342b 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf @@ -2,6 +2,11 @@ + + Additional performance telemetry (experimental) + Additional performance telemetry (experimental) + + Always place open statements at the top level Colocar siempre las instrucciones open en el nivel superior @@ -92,6 +97,11 @@ Anchura preferida de la descripción en caracteres + + Send additional performance telemetry + Send additional performance telemetry + + Display inline parameter name hints (preview) Mostrar sugerencias de nombres de parámetros en línea (vista previa) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf index 30ee652da68..5970e89584b 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf @@ -2,6 +2,11 @@ + + Additional performance telemetry (experimental) + Additional performance telemetry (experimental) + + Always place open statements at the top level Placer toujours les instructions open au niveau supérieur @@ -92,6 +97,11 @@ Largeur de description préférée en caractères + + Send additional performance telemetry + Send additional performance telemetry + + Display inline parameter name hints (preview) Afficher les conseils de nom de paramètre en ligne (préversion) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf index 6b4009249b8..c7f7d17d24b 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf @@ -2,6 +2,11 @@ + + Additional performance telemetry (experimental) + Additional performance telemetry (experimental) + + Always place open statements at the top level Inserisci sempre le istruzioni OPEN al primo livello @@ -92,6 +97,11 @@ Larghezza descrizione preferita in caratteri + + Send additional performance telemetry + Send additional performance telemetry + + Display inline parameter name hints (preview) Visualizza suggerimenti per i nomi di parametro inline (anteprima) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf index 074852e1f72..d699a836bcb 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf @@ -2,6 +2,11 @@ + + Additional performance telemetry (experimental) + Additional performance telemetry (experimental) + + Always place open statements at the top level Open ステートメントを常に最上位に配置する @@ -92,6 +97,11 @@ 優先する説明の文字幅 + + Send additional performance telemetry + Send additional performance telemetry + + Display inline parameter name hints (preview) インライン パラメーター名のヒントを表示する (プレビュー) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf index 369dc3d81cc..c603eb32700 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf @@ -2,6 +2,11 @@ + + Additional performance telemetry (experimental) + Additional performance telemetry (experimental) + + Always place open statements at the top level 항상 최상위에 open 문 배치 @@ -92,6 +97,11 @@ 기본 설정 설명 너비(문자) + + Send additional performance telemetry + Send additional performance telemetry + + Display inline parameter name hints (preview) 인라인 매개 변수 이름 힌트 표시(미리 보기) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf index 8f85bfb4f67..798997dddf3 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf @@ -2,6 +2,11 @@ + + Additional performance telemetry (experimental) + Additional performance telemetry (experimental) + + Always place open statements at the top level Zawsze umieszczaj otwarte instrukcje na najwyższym poziomie @@ -92,6 +97,11 @@ Preferowana szerokość opisu w znakach + + Send additional performance telemetry + Send additional performance telemetry + + Display inline parameter name hints (preview) Wyświetl wskazówki w tekście dotyczące nazw parametrów (wersja zapoznawcza) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf index 257b0f3f57a..110cdc0ee86 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf @@ -2,6 +2,11 @@ + + Additional performance telemetry (experimental) + Additional performance telemetry (experimental) + + Always place open statements at the top level Sempre coloque as instruções abertas no nível superior @@ -92,6 +97,11 @@ Largura de descrição preferencial em caracteres + + Send additional performance telemetry + Send additional performance telemetry + + Display inline parameter name hints (preview) Exibir dicas de nome de parâmetro embutido (versão prévia) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf index ed978ec293a..fabb6126bed 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf @@ -2,6 +2,11 @@ + + Additional performance telemetry (experimental) + Additional performance telemetry (experimental) + + Always place open statements at the top level Всегда располагайте открытые операторы на верхнем уровне @@ -92,6 +97,11 @@ Предпочитаемая ширина описания в символах + + Send additional performance telemetry + Send additional performance telemetry + + Display inline parameter name hints (preview) Отображение подсказок для имен встроенных параметров (предварительная версия) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf index 181076a632d..892af3ca3e8 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf @@ -2,6 +2,11 @@ + + Additional performance telemetry (experimental) + Additional performance telemetry (experimental) + + Always place open statements at the top level Açık deyimleri her zaman en üst düzeye yerleştir @@ -92,6 +97,11 @@ Karakter olarak tercih edilen açıklama genişliği + + Send additional performance telemetry + Send additional performance telemetry + + Display inline parameter name hints (preview) Satır içi parametre adı ipuçlarını görüntüle (önizleme) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf index 4fc3ff63c93..6e0ff2e91df 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf @@ -2,6 +2,11 @@ + + Additional performance telemetry (experimental) + Additional performance telemetry (experimental) + + Always place open statements at the top level 始终在顶层放置 open 语句 @@ -92,6 +97,11 @@ 以字符为单位的首选说明宽度 + + Send additional performance telemetry + Send additional performance telemetry + + Display inline parameter name hints (preview) 显示内联参数名称提示(预览版) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf index 1b0248ea005..1ed09d1f289 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf @@ -2,6 +2,11 @@ + + Additional performance telemetry (experimental) + Additional performance telemetry (experimental) + + Always place open statements at the top level 一律將 open 陳述式放在最上層 @@ -92,6 +97,11 @@ 慣用説明寬度 (以字元為單位) + + Send additional performance telemetry + Send additional performance telemetry + + Display inline parameter name hints (preview) 顯示內嵌參數名稱提示 (預覽) From 803e97fc53727e65872de29ef8b2be3ad26b8611 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 15 May 2023 12:56:28 +0200 Subject: [PATCH 05/10] Fantomas --- .../Completion/CompletionProvider.fs | 3 +- .../Diagnostics/DocumentDiagnosticAnalyzer.fs | 7 ++- .../Telemetry/TelemetryReporter.fs | 52 +++++++++++++------ 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index 498a9761373..baac85294f9 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -22,7 +22,6 @@ open FSharp.Compiler.Syntax open FSharp.Compiler.Text open FSharp.Compiler.Tokenization - module Logger = Microsoft.VisualStudio.FSharp.Editor.Logger type internal FSharpCompletionProvider @@ -307,7 +306,7 @@ type internal FSharpCompletionProvider "context.document.project.id", document.Project.Id.Id.ToString() "context.document.id", document.Id.Id.ToString() |] - + use _eventDuration = TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.ProvideCompletions, eventProps) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index 850b5dd06d0..249a9bb25f8 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -59,9 +59,12 @@ type internal FSharpDocumentDiagnosticAnalyzer [] () = [| "context.document.project.id", document.Project.Id.Id.ToString() "context.document.id", document.Id.Id.ToString() - "context.diagnostics.type", match diagnosticType with DiagnosticsType.Syntax -> "syntax" | DiagnosticsType.Semantic -> "semantic" + "context.diagnostics.type", + match diagnosticType with + | DiagnosticsType.Syntax -> "syntax" + | DiagnosticsType.Semantic -> "semantic" |] - + use _eventDuration = TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.GetDiagnosticsForDocument, eventProps) diff --git a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs index 98dab8502d3..e02cfb31b48 100644 --- a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs +++ b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs @@ -14,17 +14,33 @@ open Microsoft.VisualStudio.FSharp.Editor #nowarn "3220" // Ignore warning about direct tuple items access. [] -module TelemetryEvents = - let [] CodefixActivated = "codefixactivated" - let [] Hints = "hints" - let [] LanguageServiceStarted = "languageservicestarted" - let [] GetSymbolUsesInProjectsStarted = "getSymbolUsesInProjectsStarted" - let [] GetSymbolUsesInProjectsFinished = "getSymbolUsesInProjectsFinished" - let [] AddSyntacticCalssifications = "addsyntacticclassifications" - let [] AddSemanticCalssifications = "addsemanticclassifications" - let [] GetDiagnosticsForDocument = "getdiagnosticsfordocument" - let [] ProvideCompletions = "providecompletions" - +module TelemetryEvents = + [] + let CodefixActivated = "codefixactivated" + + [] + let Hints = "hints" + + [] + let LanguageServiceStarted = "languageservicestarted" + + [] + let GetSymbolUsesInProjectsStarted = "getSymbolUsesInProjectsStarted" + + [] + let GetSymbolUsesInProjectsFinished = "getSymbolUsesInProjectsFinished" + + [] + let AddSyntacticCalssifications = "addsyntacticclassifications" + + [] + let AddSemanticCalssifications = "addsemanticclassifications" + + [] + let GetDiagnosticsForDocument = "getdiagnosticsfordocument" + + [] + let ProvideCompletions = "providecompletions" // TODO: needs to be something more sophisticated in future [] @@ -47,8 +63,11 @@ module TelemetryReporter = let internal lastSentEvents = ConcurrentDictionary() - let [] eventPrefix = "dotnet/fsharp/" - let [] propPrefix = "dotnet.fsharp." + [] + let eventPrefix = "dotnet/fsharp/" + + [] + let propPrefix = "dotnet.fsharp." // This should always be inlined. let inline createEvent name (props: (string * obj) array) = @@ -70,6 +89,7 @@ type TelemetryReporter private (name: string, props: (string * obj) array, stopw static let componentModel = Package.GetGlobalService(typeof) :?> ComponentModelHost.IComponentModel + static let workspace = componentModel.GetService() static let settings: EditorOptions = workspace.Services.GetService() @@ -102,7 +122,9 @@ type TelemetryReporter private (name: string, props: (string * obj) array, stopw let stopwatch = Stopwatch() stopwatch.Start() // Whenever we create an event, we update the last sent time. - TelemetryReporter.lastSentEvents.AddOrUpdate(name, DateTime.UtcNow, (fun _ _ -> DateTime.UtcNow)) |> ignore + TelemetryReporter.lastSentEvents.AddOrUpdate(name, DateTime.UtcNow, (fun _ _ -> DateTime.UtcNow)) + |> ignore + new TelemetryReporter(name, props, stopwatch) | _ -> TelemetryReporter.noopDisposable @@ -113,4 +135,4 @@ type TelemetryReporter private (name: string, props: (string * obj) array, stopw let event = TelemetryReporter.createEvent name (Array.concat [ props; [| "vs_event_duration_ms", stopwatch.ElapsedMilliseconds |] ]) - session.PostEvent event \ No newline at end of file + session.PostEvent event From b5071224963214b393791baa304e25b315699b8b Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 15 May 2023 13:19:03 +0200 Subject: [PATCH 06/10] Fantomas + additional data --- .fantomasignore | 7 +++++-- .../Classification/ClassificationService.fs | 21 +++++++++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/.fantomasignore b/.fantomasignore index d2f57d7fc7f..45eb387fa16 100644 --- a/.fantomasignore +++ b/.fantomasignore @@ -12,7 +12,11 @@ vsintegration/* !vsintegration/tests/FSharp.Editor.Tests artifacts/ -# Explicitly unformatted implementation +# For some reason, it tries to format files from remotes (Processing .\.git\refs\remotes\\FSComp.fsi) +.git/ + + +# Explicitly unformatted implementation src/Compiler/Checking/AccessibilityLogic.fs src/Compiler/Checking/AttributeChecking.fs src/Compiler/Checking/AugmentWithHashCompare.fs @@ -113,4 +117,3 @@ src/FSharp.Core/list.fsi src/FSharp.Core/Query.fsi src/FSharp.Core/resumable.fsi src/FSharp.Core/async.fsi - diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index ddc3ace50c8..31d69db7353 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -186,8 +186,13 @@ type internal FSharpClassificationService [] () = // as it's better than having to tokenize a big part of a file which in return will allocate a lot and hurt find all references performance. let isOpenDocument = document.Project.Solution.Workspace.IsDocumentOpen document.Id - let eventProps = - [| "isOpenDocument", isOpenDocument :> obj; "textSpanLength", textSpan.Length |] + let eventProps: (string * obj) array = + [| + "context.document.project.id", document.Project.Id.Id.ToString() + "context.document.id", document.Id.Id.ToString() + "isOpenDocument", isOpenDocument + "textSpanLength", textSpan.Length + |] use _eventDuration = TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSyntacticCalssifications, eventProps) @@ -229,9 +234,11 @@ type internal FSharpClassificationService [] () = if not isOpenDocument then match! semanticClassificationCache.TryGetValueAsync document with | ValueSome classificationDataLookup -> - let eventProps = + let eventProps: (string * obj) array = [| - "isOpenDocument", isOpenDocument :> obj + "context.document.project.id", document.Project.Id.Id.ToString() + "context.document.id", document.Id.Id.ToString() + "isOpenDocument", isOpenDocument "textSpanLength", textSpan.Length "cacheHit", true |] @@ -256,9 +263,11 @@ type internal FSharpClassificationService [] () = do! semanticClassificationCache.SetAsync(document, classificationDataLookup) addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result else - let eventProps = + let eventProps: (string * obj) array = [| - "isOpenDocument", isOpenDocument :> obj + "context.document.project.id", document.Project.Id.Id.ToString() + "context.document.id", document.Id.Id.ToString() + "isOpenDocument", isOpenDocument "textSpanLength", textSpan.Length "cacheHit", false |] From 9270784388c81ea17a6fd8a11b0c1b82c51b91d0 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 15 May 2023 14:09:20 +0200 Subject: [PATCH 07/10] Lazify settings --- .../Telemetry/TelemetryReporter.fs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs index e02cfb31b48..1e53924b0e4 100644 --- a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs +++ b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs @@ -87,23 +87,23 @@ module TelemetryReporter = [] type TelemetryReporter private (name: string, props: (string * obj) array, stopwatch: Stopwatch) = - static let componentModel = - Package.GetGlobalService(typeof) :?> ComponentModelHost.IComponentModel + static let settings = + lazy + (let componentModel = + Package.GetGlobalService(typeof) :?> ComponentModelHost.IComponentModel - static let workspace = componentModel.GetService() - static let settings: EditorOptions = workspace.Services.GetService() - - static let session = TelemetryService.DefaultSession + let workspace = componentModel.GetService() + workspace.Services.GetService()) static member ReportSingleEvent(name, props) = let event = TelemetryReporter.createEvent name props - session.PostEvent event + TelemetryService.DefaultSession.PostEvent event // A naïve implementation using stopwatch and returning an IDisposable // TODO: needs a careful review, since it will be a hot path when we are sending telemetry static member ReportSingleEventWithDuration(name, props, ?throttlingStrategy) : IDisposable = - if not settings.Advanced.SendAdditionalTelemetry then + if not settings.Value.Advanced.SendAdditionalTelemetry then TelemetryReporter.noopDisposable else let throttlingStrategy = @@ -135,4 +135,4 @@ type TelemetryReporter private (name: string, props: (string * obj) array, stopw let event = TelemetryReporter.createEvent name (Array.concat [ props; [| "vs_event_duration_ms", stopwatch.ElapsedMilliseconds |] ]) - session.PostEvent event + TelemetryService.DefaultSession.PostEvent event From 886d7ad2675d4d1b4d4195b6c36c2b3a48b4a242 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 15 May 2023 16:04:07 +0200 Subject: [PATCH 08/10] misc fixes --- .../Telemetry/TelemetryReporter.fs | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs index 1e53924b0e4..4a1068f0e0d 100644 --- a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs +++ b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs @@ -47,11 +47,11 @@ module TelemetryEvents = type TelemetryThrottlingStrategy = | NoThrottling | Throttle of {| Timeout: TimeSpan |} - // At most, send one event per 3 seconds. + // At most, send one event per 5 seconds. static member Default = Throttle {| - Timeout = TimeSpan.FromSeconds(3.0) + Timeout = TimeSpan.FromSeconds(5.0) |} [] @@ -61,7 +61,7 @@ module TelemetryReporter = member _.Dispose() = () } - let internal lastSentEvents = ConcurrentDictionary() + let internal lastTriggeredEvents = ConcurrentDictionary() [] let eventPrefix = "dotnet/fsharp/" @@ -87,13 +87,16 @@ module TelemetryReporter = [] type TelemetryReporter private (name: string, props: (string * obj) array, stopwatch: Stopwatch) = - static let settings = + static member val private SendAdditionalTelemetry = lazy (let componentModel = Package.GetGlobalService(typeof) :?> ComponentModelHost.IComponentModel - - let workspace = componentModel.GetService() - workspace.Services.GetService()) + + if componentModel = null then + false + else + let workspace = componentModel.GetService() + workspace.Services.GetService().Advanced.SendAdditionalTelemetry) static member ReportSingleEvent(name, props) = let event = TelemetryReporter.createEvent name props @@ -103,7 +106,7 @@ type TelemetryReporter private (name: string, props: (string * obj) array, stopw // TODO: needs a careful review, since it will be a hot path when we are sending telemetry static member ReportSingleEventWithDuration(name, props, ?throttlingStrategy) : IDisposable = - if not settings.Value.Advanced.SendAdditionalTelemetry then + if not TelemetryReporter.SendAdditionalTelemetry.Value then TelemetryReporter.noopDisposable else let throttlingStrategy = @@ -116,13 +119,13 @@ type TelemetryReporter private (name: string, props: (string * obj) array, stopw new TelemetryReporter(name, props, stopwatch) | TelemetryThrottlingStrategy.Throttle s -> // This is not "atomic" for now, theoretically multiple threads can send the event as for now. - match TelemetryReporter.lastSentEvents.TryGetValue(name) with - | false, lastSent - | true, lastSent when lastSent + s.Timeout < DateTime.UtcNow -> + match TelemetryReporter.lastTriggeredEvents.TryGetValue(name) with + | false, lastTriggered + | true, lastTriggered when lastTriggered + s.Timeout < DateTime.UtcNow -> let stopwatch = Stopwatch() stopwatch.Start() - // Whenever we create an event, we update the last sent time. - TelemetryReporter.lastSentEvents.AddOrUpdate(name, DateTime.UtcNow, (fun _ _ -> DateTime.UtcNow)) + // Whenever we create an event, we update the time. + TelemetryReporter.lastTriggeredEvents.AddOrUpdate(name, DateTime.UtcNow, (fun _ _ -> DateTime.UtcNow)) |> ignore new TelemetryReporter(name, props, stopwatch) From 63e0d6fabb217ef019233d976818335521cca37c Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 15 May 2023 16:11:28 +0200 Subject: [PATCH 09/10] fantomas --- vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs index 4a1068f0e0d..566ed7a94a4 100644 --- a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs +++ b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs @@ -91,7 +91,7 @@ type TelemetryReporter private (name: string, props: (string * obj) array, stopw lazy (let componentModel = Package.GetGlobalService(typeof) :?> ComponentModelHost.IComponentModel - + if componentModel = null then false else From eca3d5220a2963c6ed382ec9522a6f5d46e959b2 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 15 May 2023 16:47:57 +0200 Subject: [PATCH 10/10] Enable telemetry by default if internal user --- .../FSharp.Editor/Telemetry/TelemetryReporter.fs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs index 566ed7a94a4..04832c84715 100644 --- a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs +++ b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs @@ -93,7 +93,7 @@ type TelemetryReporter private (name: string, props: (string * obj) array, stopw Package.GetGlobalService(typeof) :?> ComponentModelHost.IComponentModel if componentModel = null then - false + TelemetryService.DefaultSession.IsUserMicrosoftInternal else let workspace = componentModel.GetService() workspace.Services.GetService().Advanced.SendAdditionalTelemetry) @@ -106,9 +106,12 @@ type TelemetryReporter private (name: string, props: (string * obj) array, stopw // TODO: needs a careful review, since it will be a hot path when we are sending telemetry static member ReportSingleEventWithDuration(name, props, ?throttlingStrategy) : IDisposable = - if not TelemetryReporter.SendAdditionalTelemetry.Value then - TelemetryReporter.noopDisposable - else + let additionalTelemetryEnabled = not TelemetryReporter.SendAdditionalTelemetry.Value + + let isUserMicrosoftInternal = + TelemetryService.DefaultSession.IsUserMicrosoftInternal + + if additionalTelemetryEnabled || isUserMicrosoftInternal then let throttlingStrategy = defaultArg throttlingStrategy TelemetryThrottlingStrategy.Default @@ -130,6 +133,8 @@ type TelemetryReporter private (name: string, props: (string * obj) array, stopw new TelemetryReporter(name, props, stopwatch) | _ -> TelemetryReporter.noopDisposable + else + TelemetryReporter.noopDisposable interface IDisposable with member _.Dispose() =