From 18348142006b289056ea25746bd9ef28ed30c5f5 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Fri, 18 Sep 2020 15:05:26 -0700 Subject: [PATCH 01/59] Event to track how many times commands are executed on PMC. --- src/NuGet.Clients/NuGet.Console/Console/ConsoleDispatcher.cs | 3 +++ .../NuGetConsole.Host.PowerShell/PowerShellHost.cs | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/NuGet.Clients/NuGet.Console/Console/ConsoleDispatcher.cs b/src/NuGet.Clients/NuGet.Console/Console/ConsoleDispatcher.cs index e1599bcec13..a16fd780f6e 100644 --- a/src/NuGet.Clients/NuGet.Console/Console/ConsoleDispatcher.cs +++ b/src/NuGet.Clients/NuGet.Console/Console/ConsoleDispatcher.cs @@ -305,6 +305,9 @@ protected Tuple Process(InputLine inputLine) if (inputLine.Flags.HasFlag(InputLineFlag.Execute)) { + var telemetryEvent = new TelemetryEvent("NugetPMCExecuteCommand"); + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + string command = inputLine.Text; bool isExecuted = WpfConsole.Host.Execute(WpfConsole, command, null); WpfConsole.InputHistory.Add(command); diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 1b08399b451..fdd1493462f 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -528,6 +528,10 @@ public bool Execute(IConsole console, string command, params object[] inputs) throw new ArgumentNullException(nameof(command)); } + // Command can come here from both PMC and PM UI. + var telemetryEvent = new TelemetryEvent("NugetPowerShellHostExecuteCommand"); + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + // since install.ps1/uninstall.ps1 could depend on init scripts, so we need to make sure // to run it once for each solution NuGetUIThreadHelper.JoinableTaskFactory.Run(async () => From 594fad9bc54be28f5a5473af5238bcb3c9acb3a8 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Fri, 18 Sep 2020 16:17:54 -0700 Subject: [PATCH 02/59] Batch count of commands executed on PMC. --- .../Console/ConsoleDispatcher.cs | 3 --- .../PowerShellHost.cs | 19 +++++++++++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/NuGet.Clients/NuGet.Console/Console/ConsoleDispatcher.cs b/src/NuGet.Clients/NuGet.Console/Console/ConsoleDispatcher.cs index a16fd780f6e..e1599bcec13 100644 --- a/src/NuGet.Clients/NuGet.Console/Console/ConsoleDispatcher.cs +++ b/src/NuGet.Clients/NuGet.Console/Console/ConsoleDispatcher.cs @@ -305,9 +305,6 @@ protected Tuple Process(InputLine inputLine) if (inputLine.Flags.HasFlag(InputLineFlag.Execute)) { - var telemetryEvent = new TelemetryEvent("NugetPMCExecuteCommand"); - TelemetryActivity.EmitTelemetryEvent(telemetryEvent); - string command = inputLine.Text; bool isExecuted = WpfConsole.Host.Execute(WpfConsole, command, null); WpfConsole.InputHistory.Add(command); diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index fdd1493462f..8fa5057923a 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -62,6 +62,7 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private string _activePackageSource; private string[] _packageSources; private readonly Lazy _dte; + private int _pmcExecutedCount; private uint _solutionExistsCookie; @@ -325,6 +326,13 @@ public void Initialize(IConsole console) DefaultProject = null; + var telemetryEvent = new TelemetryEvent("PMCExecuteCommand", new Dictionary + { + { "NugetPMCExecuteCommandCount", _pmcExecutedCount} + }); + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + _pmcExecutedCount = 0; + NuGetUIThreadHelper.JoinableTaskFactory.Run(CommandUiUtilities.InvalidateDefaultProjectAsync); }; } @@ -529,8 +537,10 @@ public bool Execute(IConsole console, string command, params object[] inputs) } // Command can come here from both PMC and PM UI. - var telemetryEvent = new TelemetryEvent("NugetPowerShellHostExecuteCommand"); - TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + if (console is IWpfConsole) + { + _pmcExecutedCount ++; + } // since install.ps1/uninstall.ps1 could depend on init scripts, so we need to make sure // to run it once for each solution @@ -912,6 +922,11 @@ protected async Task GetPathExpansionsAsyncCore(string line, bo public void Dispose() { + var telemetryEvent = new TelemetryEvent("NugetPowerShellHostExecuteCommand" , new Dictionary + { + { "count", _pmcExecutedCount} + }); + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); _restoreEvents.SolutionRestoreCompleted -= RestoreEvents_SolutionRestoreCompleted; _initScriptsLock.Dispose(); Runspace?.Dispose(); From a8cb55d2f3c63d22f78d9570d094d3bd278c3b97 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Fri, 18 Sep 2020 16:30:02 -0700 Subject: [PATCH 03/59] Fix format. --- .../NuGetConsole.Host.PowerShell/PowerShellHost.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 8fa5057923a..c7eac4925b3 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -539,7 +539,7 @@ public bool Execute(IConsole console, string command, params object[] inputs) // Command can come here from both PMC and PM UI. if (console is IWpfConsole) { - _pmcExecutedCount ++; + _pmcExecutedCount++; } // since install.ps1/uninstall.ps1 could depend on init scripts, so we need to make sure @@ -922,11 +922,6 @@ protected async Task GetPathExpansionsAsyncCore(string line, bo public void Dispose() { - var telemetryEvent = new TelemetryEvent("NugetPowerShellHostExecuteCommand" , new Dictionary - { - { "count", _pmcExecutedCount} - }); - TelemetryActivity.EmitTelemetryEvent(telemetryEvent); _restoreEvents.SolutionRestoreCompleted -= RestoreEvents_SolutionRestoreCompleted; _initScriptsLock.Dispose(); Runspace?.Dispose(); From ef02445f14b92d2395700ce09a71e752b74ef195 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Wed, 23 Sep 2020 16:59:07 -0700 Subject: [PATCH 04/59] Add telemetry for how many times PMC was openened. Add telemetry for if PMC opened by default without user gesture. Add telemetry for how many times powershell commands executed. --- .../Xamls/ConsoleContainer.xaml.cs | 29 +++++++++++++++++++ .../GlobalSuppressions.cs | 1 + .../PowerShellHost.cs | 20 +++++++++++-- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index f8c5cc3a737..bc586ced44b 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Threading; using System.Windows; using System.Windows.Controls; @@ -9,6 +10,7 @@ using Microsoft.ServiceHub.Framework; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; +using NuGet.Common; using NuGet.PackageManagement; using NuGet.PackageManagement.UI; using NuGet.VisualStudio; @@ -23,10 +25,13 @@ namespace NuGetConsole public sealed partial class ConsoleContainer : UserControl, IDisposable { private INuGetSolutionManagerService _solutionManager; + private int _windowLoadCount; + private bool _isTelemetryEmitted; public ConsoleContainer() { InitializeComponent(); + Loaded += ConsoleContainer_Loaded; ThreadHelper.JoinableTaskFactory.StartOnIdle( async () => @@ -76,6 +81,25 @@ public void NotifyInitializationCompleted() public void Dispose() { + if (!_isTelemetryEmitted) + { + var telemetryEvent = new TelemetryEvent("PackageManagerConsoleLoadCount", new Dictionary + { + { "NugetPMCLoadCount", _windowLoadCount} + }); + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + + if (IsLoaded) + { + telemetryEvent = new TelemetryEvent("PackageManagerConsoleDefaultOpen"); + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + } + + _isTelemetryEmitted = true; + } + + Loaded -= ConsoleContainer_Loaded; + // Use more verbose null-checking syntax to avoid ISB001 misfiring. if (_solutionManager != null) { @@ -84,5 +108,10 @@ public void Dispose() GC.SuppressFinalize(this); } + + void ConsoleContainer_Loaded(object sender, RoutedEventArgs e) + { + _windowLoadCount++; + } } } diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/GlobalSuppressions.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/GlobalSuppressions.cs index 567e0e83672..87168dad383 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/GlobalSuppressions.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/GlobalSuppressions.cs @@ -29,3 +29,4 @@ [assembly: SuppressMessage("Build", "CA1303:Method 'T TypeWrapper.GetInterface(object scriptValue, Type interfaceType, Func> getTypeWrapper)' passes a literal string as parameter 'message' of a call to 'ArgumentException.ArgumentException(string message, string paramName)'. Retrieve the following string(s) from a resource table instead: \"Invalid argument\".", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.TypeWrapper`1.GetInterface(System.Object,System.Type,System.Func{System.Object,NuGetConsole.Host.TypeWrapper`1})~`0")] [assembly: SuppressMessage("Build", "CA1507:Use nameof in place of string literal 'interfaceType'", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.TypeWrapper`1.GetInterface(System.Object,System.Type,System.Func{System.Object,NuGetConsole.Host.TypeWrapper`1})~`0")] [assembly: SuppressMessage("Build", "CA1062:In externally visible method 'T TypeWrapper.GetInterface(Type interfaceType)', validate parameter 'interfaceType' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.TypeWrapper`1.GetInterface(System.Type)~`0")] +[assembly: SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.PowerShell.Implementation.PowerShellHost.ExecuteInitPs1Async(System.String,NuGet.Packaging.Core.PackageIdentity)~System.Threading.Tasks.Task")] diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index c7eac4925b3..897af51ecac 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -63,6 +63,7 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private string[] _packageSources; private readonly Lazy _dte; private int _pmcExecutedCount; + private int _nonPmcExecutedCount; private uint _solutionExistsCookie; @@ -326,12 +327,15 @@ public void Initialize(IConsole console) DefaultProject = null; - var telemetryEvent = new TelemetryEvent("PMCExecuteCommand", new Dictionary + var telemetryEvent = new TelemetryEvent("PowerShellExecuteCommand", new Dictionary { - { "NugetPMCExecuteCommandCount", _pmcExecutedCount} + { "NugetPMCExecuteCommandCount", _pmcExecutedCount}, + { "NugetNonPMCExecuteCommandCount", _nonPmcExecutedCount}, + { "NugetTotalExecuteCommandCount", _pmcExecutedCount + _nonPmcExecutedCount} }); TelemetryActivity.EmitTelemetryEvent(telemetryEvent); _pmcExecutedCount = 0; + _nonPmcExecutedCount = 0; NuGetUIThreadHelper.JoinableTaskFactory.Run(CommandUiUtilities.InvalidateDefaultProjectAsync); }; @@ -371,6 +375,9 @@ private void HandleSolutionOpened() { _scriptExecutor.Value.Reset(); + _pmcExecutedCount = 0; + _nonPmcExecutedCount = 0; + // Solution opened event is raised on the UI thread // Go off the UI thread before calling likely expensive call of ExecuteInitScriptsAsync // Also, it uses semaphores, do not call it from the UI thread @@ -492,6 +499,11 @@ private async Task ExecuteInitPs1Async(string installPath, PackageIdentity ident request.BuildInput(), outputResults: true); + var telemetryEvent = new TelemetryEvent(eventName: "PowershellScriptExecuted"); + telemetryEvent.AddPiiData("id", identity.Id?.ToLowerInvariant() ?? "(empty package id)"); + telemetryEvent["version"] = identity.Version; + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + return; } } @@ -541,6 +553,10 @@ public bool Execute(IConsole console, string command, params object[] inputs) { _pmcExecutedCount++; } + else + { + _nonPmcExecutedCount++; + } // since install.ps1/uninstall.ps1 could depend on init scripts, so we need to make sure // to run it once for each solution From 2b2e4a62a82a615449e8f69d35af3c8e321caef8 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Wed, 23 Sep 2020 17:11:43 -0700 Subject: [PATCH 05/59] Add code comment for automatically loaded PMC. --- src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index bc586ced44b..d6f236f41eb 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -89,6 +89,7 @@ public void Dispose() }); TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + // Work around to detect if PMC loaded automatically because it was last focused window. if (IsLoaded) { telemetryEvent = new TelemetryEvent("PackageManagerConsoleDefaultOpen"); From b0476a64456e29f225ab5d9d0170c6536fd14470 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Tue, 29 Sep 2020 12:10:11 -0700 Subject: [PATCH 06/59] There are 2 seperate instances of PowerShellHost.cs can be created for PM UI and PM PMC. So make change to accommodiate this concern. --- .../PowerShellHost.cs | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 897af51ecac..98276025c09 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -316,30 +316,35 @@ public void Initialize(IConsole console) UpdateWorkingDirectory(); await ExecuteInitScriptsAsync(); - // check if PMC console is actually opened, then only hook to solution load/close events. + // check if PMC console is actually opened, then only hook to solution load events. if (console is IWpfConsole) { // Hook up solution events _solutionManager.Value.SolutionOpened += (_, __) => HandleSolutionOpened(); - _solutionManager.Value.SolutionClosed += (o, e) => - { - UpdateWorkingDirectory(); - - DefaultProject = null; + } - var telemetryEvent = new TelemetryEvent("PowerShellExecuteCommand", new Dictionary + _solutionManager.Value.SolutionClosed += (o, e) => + { + var telemetryEvent = new TelemetryEvent("PowerShellExecuteCommand", new Dictionary { { "NugetPMCExecuteCommandCount", _pmcExecutedCount}, { "NugetNonPMCExecuteCommandCount", _nonPmcExecutedCount}, { "NugetTotalExecuteCommandCount", _pmcExecutedCount + _nonPmcExecutedCount} }); - TelemetryActivity.EmitTelemetryEvent(telemetryEvent); - _pmcExecutedCount = 0; - _nonPmcExecutedCount = 0; + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + _pmcExecutedCount = 0; + _nonPmcExecutedCount = 0; + + if (console is IWpfConsole) + { + UpdateWorkingDirectory(); + + DefaultProject = null; NuGetUIThreadHelper.JoinableTaskFactory.Run(CommandUiUtilities.InvalidateDefaultProjectAsync); - }; - } + } + }; + _solutionManager.Value.NuGetProjectAdded += (o, e) => UpdateWorkingDirectoryAndAvailableProjects(); _solutionManager.Value.NuGetProjectRenamed += (o, e) => UpdateWorkingDirectoryAndAvailableProjects(); _solutionManager.Value.NuGetProjectUpdated += (o, e) => UpdateWorkingDirectoryAndAvailableProjects(); From 6a2cb418bebe9c9176fe65243ff9ad058985066f Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Tue, 29 Sep 2020 12:45:33 -0700 Subject: [PATCH 07/59] Combine 2 events make less number of event (just 1) to emit. --- .../NuGet.Console/Xamls/ConsoleContainer.xaml.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index d6f236f41eb..c6ed6c4b7bc 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -85,17 +85,18 @@ public void Dispose() { var telemetryEvent = new TelemetryEvent("PackageManagerConsoleLoadCount", new Dictionary { - { "NugetPMCLoadCount", _windowLoadCount} + { "NuGetPMCLoadCount", _windowLoadCount}, + { "PackageManagerConsoleDefaultOpen", false} }); - TelemetryActivity.EmitTelemetryEvent(telemetryEvent); // Work around to detect if PMC loaded automatically because it was last focused window. if (IsLoaded) { - telemetryEvent = new TelemetryEvent("PackageManagerConsoleDefaultOpen"); - TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + telemetryEvent["PackageManagerConsoleDefaultOpen"] = true; } + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + _isTelemetryEmitted = true; } From a3945de2e9994b38ddec0f1ff5ed365b4d5dae9d Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Tue, 29 Sep 2020 13:35:14 -0700 Subject: [PATCH 08/59] Fix typos. --- .../NuGetConsole.Host.PowerShell/PowerShellHost.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 98276025c09..84eaf335e84 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -327,9 +327,9 @@ public void Initialize(IConsole console) { var telemetryEvent = new TelemetryEvent("PowerShellExecuteCommand", new Dictionary { - { "NugetPMCExecuteCommandCount", _pmcExecutedCount}, - { "NugetNonPMCExecuteCommandCount", _nonPmcExecutedCount}, - { "NugetTotalExecuteCommandCount", _pmcExecutedCount + _nonPmcExecutedCount} + { "NuGetPMCExecuteCommandCount", _pmcExecutedCount}, + { "NuGetNonPMCExecuteCommandCount", _nonPmcExecutedCount}, + { "NuGetTotalExecuteCommandCount", _pmcExecutedCount + _nonPmcExecutedCount} }); TelemetryActivity.EmitTelemetryEvent(telemetryEvent); _pmcExecutedCount = 0; @@ -504,7 +504,7 @@ private async Task ExecuteInitPs1Async(string installPath, PackageIdentity ident request.BuildInput(), outputResults: true); - var telemetryEvent = new TelemetryEvent(eventName: "PowershellScriptExecuted"); + var telemetryEvent = new TelemetryEvent(eventName: "PowerShellScriptExecuted"); telemetryEvent.AddPiiData("id", identity.Id?.ToLowerInvariant() ?? "(empty package id)"); telemetryEvent["version"] = identity.Version; TelemetryActivity.EmitTelemetryEvent(telemetryEvent); From 287c0c6228d3c7b8cb53922d253886f0008ff2a8 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Tue, 29 Sep 2020 15:11:47 -0700 Subject: [PATCH 09/59] Add actual origin which caused init.ps1 to run into telemetry. --- .../GlobalSuppressions.cs | 2 +- .../PowerShellHost.cs | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/GlobalSuppressions.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/GlobalSuppressions.cs index 87168dad383..0f1e4fcbd29 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/GlobalSuppressions.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/GlobalSuppressions.cs @@ -29,4 +29,4 @@ [assembly: SuppressMessage("Build", "CA1303:Method 'T TypeWrapper.GetInterface(object scriptValue, Type interfaceType, Func> getTypeWrapper)' passes a literal string as parameter 'message' of a call to 'ArgumentException.ArgumentException(string message, string paramName)'. Retrieve the following string(s) from a resource table instead: \"Invalid argument\".", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.TypeWrapper`1.GetInterface(System.Object,System.Type,System.Func{System.Object,NuGetConsole.Host.TypeWrapper`1})~`0")] [assembly: SuppressMessage("Build", "CA1507:Use nameof in place of string literal 'interfaceType'", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.TypeWrapper`1.GetInterface(System.Object,System.Type,System.Func{System.Object,NuGetConsole.Host.TypeWrapper`1})~`0")] [assembly: SuppressMessage("Build", "CA1062:In externally visible method 'T TypeWrapper.GetInterface(Type interfaceType)', validate parameter 'interfaceType' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.TypeWrapper`1.GetInterface(System.Type)~`0")] -[assembly: SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.PowerShell.Implementation.PowerShellHost.ExecuteInitPs1Async(System.String,NuGet.Packaging.Core.PackageIdentity)~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.PowerShell.Implementation.PowerShellHost.ExecuteInitPs1Async(System.String,NuGet.Packaging.Core.PackageIdentity,System.Int32)~System.Threading.Tasks.Task")] diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 84eaf335e84..adf4c97e568 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -314,7 +314,7 @@ public void Initialize(IConsole console) } UpdateWorkingDirectory(); - await ExecuteInitScriptsAsync(); + await ExecuteInitScriptsAsync(0); // check if PMC console is actually opened, then only hook to solution load events. if (console is IWpfConsole) @@ -329,8 +329,10 @@ public void Initialize(IConsole console) { { "NuGetPMCExecuteCommandCount", _pmcExecutedCount}, { "NuGetNonPMCExecuteCommandCount", _nonPmcExecutedCount}, - { "NuGetTotalExecuteCommandCount", _pmcExecutedCount + _nonPmcExecutedCount} + { "NuGetTotalExecuteCommandCount", _pmcExecutedCount + _nonPmcExecutedCount}, + { "isIWpfConsole", console is IWpfConsole} }); + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); _pmcExecutedCount = 0; _nonPmcExecutedCount = 0; @@ -396,7 +398,7 @@ private void HandleSolutionOpened() { if (await _solutionManager.Value.IsAllProjectsNominatedAsync()) { - await ExecuteInitScriptsAsync(); + await ExecuteInitScriptsAsync(1); break; } @@ -433,7 +435,7 @@ private void UpdateWorkingDirectory() } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We don't want execution of init scripts to crash our console.")] - private async Task ExecuteInitScriptsAsync() + private async Task ExecuteInitScriptsAsync(int origin) { // Fix for Bug 1426 Disallow ExecuteInitScripts from being executed concurrently by multiple threads. using (await _initScriptsLock.EnterAsync()) @@ -471,7 +473,7 @@ private async Task ExecuteInitScriptsAsync() foreach (var installedPackage in installedPackages) { - await ExecuteInitPs1Async(installedPackage.InstallPath, installedPackage.Identity); + await ExecuteInitPs1Async(installedPackage.InstallPath, installedPackage.Identity, origin); } // We are done executing scripts, so record the restore and solution directory that we executed for. @@ -481,7 +483,7 @@ private async Task ExecuteInitScriptsAsync() } } - private async Task ExecuteInitPs1Async(string installPath, PackageIdentity identity) + private async Task ExecuteInitPs1Async(string installPath, PackageIdentity identity, int origin) { try { @@ -507,6 +509,8 @@ private async Task ExecuteInitPs1Async(string installPath, PackageIdentity ident var telemetryEvent = new TelemetryEvent(eventName: "PowerShellScriptExecuted"); telemetryEvent.AddPiiData("id", identity.Id?.ToLowerInvariant() ?? "(empty package id)"); telemetryEvent["version"] = identity.Version; + telemetryEvent["origin"] = origin == 0 ? "Initialize" : origin == 1 ? "HandleSolutionOpened" : "Execute"; + telemetryEvent["isIWpfConsole"] = ActiveConsole is IWpfConsole; TelemetryActivity.EmitTelemetryEvent(telemetryEvent); return; @@ -567,7 +571,7 @@ public bool Execute(IConsole console, string command, params object[] inputs) // to run it once for each solution NuGetUIThreadHelper.JoinableTaskFactory.Run(async () => { - await ExecuteInitScriptsAsync(); + await ExecuteInitScriptsAsync(2); }); NuGetEventTrigger.Instance.TriggerEvent(NuGetEvent.PackageManagerConsoleCommandExecutionBegin); From a9d8ffda0ea32c011d81d70a0e723b6328b1e8f2 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Tue, 29 Sep 2020 15:59:48 -0700 Subject: [PATCH 10/59] Make names of telemetry event a constants. --- .../Xamls/ConsoleContainer.xaml.cs | 12 +++++++---- .../PowerShellHost.cs | 20 ++++++++++++------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index c6ed6c4b7bc..8d648ad6408 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -28,6 +28,10 @@ public sealed partial class ConsoleContainer : UserControl, IDisposable private int _windowLoadCount; private bool _isTelemetryEmitted; + private const string PackageManagerConsoleLoadCount = "PackageManagerConsoleLoadCount"; + private const string NuGetPMCLoadCount = "NuGetPMCLoadCount"; + private const string OpenAtStart = "OpenAtStart"; + public ConsoleContainer() { InitializeComponent(); @@ -83,16 +87,16 @@ public void Dispose() { if (!_isTelemetryEmitted) { - var telemetryEvent = new TelemetryEvent("PackageManagerConsoleLoadCount", new Dictionary + var telemetryEvent = new TelemetryEvent(PackageManagerConsoleLoadCount, new Dictionary { - { "NuGetPMCLoadCount", _windowLoadCount}, - { "PackageManagerConsoleDefaultOpen", false} + { NuGetPMCLoadCount, _windowLoadCount}, + { OpenAtStart, false} }); // Work around to detect if PMC loaded automatically because it was last focused window. if (IsLoaded) { - telemetryEvent["PackageManagerConsoleDefaultOpen"] = true; + telemetryEvent[OpenAtStart] = true; } TelemetryActivity.EmitTelemetryEvent(telemetryEvent); diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index adf4c97e568..74d469298ba 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -59,6 +59,12 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private const string DTEKey = "DTE"; private const string CancellationTokenKey = "CancellationTokenKey"; private const int ExecuteInitScriptsRetriesLimit = 50; + private const string PowerShellExecuteCommand = "PowerShellExecuteCommand"; + private const string NuGetPMCExecuteCommandCount = "NuGetPMCExecuteCommandCount"; + private const string NuGetNonPMCExecuteCommandCount = "NuGetNonPMCExecuteCommandCount"; + private const string NuGetTotalExecuteCommandCount = "NuGetTotalExecuteCommandCount"; + private const string IsIWpfConsole = "IsIWpfConsole"; + private const string PowerShellScriptExecuted = "PowerShellScriptExecuted"; private string _activePackageSource; private string[] _packageSources; private readonly Lazy _dte; @@ -325,12 +331,12 @@ public void Initialize(IConsole console) _solutionManager.Value.SolutionClosed += (o, e) => { - var telemetryEvent = new TelemetryEvent("PowerShellExecuteCommand", new Dictionary + var telemetryEvent = new TelemetryEvent(PowerShellExecuteCommand, new Dictionary { - { "NuGetPMCExecuteCommandCount", _pmcExecutedCount}, - { "NuGetNonPMCExecuteCommandCount", _nonPmcExecutedCount}, - { "NuGetTotalExecuteCommandCount", _pmcExecutedCount + _nonPmcExecutedCount}, - { "isIWpfConsole", console is IWpfConsole} + { NuGetPMCExecuteCommandCount, _pmcExecutedCount}, + { NuGetNonPMCExecuteCommandCount, _nonPmcExecutedCount}, + { NuGetTotalExecuteCommandCount, _pmcExecutedCount + _nonPmcExecutedCount}, + { IsIWpfConsole, console is IWpfConsole} }); TelemetryActivity.EmitTelemetryEvent(telemetryEvent); @@ -506,11 +512,11 @@ private async Task ExecuteInitPs1Async(string installPath, PackageIdentity ident request.BuildInput(), outputResults: true); - var telemetryEvent = new TelemetryEvent(eventName: "PowerShellScriptExecuted"); + var telemetryEvent = new TelemetryEvent(eventName: PowerShellScriptExecuted); telemetryEvent.AddPiiData("id", identity.Id?.ToLowerInvariant() ?? "(empty package id)"); telemetryEvent["version"] = identity.Version; telemetryEvent["origin"] = origin == 0 ? "Initialize" : origin == 1 ? "HandleSolutionOpened" : "Execute"; - telemetryEvent["isIWpfConsole"] = ActiveConsole is IWpfConsole; + telemetryEvent[IsIWpfConsole] = ActiveConsole is IWpfConsole; TelemetryActivity.EmitTelemetryEvent(telemetryEvent); return; From 2061923a2348422b941740a2e33433b47112e0b3 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Tue, 29 Sep 2020 16:01:44 -0700 Subject: [PATCH 11/59] Get donnie's advise for naming. --- .../NuGet.Console/Xamls/ConsoleContainer.xaml.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index 8d648ad6408..c325a4c7718 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -30,7 +30,7 @@ public sealed partial class ConsoleContainer : UserControl, IDisposable private const string PackageManagerConsoleLoadCount = "PackageManagerConsoleLoadCount"; private const string NuGetPMCLoadCount = "NuGetPMCLoadCount"; - private const string OpenAtStart = "OpenAtStart"; + private const string ReOpenAtStart = "ReOpenAtStart"; public ConsoleContainer() { @@ -90,13 +90,13 @@ public void Dispose() var telemetryEvent = new TelemetryEvent(PackageManagerConsoleLoadCount, new Dictionary { { NuGetPMCLoadCount, _windowLoadCount}, - { OpenAtStart, false} + { ReOpenAtStart, false} }); // Work around to detect if PMC loaded automatically because it was last focused window. if (IsLoaded) { - telemetryEvent[OpenAtStart] = true; + telemetryEvent[ReOpenAtStart] = true; } TelemetryActivity.EmitTelemetryEvent(telemetryEvent); From 57e8963a29cbb0aef0c5790820830c4e6e26b8c2 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Tue, 29 Sep 2020 21:45:35 -0700 Subject: [PATCH 12/59] Remove init.ps1 telemetry because current scenario doesn't capture initial run of install new package with init.ps1. It's bit complicated. --- .../GlobalSuppressions.cs | 2 +- .../PowerShellHost.cs | 20 ++++++------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/GlobalSuppressions.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/GlobalSuppressions.cs index 0f1e4fcbd29..87168dad383 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/GlobalSuppressions.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/GlobalSuppressions.cs @@ -29,4 +29,4 @@ [assembly: SuppressMessage("Build", "CA1303:Method 'T TypeWrapper.GetInterface(object scriptValue, Type interfaceType, Func> getTypeWrapper)' passes a literal string as parameter 'message' of a call to 'ArgumentException.ArgumentException(string message, string paramName)'. Retrieve the following string(s) from a resource table instead: \"Invalid argument\".", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.TypeWrapper`1.GetInterface(System.Object,System.Type,System.Func{System.Object,NuGetConsole.Host.TypeWrapper`1})~`0")] [assembly: SuppressMessage("Build", "CA1507:Use nameof in place of string literal 'interfaceType'", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.TypeWrapper`1.GetInterface(System.Object,System.Type,System.Func{System.Object,NuGetConsole.Host.TypeWrapper`1})~`0")] [assembly: SuppressMessage("Build", "CA1062:In externally visible method 'T TypeWrapper.GetInterface(Type interfaceType)', validate parameter 'interfaceType' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.TypeWrapper`1.GetInterface(System.Type)~`0")] -[assembly: SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.PowerShell.Implementation.PowerShellHost.ExecuteInitPs1Async(System.String,NuGet.Packaging.Core.PackageIdentity,System.Int32)~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.PowerShell.Implementation.PowerShellHost.ExecuteInitPs1Async(System.String,NuGet.Packaging.Core.PackageIdentity)~System.Threading.Tasks.Task")] diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 74d469298ba..20eb222ff90 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -64,7 +64,6 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private const string NuGetNonPMCExecuteCommandCount = "NuGetNonPMCExecuteCommandCount"; private const string NuGetTotalExecuteCommandCount = "NuGetTotalExecuteCommandCount"; private const string IsIWpfConsole = "IsIWpfConsole"; - private const string PowerShellScriptExecuted = "PowerShellScriptExecuted"; private string _activePackageSource; private string[] _packageSources; private readonly Lazy _dte; @@ -320,7 +319,7 @@ public void Initialize(IConsole console) } UpdateWorkingDirectory(); - await ExecuteInitScriptsAsync(0); + await ExecuteInitScriptsAsync(); // check if PMC console is actually opened, then only hook to solution load events. if (console is IWpfConsole) @@ -404,7 +403,7 @@ private void HandleSolutionOpened() { if (await _solutionManager.Value.IsAllProjectsNominatedAsync()) { - await ExecuteInitScriptsAsync(1); + await ExecuteInitScriptsAsync(); break; } @@ -441,7 +440,7 @@ private void UpdateWorkingDirectory() } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We don't want execution of init scripts to crash our console.")] - private async Task ExecuteInitScriptsAsync(int origin) + private async Task ExecuteInitScriptsAsync() { // Fix for Bug 1426 Disallow ExecuteInitScripts from being executed concurrently by multiple threads. using (await _initScriptsLock.EnterAsync()) @@ -479,7 +478,7 @@ private async Task ExecuteInitScriptsAsync(int origin) foreach (var installedPackage in installedPackages) { - await ExecuteInitPs1Async(installedPackage.InstallPath, installedPackage.Identity, origin); + await ExecuteInitPs1Async(installedPackage.InstallPath, installedPackage.Identity); } // We are done executing scripts, so record the restore and solution directory that we executed for. @@ -489,7 +488,7 @@ private async Task ExecuteInitScriptsAsync(int origin) } } - private async Task ExecuteInitPs1Async(string installPath, PackageIdentity identity, int origin) + private async Task ExecuteInitPs1Async(string installPath, PackageIdentity identity) { try { @@ -512,13 +511,6 @@ private async Task ExecuteInitPs1Async(string installPath, PackageIdentity ident request.BuildInput(), outputResults: true); - var telemetryEvent = new TelemetryEvent(eventName: PowerShellScriptExecuted); - telemetryEvent.AddPiiData("id", identity.Id?.ToLowerInvariant() ?? "(empty package id)"); - telemetryEvent["version"] = identity.Version; - telemetryEvent["origin"] = origin == 0 ? "Initialize" : origin == 1 ? "HandleSolutionOpened" : "Execute"; - telemetryEvent[IsIWpfConsole] = ActiveConsole is IWpfConsole; - TelemetryActivity.EmitTelemetryEvent(telemetryEvent); - return; } } @@ -577,7 +569,7 @@ public bool Execute(IConsole console, string command, params object[] inputs) // to run it once for each solution NuGetUIThreadHelper.JoinableTaskFactory.Run(async () => { - await ExecuteInitScriptsAsync(2); + await ExecuteInitScriptsAsync(); }); NuGetEventTrigger.Instance.TriggerEvent(NuGetEvent.PackageManagerConsoleCommandExecutionBegin); From e7e032b7f3a2eae44aa2c4c9a443e387cf723cc0 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Wed, 30 Sep 2020 10:11:22 -0700 Subject: [PATCH 13/59] Address Andy's code review comment. --- .../NuGet.Console/Xamls/ConsoleContainer.xaml.cs | 10 +++------- .../NuGetConsole.Host.PowerShell/PowerShellHost.cs | 7 ++++++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index c325a4c7718..f6c87527325 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -87,18 +87,14 @@ public void Dispose() { if (!_isTelemetryEmitted) { + // Work around to detect if PMC loaded automatically because it was last focused window. + var reopenAtStart = IsLoaded; var telemetryEvent = new TelemetryEvent(PackageManagerConsoleLoadCount, new Dictionary { { NuGetPMCLoadCount, _windowLoadCount}, - { ReOpenAtStart, false} + { ReOpenAtStart, reopenAtStart} }); - // Work around to detect if PMC loaded automatically because it was last focused window. - if (IsLoaded) - { - telemetryEvent[ReOpenAtStart] = true; - } - TelemetryActivity.EmitTelemetryEvent(telemetryEvent); _isTelemetryEmitted = true; diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 20eb222ff90..ec1701318c7 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -64,11 +64,13 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private const string NuGetNonPMCExecuteCommandCount = "NuGetNonPMCExecuteCommandCount"; private const string NuGetTotalExecuteCommandCount = "NuGetTotalExecuteCommandCount"; private const string IsIWpfConsole = "IsIWpfConsole"; + private const string RunningDuration = "RunningDuration"; private string _activePackageSource; private string[] _packageSources; private readonly Lazy _dte; private int _pmcExecutedCount; private int _nonPmcExecutedCount; + private DateTime _startRunningTime; private uint _solutionExistsCookie; @@ -122,6 +124,8 @@ protected PowerShellHost(string name, IRestoreEvents restoreEvents, IRunspaceMan () => ServiceLocator.GetInstanceSafe()); _commonOperations = new Lazy(() => ServiceLocator.GetInstanceSafe()); + _startRunningTime = DateTime.UtcNow; + _name = name; IsCommandEnabled = true; @@ -335,7 +339,8 @@ public void Initialize(IConsole console) { NuGetPMCExecuteCommandCount, _pmcExecutedCount}, { NuGetNonPMCExecuteCommandCount, _nonPmcExecutedCount}, { NuGetTotalExecuteCommandCount, _pmcExecutedCount + _nonPmcExecutedCount}, - { IsIWpfConsole, console is IWpfConsole} + { IsIWpfConsole, console is IWpfConsole}, + { RunningDuration, (DateTime.UtcNow - _startRunningTime).TotalSeconds} }); TelemetryActivity.EmitTelemetryEvent(telemetryEvent); From 22300d44c0773d4c4b3c34a40550287420ff2174 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Wed, 30 Sep 2020 11:26:30 -0700 Subject: [PATCH 14/59] Remove unused variable. --- .../NuGetConsole.Host.PowerShell/PowerShellHost.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index ec1701318c7..20eb222ff90 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -64,13 +64,11 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private const string NuGetNonPMCExecuteCommandCount = "NuGetNonPMCExecuteCommandCount"; private const string NuGetTotalExecuteCommandCount = "NuGetTotalExecuteCommandCount"; private const string IsIWpfConsole = "IsIWpfConsole"; - private const string RunningDuration = "RunningDuration"; private string _activePackageSource; private string[] _packageSources; private readonly Lazy _dte; private int _pmcExecutedCount; private int _nonPmcExecutedCount; - private DateTime _startRunningTime; private uint _solutionExistsCookie; @@ -124,8 +122,6 @@ protected PowerShellHost(string name, IRestoreEvents restoreEvents, IRunspaceMan () => ServiceLocator.GetInstanceSafe()); _commonOperations = new Lazy(() => ServiceLocator.GetInstanceSafe()); - _startRunningTime = DateTime.UtcNow; - _name = name; IsCommandEnabled = true; @@ -339,8 +335,7 @@ public void Initialize(IConsole console) { NuGetPMCExecuteCommandCount, _pmcExecutedCount}, { NuGetNonPMCExecuteCommandCount, _nonPmcExecutedCount}, { NuGetTotalExecuteCommandCount, _pmcExecutedCount + _nonPmcExecutedCount}, - { IsIWpfConsole, console is IWpfConsole}, - { RunningDuration, (DateTime.UtcNow - _startRunningTime).TotalSeconds} + { IsIWpfConsole, console is IWpfConsole} }); TelemetryActivity.EmitTelemetryEvent(telemetryEvent); From 9127d0ad655ed9bf782b6e2b5a82373009ef25b2 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Wed, 30 Sep 2020 13:29:16 -0700 Subject: [PATCH 15/59] Send single event for both PMUI and PMC powershell events. --- .../PowerShellHost.cs | 89 +++++++++++++++---- 1 file changed, 72 insertions(+), 17 deletions(-) diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 20eb222ff90..533cad62574 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -38,6 +38,11 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private static readonly string AggregateSourceName = Resources.AggregateSourceName; private static readonly TimeSpan ExecuteInitScriptsRetryDelay = TimeSpan.FromMilliseconds(400); private static readonly int MaxTasks = 16; + private readonly static object TelemetryLock = new object(); + private static bool IsTelemetryEmitted; + private static int PmcExecutedCount; + private static int NonPmcExecutedCount; + private static byte PowerShellHostInstances; private Microsoft.VisualStudio.Threading.AsyncLazy _vsMonitorSelection; private IVsMonitorSelection VsMonitorSelection => ThreadHelper.JoinableTaskFactory.Run(_vsMonitorSelection.GetValueAsync); @@ -63,12 +68,13 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private const string NuGetPMCExecuteCommandCount = "NuGetPMCExecuteCommandCount"; private const string NuGetNonPMCExecuteCommandCount = "NuGetNonPMCExecuteCommandCount"; private const string NuGetTotalExecuteCommandCount = "NuGetTotalExecuteCommandCount"; - private const string IsIWpfConsole = "IsIWpfConsole"; + private const string LoadedFromPMC = "LoadedFromPMC"; + private const string FirstTimeLoadedFromPMC = "FirstTimeLoadedFromPMC"; + private const string LoadedFromPMUI = "LoadedFromPMUI"; + private const string FirstTimeLoadedFromPMUI = "FirstTimeLoadedFromPMUI"; private string _activePackageSource; private string[] _packageSources; private readonly Lazy _dte; - private int _pmcExecutedCount; - private int _nonPmcExecutedCount; private uint _solutionExistsCookie; @@ -330,17 +336,28 @@ public void Initialize(IConsole console) _solutionManager.Value.SolutionClosed += (o, e) => { - var telemetryEvent = new TelemetryEvent(PowerShellExecuteCommand, new Dictionary + lock (TelemetryLock) + { + if (!IsTelemetryEmitted) { - { NuGetPMCExecuteCommandCount, _pmcExecutedCount}, - { NuGetNonPMCExecuteCommandCount, _nonPmcExecutedCount}, - { NuGetTotalExecuteCommandCount, _pmcExecutedCount + _nonPmcExecutedCount}, - { IsIWpfConsole, console is IWpfConsole} - }); - - TelemetryActivity.EmitTelemetryEvent(telemetryEvent); - _pmcExecutedCount = 0; - _nonPmcExecutedCount = 0; + var telemetryEvent = new TelemetryEvent(PowerShellExecuteCommand, new Dictionary + { + { NuGetPMCExecuteCommandCount, PmcExecutedCount}, + { NuGetNonPMCExecuteCommandCount, NonPmcExecutedCount}, + { NuGetTotalExecuteCommandCount, PmcExecutedCount + NonPmcExecutedCount}, + { LoadedFromPMC, (PowerShellHostInstances & 0b00000001) == 0b00000001}, + { LoadedFromPMUI, (PowerShellHostInstances & 0b00000010) == 0b00000010}, + { FirstTimeLoadedFromPMC, (PowerShellHostInstances & 0b00000100) == 0b00000100}, + { FirstTimeLoadedFromPMUI, (PowerShellHostInstances & 0b00001000) == 0b00001000} + }); + + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + + PmcExecutedCount = 0; + NonPmcExecutedCount = 0; + IsTelemetryEmitted = true; + } + } if (console is IWpfConsole) { @@ -369,6 +386,31 @@ public void Initialize(IConsole console) SetPrivateDataOnHost(false); StartAsyncDefaultProjectUpdate(); + + lock (TelemetryLock) + { + //There is edge case where PMC is opened but user doesn't execute any command on it. + if (console is IWpfConsole) + { + if ((PowerShellHostInstances & 0b00000001) == 0) + { + // First time load PMC + PowerShellHostInstances |= 0b00000100; + } + + PowerShellHostInstances |= 0b00000001; + } + else + { + if ((PowerShellHostInstances & 0b00000010) == 0) + { + // First time load from PMUI + PowerShellHostInstances |= 0b00001000; + } + + PowerShellHostInstances |= 0b00000010; + } + } } catch (Exception ex) { @@ -387,8 +429,15 @@ private void HandleSolutionOpened() { _scriptExecutor.Value.Reset(); - _pmcExecutedCount = 0; - _nonPmcExecutedCount = 0; + lock (TelemetryLock) + { + PmcExecutedCount = 0; + NonPmcExecutedCount = 0; + IsTelemetryEmitted = false; + + //Reset first time load powershell flag bits, but keep other 2 flags for origin of powershell load. + PowerShellHostInstances &= 0b00000011; + } // Solution opened event is raised on the UI thread // Go off the UI thread before calling likely expensive call of ExecuteInitScriptsAsync @@ -558,11 +607,17 @@ public bool Execute(IConsole console, string command, params object[] inputs) // Command can come here from both PMC and PM UI. if (console is IWpfConsole) { - _pmcExecutedCount++; + lock (TelemetryLock) + { + PmcExecutedCount++; + } } else { - _nonPmcExecutedCount++; + lock (TelemetryLock) + { + NonPmcExecutedCount++; + } } // since install.ps1/uninstall.ps1 could depend on init scripts, so we need to make sure From be473af48a465ae70f907f8202dfc0785eb9280f Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Mon, 2 Nov 2020 21:06:59 -0800 Subject: [PATCH 16/59] Make powershell telemetry works for with and without solution load. --- .../NuGet.Console/WpfConsole/WpfConsole.cs | 6 + .../Xamls/ConsoleContainer.xaml.cs | 46 ++++- .../VSSettings.cs | 186 ++++++++++++++++-- .../NuGetConsole.Host.PowerShell.csproj | 12 +- .../PowerShellHost.cs | 84 +++++--- .../PublicAPI/net45/PublicAPI.Unshipped.txt | 2 + .../PublicAPI/net472/PublicAPI.Unshipped.txt | 2 + .../netstandard2.0/PublicAPI.Unshipped.txt | 2 + .../Telemetry/INuGetSolutionTelemetry.cs | 13 ++ 9 files changed, 300 insertions(+), 53 deletions(-) create mode 100644 src/NuGet.Core/NuGet.Common/Telemetry/INuGetSolutionTelemetry.cs diff --git a/src/NuGet.Clients/NuGet.Console/WpfConsole/WpfConsole.cs b/src/NuGet.Clients/NuGet.Console/WpfConsole/WpfConsole.cs index 6d6ccdbf5df..414e5431d6d 100644 --- a/src/NuGet.Clients/NuGet.Console/WpfConsole/WpfConsole.cs +++ b/src/NuGet.Clients/NuGet.Console/WpfConsole/WpfConsole.cs @@ -733,6 +733,12 @@ protected virtual void Dispose(bool disposing) { disposable.Dispose(); } + + var host = Host as IDisposable; + if (host != null) + { + host.Dispose(); + } } } diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index f6c87527325..07e9758a328 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -11,8 +11,10 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using NuGet.Common; +using NuGet.Common.Telemetry; using NuGet.PackageManagement; using NuGet.PackageManagement.UI; +using NuGet.PackageManagement.VisualStudio; using NuGet.VisualStudio; using NuGet.VisualStudio.Common; using NuGet.VisualStudio.Internal.Contracts; @@ -25,11 +27,13 @@ namespace NuGetConsole public sealed partial class ConsoleContainer : UserControl, IDisposable { private INuGetSolutionManagerService _solutionManager; + private IVsSolutionManager _iVsSolutionManager; + private INuGetSolutionTelemetry _nugetSolutionTelemetry; private int _windowLoadCount; private bool _isTelemetryEmitted; - private const string PackageManagerConsoleLoadCount = "PackageManagerConsoleLoadCount"; - private const string NuGetPMCLoadCount = "NuGetPMCLoadCount"; + private const string PackageManagerConsoleWindowsLoad = "PackageManagerConsoleWindowsLoad"; + private const string NuGetPMCWindowLoadCount = "NuGetPMCWindowLoadCount"; private const string ReOpenAtStart = "ReOpenAtStart"; public ConsoleContainer() @@ -52,10 +56,31 @@ await System.Threading.Tasks.Task.Run( Assumes.NotNull(_solutionManager); + _iVsSolutionManager = await ServiceLocator.GetInstanceAsync(); + Assumes.NotNull(_iVsSolutionManager); + + // Hook up solution events + _iVsSolutionManager.SolutionOpening += (_, __) => + { + // PMC used before any solution is loaded, let's emit what we have before loading a solution. + // Please note: Here _windowLoadCount is already 1 if PMC reopen since it was last active window last time VS instance close. + if (_windowLoadCount > 0) + { + EmitPowershellUsageTelemetry(); + } + + //_isTelemetryEmitted = false; + }; + _iVsSolutionManager.SolutionClosing += (o, e) => + { + EmitPowershellUsageTelemetry(); + }; + var productUpdateService = ServiceLocator.GetInstance(); var packageRestoreManager = ServiceLocator.GetInstance(); var deleteOnRestartManager = ServiceLocator.GetInstance(); var shell = ServiceLocator.GetGlobalService(); + _nugetSolutionTelemetry = ServiceLocator.GetInstanceSafe(); await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -89,13 +114,13 @@ public void Dispose() { // Work around to detect if PMC loaded automatically because it was last focused window. var reopenAtStart = IsLoaded; - var telemetryEvent = new TelemetryEvent(PackageManagerConsoleLoadCount, new Dictionary + var telemetryEvent = new TelemetryEvent(PackageManagerConsoleWindowsLoad, new Dictionary { - { NuGetPMCLoadCount, _windowLoadCount}, + { NuGetPMCWindowLoadCount, _windowLoadCount}, // Usefull if PMC used without any solution load at all then VS instance closed. { ReOpenAtStart, reopenAtStart} }); - TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + _nugetSolutionTelemetry.AddSolutionTelemetryEvent(telemetryEvent); _isTelemetryEmitted = true; } @@ -115,5 +140,16 @@ void ConsoleContainer_Loaded(object sender, RoutedEventArgs e) { _windowLoadCount++; } + + private void EmitPowershellUsageTelemetry() + { + var telemetryEvent = new TelemetryEvent(PackageManagerConsoleWindowsLoad, new Dictionary + { + { NuGetPMCWindowLoadCount, _windowLoadCount}, + }); + _nugetSolutionTelemetry.AddSolutionTelemetryEvent(telemetryEvent); + + _windowLoadCount = 0; + } } } diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs index a495beaa98e..35660227a9e 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs @@ -5,21 +5,46 @@ using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; -using System.Threading.Tasks; -using Microsoft.VisualStudio.Threading; +using System.Linq; +using NuGet.Common.Telemetry; using NuGet.Configuration; using NuGet.VisualStudio; namespace NuGet.PackageManagement.VisualStudio { [Export(typeof(ISettings))] + [Export(typeof(INuGetSolutionTelemetry))] [PartCreationPolicy(CreationPolicy.Shared)] - public sealed class VSSettings : ISettings, IDisposable + public sealed class VSSettings : ISettings, INuGetSolutionTelemetry, IDisposable { private const string NuGetSolutionSettingsFolder = ".nuget"; - // to initialize SolutionSettings first time outside MEF constructor - private Tuple> _solutionSettings; + private Tuple> _solutionSettings; + // PMC, PMUI powershell telemetry consts + private const string NugetPowershellPrefix = "NugetPowershell."; // Using prefix prevent accidental same name property collission from different type telemetry. + private const string NugetVSSolutionClose = "NugetVSSolutionClose"; + private const string NugetVSInstanceClose = "NugetVSInstanceClose"; + private const string PowerShellExecuteCommand = "PowerShellExecuteCommand"; + private const string NuGetPMCExecuteCommandCount = "NuGetPMCExecuteCommandCount"; + private const string NuGetNonPMCExecuteCommandCount = "NuGetNonPMCExecuteCommandCount"; + private const string LoadedFromPMC = "LoadedFromPMC"; + private const string FirstTimeLoadedFromPMC = "FirstTimeLoadedFromPMC"; + private const string LoadedFromPMUI = "LoadedFromPMUI"; + private const string FirstTimeLoadedFromPMUI = "FirstTimeLoadedFromPMUI"; + // PMC UI Console Container telemetry consts + private const string NuGetPMCWindowLoadCount = "NuGetPMCWindowLoadCount"; + private const string ReOpenAtStart = "ReOpenAtStart"; + // _solutionTelemetryEvents hold telemetry for current VS solution session. + private List _vsSolutionTelemetryEvents; + private Dictionary _vsSolutionTelemetryEmitQueue; + // _vsInstanceTelemetryEvents hold telemetry for current VS instance session. + private List _vsInstanceTelemetryEvents; + private Dictionary _vsInstanceTelemetryEmitQueue; + private const string SolutionCount = "SolutionCount"; + private const string PMCPowershellLoadedSolutionCount = "PMCPowershellLoadedSolutionCount"; + private const string PMUIPowershellLoadedSolutionCount = "PMUIPowershellLoadedSolutionCount"; + private const string SolutionLoaded = "SolutionLoaded"; + private int _solutionCount; private ISettings SolutionSettings { @@ -51,8 +76,13 @@ public VSSettings(ISolutionManager solutionManager, IMachineWideSettings machine { SolutionManager = solutionManager ?? throw new ArgumentNullException(nameof(solutionManager)); MachineWideSettings = machineWideSettings; - SolutionManager.SolutionOpening += OnSolutionOpenedOrClosed; - SolutionManager.SolutionClosed += OnSolutionOpenedOrClosed; + SolutionManager.SolutionOpening += OnSolutionOpening; + SolutionManager.SolutionOpened += OnSolutionOpened; + SolutionManager.SolutionClosed += OnSolutionClosed; + _vsSolutionTelemetryEvents = new List(); + _vsSolutionTelemetryEmitQueue = new Dictionary(); + _vsInstanceTelemetryEvents = new List(); + _vsInstanceTelemetryEmitQueue = new Dictionary(); } private bool ResetSolutionSettingsIfNeeded() @@ -77,9 +107,9 @@ private bool ResetSolutionSettingsIfNeeded() // That however is not the case for solution close and same session close -> open events. Those will be on the UI thread. if (_solutionSettings == null || !string.Equals(root, _solutionSettings.Item1)) { - _solutionSettings = new Tuple>( + _solutionSettings = new Tuple>( item1: root, - item2: new AsyncLazy(async () => + item2: new Microsoft.VisualStudio.Threading.AsyncLazy(async () => { ISettings settings = null; try @@ -103,6 +133,11 @@ private bool ResetSolutionSettingsIfNeeded() } private void OnSolutionOpenedOrClosed(object sender, EventArgs e) + { + DetectSolutionSettingChange(); + } + + private void DetectSolutionSettingChange() { var hasChanged = ResetSolutionSettingsIfNeeded(); @@ -112,6 +147,28 @@ private void OnSolutionOpenedOrClosed(object sender, EventArgs e) } } + private void OnSolutionOpening(object sender, EventArgs e) + { + DetectSolutionSettingChange(); + } + + private void OnSolutionOpened(object sender, EventArgs e) + { + // PMC used before any solution is loaded, let's emit what we have before loading a solution. + if (_solutionCount == 0 && _vsSolutionTelemetryEvents.Any(e => e[NuGetPMCWindowLoadCount] is int && (int)e[NuGetPMCWindowLoadCount] > 0)) + { + EmitVSSolutionTelemetry(); + } + + _solutionCount++; + } + + private void OnSolutionClosed(object sender, EventArgs e) + { + EmitVSSolutionTelemetry(); + DetectSolutionSettingChange(); + } + public SettingSection GetSection(string sectionName) { return SolutionSettings.GetSection(sectionName); @@ -147,11 +204,118 @@ public void SaveToDisk() public void Dispose() { - SolutionManager.SolutionOpening -= OnSolutionOpenedOrClosed; - SolutionManager.SolutionClosed -= OnSolutionOpenedOrClosed; + SolutionManager.SolutionOpening -= OnSolutionOpening; + SolutionManager.SolutionOpened -= OnSolutionOpened; + SolutionManager.SolutionClosed -= OnSolutionClosed; + + EmitVSInstanceTelemetry(); + } + + public void AddSolutionTelemetryEvent(Common.TelemetryEvent telemetryData) + { + _vsSolutionTelemetryEvents.Add(telemetryData); + _vsInstanceTelemetryEvents.Add(telemetryData); } // The value for SolutionSettings can't possibly be null, but it could be a read-only instance private bool CanChangeSettings => !ReferenceEquals(SolutionSettings, NullSettings.Instance); + + // Emit VS solution session telemetry when solution is closed. + private void EmitVSSolutionTelemetry() + { + try + { + // Queue all different types of telemetries and do some processing prior to emit. + EnqueueVSSolutionPowershellTelemetry(); + + // Add other telemetry types here in the future. You can emit many different types of telemetry other than powershell here. + // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. + // Using prefix avoid collision of property names from different types of telemetry. + + _vsSolutionTelemetryEvents.Clear(); + + // Actual emit + CombineAndEmitTelemetry(_vsSolutionTelemetryEmitQueue, NugetVSSolutionClose); + _vsSolutionTelemetryEmitQueue.Clear(); + } + catch (Exception) + {} + } + + private void EnqueueVSSolutionPowershellTelemetry() + { + var vsSolutionPowershellTelemetry = _vsSolutionTelemetryEvents.FirstOrDefault(e => e.Name == PowerShellExecuteCommand); + + // If powershell(PMC/PMUI) is not loaded at all then we need to create default telemetry event which will be emitted. + if (vsSolutionPowershellTelemetry == null) + { + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, 0); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetNonPMCExecuteCommandCount, 0); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMC, false); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMUI, false); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMC, false); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMUI, false); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + SolutionLoaded, _solutionCount > 0); // If 'false' : PMC used before any solution is loaded. + } + else + { + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount]); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetNonPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetNonPMCExecuteCommandCount]); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMC, vsSolutionPowershellTelemetry[LoadedFromPMC]); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMUI, vsSolutionPowershellTelemetry[LoadedFromPMUI]); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMC, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMC]); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMUI, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMUI]); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + SolutionLoaded, _solutionCount > 0); // If 'false' : PMC used before any solution is loaded. + } + + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCWindowLoadCount, _vsSolutionTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); + } + + // Emit VS solution session telemetry when VS instance is closed. + private void EmitVSInstanceTelemetry() + { + try + { + EnqueueVSInstancePowershellTelemetry(); + // Add other telemetry types here in the future. You can emit many different types of telemetry here. + // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. + // Using prefix avoid collision of property names from different types of telemetry. + + CombineAndEmitTelemetry(_vsInstanceTelemetryEmitQueue, NugetVSInstanceClose); + } + catch (Exception) + {} + } + + private void EnqueueVSInstancePowershellTelemetry() + { + // Edge case. PMC can be used without loading VS solution at all. + if (_vsSolutionTelemetryEvents.Any(e => e.Name == PowerShellExecuteCommand)) + { + EmitVSSolutionTelemetry(); + } + + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + ReOpenAtStart, _vsInstanceTelemetryEvents.Where(e => e[ReOpenAtStart] != null).Any()); // Whether PMC window re-open at start by default next time VS open? + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCWindowLoadCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); // PMC Window load count + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMCExecuteCommandCount])); // PMC number of commands executed. + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetNonPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetNonPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetNonPMCExecuteCommandCount])); // PMUI number of powershell commands executed. + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + SolutionCount, _solutionCount); + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + PMCPowershellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[SolutionLoaded] is bool && (bool)e[SolutionLoaded] && e[LoadedFromPMC] is bool).Count(e => (bool)e[LoadedFromPMC] == true)); // SolutionLoaded used here to remove edge case : PMC used before any solution is loaded + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + PMUIPowershellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[LoadedFromPMUI] is bool).Count(e => (bool)e[LoadedFromPMUI] == true)); + } + + // Instead of emitting one by one we combine them into single event and each event is a property of this single event. + // Currently we emit vs.nuget.NugetVSSolutionClose, vs.nuget.NugetVSInstanceClose events. + private void CombineAndEmitTelemetry(Dictionary telemetryEvents, string telemetryType) + { + var vsSolutionCloseTelemetry = new Common.TelemetryEvent(telemetryType, new Dictionary()); + + foreach (KeyValuePair telemetryEvent in telemetryEvents) + { + vsSolutionCloseTelemetry[telemetryEvent.Key] = telemetryEvent.Value; + } + + Common.TelemetryActivity.EmitTelemetryEvent(vsSolutionCloseTelemetry); + } } } diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj index f1bc5aeccb3..466dc4ef1a7 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj @@ -1,4 +1,4 @@ - + @@ -128,6 +128,10 @@ {26dc17ac-a390-4515-a2c0-07a0950036c5} NuGet.PackageManagement.PowerShellCmdlets + + {306cddfa-ff0b-4299-930c-9ec6c9308160} + NuGet.PackageManagement.VisualStudio + {eea49a74-6efc-410e-9745-bad367ac151d} NuGet.VisualStudio.Common @@ -154,13 +158,11 @@ $(LocTargets);GetPowerShellCmdletsHelpFile GetLocalizedPowerShellCmdletHelpFile - - + @@ -212,4 +214,4 @@ - + \ No newline at end of file diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 533cad62574..41a7bcead1a 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -21,6 +21,7 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; using NuGet.Common; +using NuGet.Common.Telemetry; using NuGet.Configuration; using NuGet.PackageManagement; using NuGet.PackageManagement.VisualStudio; @@ -56,6 +57,7 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private readonly Lazy _settings; private readonly Lazy _sourceControlManagerProvider; private readonly Lazy _commonOperations; + private readonly Lazy _nugetSolutionTelemetry; private readonly Lazy _deleteOnRestartManager; private readonly Lazy _scriptExecutor; private const string ActivePackageSourceKey = "activePackageSource"; @@ -67,11 +69,11 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private const string PowerShellExecuteCommand = "PowerShellExecuteCommand"; private const string NuGetPMCExecuteCommandCount = "NuGetPMCExecuteCommandCount"; private const string NuGetNonPMCExecuteCommandCount = "NuGetNonPMCExecuteCommandCount"; - private const string NuGetTotalExecuteCommandCount = "NuGetTotalExecuteCommandCount"; private const string LoadedFromPMC = "LoadedFromPMC"; private const string FirstTimeLoadedFromPMC = "FirstTimeLoadedFromPMC"; private const string LoadedFromPMUI = "LoadedFromPMUI"; private const string FirstTimeLoadedFromPMUI = "FirstTimeLoadedFromPMUI"; + private const string SolutionLoaded = "SolutionLoaded"; private string _activePackageSource; private string[] _packageSources; private readonly Lazy _dte; @@ -127,7 +129,7 @@ protected PowerShellHost(string name, IRestoreEvents restoreEvents, IRunspaceMan _sourceControlManagerProvider = new Lazy( () => ServiceLocator.GetInstanceSafe()); _commonOperations = new Lazy(() => ServiceLocator.GetInstanceSafe()); - + _nugetSolutionTelemetry = new Lazy(() => ServiceLocator.GetInstanceSafe()); _name = name; IsCommandEnabled = true; @@ -334,31 +336,22 @@ public void Initialize(IConsole console) _solutionManager.Value.SolutionOpened += (_, __) => HandleSolutionOpened(); } - _solutionManager.Value.SolutionClosed += (o, e) => + _solutionManager.Value.SolutionOpening += (o, e) => { - lock (TelemetryLock) + if (PmcExecutedCount > 0) { - if (!IsTelemetryEmitted) - { - var telemetryEvent = new TelemetryEvent(PowerShellExecuteCommand, new Dictionary - { - { NuGetPMCExecuteCommandCount, PmcExecutedCount}, - { NuGetNonPMCExecuteCommandCount, NonPmcExecutedCount}, - { NuGetTotalExecuteCommandCount, PmcExecutedCount + NonPmcExecutedCount}, - { LoadedFromPMC, (PowerShellHostInstances & 0b00000001) == 0b00000001}, - { LoadedFromPMUI, (PowerShellHostInstances & 0b00000010) == 0b00000010}, - { FirstTimeLoadedFromPMC, (PowerShellHostInstances & 0b00000100) == 0b00000100}, - { FirstTimeLoadedFromPMUI, (PowerShellHostInstances & 0b00001000) == 0b00001000} - }); - - TelemetryActivity.EmitTelemetryEvent(telemetryEvent); - - PmcExecutedCount = 0; - NonPmcExecutedCount = 0; - IsTelemetryEmitted = true; - } + // PMC used before any solution is loaded, let's emit what we have before loading a solution. + EmitPowershellUsageTelemetry(false); } + }; + + _solutionManager.Value.SolutionClosing += (o, e) => + { + EmitPowershellUsageTelemetry(true); + }; + _solutionManager.Value.SolutionClosed += (o, e) => + { if (console is IWpfConsole) { UpdateWorkingDirectory(); @@ -398,6 +391,7 @@ public void Initialize(IConsole console) PowerShellHostInstances |= 0b00000100; } + // LoadedFromPMC PowerShellHostInstances |= 0b00000001; } else @@ -408,6 +402,7 @@ public void Initialize(IConsole console) PowerShellHostInstances |= 0b00001000; } + // LoadedFromPMUI PowerShellHostInstances |= 0b00000010; } } @@ -429,15 +424,9 @@ private void HandleSolutionOpened() { _scriptExecutor.Value.Reset(); - lock (TelemetryLock) - { - PmcExecutedCount = 0; - NonPmcExecutedCount = 0; - IsTelemetryEmitted = false; - - //Reset first time load powershell flag bits, but keep other 2 flags for origin of powershell load. - PowerShellHostInstances &= 0b00000011; - } + PmcExecutedCount = 0; + NonPmcExecutedCount = 0; + IsTelemetryEmitted = false; // Solution opened event is raised on the UI thread // Go off the UI thread before calling likely expensive call of ExecuteInitScriptsAsync @@ -919,6 +908,34 @@ private async Task> CompleteTaskAsync(List + { + { NuGetPMCExecuteCommandCount, PmcExecutedCount}, + { NuGetNonPMCExecuteCommandCount, NonPmcExecutedCount}, + { LoadedFromPMC, (PowerShellHostInstances & 0b00000001) == 0b00000001}, + { LoadedFromPMUI, (PowerShellHostInstances & 0b00000010) == 0b00000010}, + { FirstTimeLoadedFromPMC, (PowerShellHostInstances & 0b00000100) == 0b00000100}, + { FirstTimeLoadedFromPMUI, (PowerShellHostInstances & 0b00001000) == 0b00001000}, + { SolutionLoaded, withSolution} + }); + _nugetSolutionTelemetry.Value.AddSolutionTelemetryEvent(telemetryEvent); + + PmcExecutedCount = 0; + NonPmcExecutedCount = 0; + IsTelemetryEmitted = true; + + //Reset first time load powershell flag bits, but keep other 2 flags for origin of powershell load. + PowerShellHostInstances &= 0b00000011; + } + } + } + #region ITabExpansion public Task GetExpansionsAsync(string line, string lastWord, CancellationToken token) @@ -1003,6 +1020,9 @@ public void Dispose() _restoreEvents.SolutionRestoreCompleted -= RestoreEvents_SolutionRestoreCompleted; _initScriptsLock.Dispose(); Runspace?.Dispose(); + + // Below emits telemetry in there was no solution was loaded at all, but if there were any solution then this one internally ignored because it's already emitted with solution SolutionClosing event. + EmitPowershellUsageTelemetry(false); } #endregion diff --git a/src/NuGet.Core/NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt index 27e57301d51..f7bb5f81732 100644 --- a/src/NuGet.Core/NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt @@ -2,3 +2,5 @@ NuGet.Common.NuGetLogCode.NU1010 = 1010 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1011 = 1011 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU5501 = 5501 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1012 = 1012 -> NuGet.Common.NuGetLogCode +NuGet.Common.Telemetry.INuGetSolutionTelemetry +NuGet.Common.Telemetry.INuGetSolutionTelemetry.AddSolutionTelemetryEvent(NuGet.Common.TelemetryEvent telemetryData) -> void diff --git a/src/NuGet.Core/NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt index 27e57301d51..f7bb5f81732 100644 --- a/src/NuGet.Core/NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt @@ -2,3 +2,5 @@ NuGet.Common.NuGetLogCode.NU1010 = 1010 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1011 = 1011 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU5501 = 5501 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1012 = 1012 -> NuGet.Common.NuGetLogCode +NuGet.Common.Telemetry.INuGetSolutionTelemetry +NuGet.Common.Telemetry.INuGetSolutionTelemetry.AddSolutionTelemetryEvent(NuGet.Common.TelemetryEvent telemetryData) -> void diff --git a/src/NuGet.Core/NuGet.Common/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Common/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt index 27e57301d51..f7bb5f81732 100644 --- a/src/NuGet.Core/NuGet.Common/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Common/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -2,3 +2,5 @@ NuGet.Common.NuGetLogCode.NU1010 = 1010 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1011 = 1011 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU5501 = 5501 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1012 = 1012 -> NuGet.Common.NuGetLogCode +NuGet.Common.Telemetry.INuGetSolutionTelemetry +NuGet.Common.Telemetry.INuGetSolutionTelemetry.AddSolutionTelemetryEvent(NuGet.Common.TelemetryEvent telemetryData) -> void diff --git a/src/NuGet.Core/NuGet.Common/Telemetry/INuGetSolutionTelemetry.cs b/src/NuGet.Core/NuGet.Common/Telemetry/INuGetSolutionTelemetry.cs new file mode 100644 index 00000000000..31eae9020fc --- /dev/null +++ b/src/NuGet.Core/NuGet.Common/Telemetry/INuGetSolutionTelemetry.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace NuGet.Common.Telemetry +{ + // This one used for emitting aggregated telemetry at VS solution close or VS instance close. + public interface INuGetSolutionTelemetry + { + /// Send a to telemetry. + /// Telemetry event to send. + void AddSolutionTelemetryEvent(TelemetryEvent telemetryData); + } +} From 8190d667070914f9b618981e555b4f54507cdd80 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Wed, 4 Nov 2020 11:23:37 -0800 Subject: [PATCH 17/59] No need to emit separate nugetvssolutionclose if there were no solution at all. --- .../VSSettings.cs | 25 ++++++++++++------- .../PowerShellHost.cs | 3 ++- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs index 35660227a9e..bf49acc671b 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs @@ -155,7 +155,8 @@ private void OnSolutionOpening(object sender, EventArgs e) private void OnSolutionOpened(object sender, EventArgs e) { // PMC used before any solution is loaded, let's emit what we have before loading a solution. - if (_solutionCount == 0 && _vsSolutionTelemetryEvents.Any(e => e[NuGetPMCWindowLoadCount] is int && (int)e[NuGetPMCWindowLoadCount] > 0)) + // Used means at least one powershell command executed, otherwise telemetry(NuGetPMCWindowLoadCount and FirstTimeLoadedFromPMC) is merged with first opened solution metric rather than sending separate nugetvssolutionclose telemetry with no data. + if (_solutionCount == 0 && _vsSolutionTelemetryEvents.Any(e => e[NuGetPMCExecuteCommandCount] is int && (int)e[NuGetPMCExecuteCommandCount] > 0)) { EmitVSSolutionTelemetry(); } @@ -239,7 +240,7 @@ private void EmitVSSolutionTelemetry() _vsSolutionTelemetryEmitQueue.Clear(); } catch (Exception) - {} + { } } private void EnqueueVSSolutionPowershellTelemetry() @@ -259,6 +260,12 @@ private void EnqueueVSSolutionPowershellTelemetry() } else { + // PMC opened, but no command executed nor any solution opened. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore. + if (_solutionCount == 0 && (int)vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount] == 0) + { + return; + } + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount]); _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetNonPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetNonPMCExecuteCommandCount]); _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMC, vsSolutionPowershellTelemetry[LoadedFromPMC]); @@ -284,17 +291,11 @@ private void EmitVSInstanceTelemetry() CombineAndEmitTelemetry(_vsInstanceTelemetryEmitQueue, NugetVSInstanceClose); } catch (Exception) - {} + { } } private void EnqueueVSInstancePowershellTelemetry() { - // Edge case. PMC can be used without loading VS solution at all. - if (_vsSolutionTelemetryEvents.Any(e => e.Name == PowerShellExecuteCommand)) - { - EmitVSSolutionTelemetry(); - } - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + ReOpenAtStart, _vsInstanceTelemetryEvents.Where(e => e[ReOpenAtStart] != null).Any()); // Whether PMC window re-open at start by default next time VS open? _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCWindowLoadCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); // PMC Window load count _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMCExecuteCommandCount])); // PMC number of commands executed. @@ -308,6 +309,12 @@ private void EnqueueVSInstancePowershellTelemetry() // Currently we emit vs.nuget.NugetVSSolutionClose, vs.nuget.NugetVSInstanceClose events. private void CombineAndEmitTelemetry(Dictionary telemetryEvents, string telemetryType) { + // No event to emit + if (!telemetryEvents.Keys.Any()) + { + return; + } + var vsSolutionCloseTelemetry = new Common.TelemetryEvent(telemetryType, new Dictionary()); foreach (KeyValuePair telemetryEvent in telemetryEvents) diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 41a7bcead1a..bebb6cf6e7b 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -1021,7 +1021,8 @@ public void Dispose() _initScriptsLock.Dispose(); Runspace?.Dispose(); - // Below emits telemetry in there was no solution was loaded at all, but if there were any solution then this one internally ignored because it's already emitted with solution SolutionClosing event. + // Below emits telemetry in there was no solution was loaded at all, but if there were any solution then this one will ignored because actual data already emitted with solution SolutionClosing event previously. + // If no solution loaded nor PMC is engaged at then this will be ignored. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore. EmitPowershellUsageTelemetry(false); } From a265545ea1d79dcc7a08e8247bffe3991fed84f5 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Thu, 5 Nov 2020 15:45:02 -0800 Subject: [PATCH 18/59] Refactor code so it's not much mix too much telemetry code into other code. --- .../Xamls/ConsoleContainer.xaml.cs | 13 +- .../VSSettings.cs | 170 ++---------------- .../Console/VSInstanceTelemetryConsts.cs | 32 ++++ .../Console/VSIntanceTelemetryEmit.cs | 159 ++++++++++++++++ .../NuGetConsole.Host.PowerShell.csproj | 6 +- .../PowerShellHost.cs | 33 ++-- .../PowershellTelemetryConsts.cs | 11 ++ ...eManagement.PowerShellCmdlets.dll-Help.xml | 3 +- 8 files changed, 230 insertions(+), 197 deletions(-) create mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSInstanceTelemetryConsts.cs create mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSIntanceTelemetryEmit.cs create mode 100644 src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowershellTelemetryConsts.cs diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index 07e9758a328..103d404ebc7 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -60,17 +60,6 @@ await System.Threading.Tasks.Task.Run( Assumes.NotNull(_iVsSolutionManager); // Hook up solution events - _iVsSolutionManager.SolutionOpening += (_, __) => - { - // PMC used before any solution is loaded, let's emit what we have before loading a solution. - // Please note: Here _windowLoadCount is already 1 if PMC reopen since it was last active window last time VS instance close. - if (_windowLoadCount > 0) - { - EmitPowershellUsageTelemetry(); - } - - //_isTelemetryEmitted = false; - }; _iVsSolutionManager.SolutionClosing += (o, e) => { EmitPowershellUsageTelemetry(); @@ -116,7 +105,7 @@ public void Dispose() var reopenAtStart = IsLoaded; var telemetryEvent = new TelemetryEvent(PackageManagerConsoleWindowsLoad, new Dictionary { - { NuGetPMCWindowLoadCount, _windowLoadCount}, // Usefull if PMC used without any solution load at all then VS instance closed. + { NuGetPMCWindowLoadCount, _windowLoadCount}, // Useful if PMC used without any solution load at all then VS instance closed. { ReOpenAtStart, reopenAtStart} }); diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs index bf49acc671b..2b1bd953b59 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs @@ -9,42 +9,18 @@ using NuGet.Common.Telemetry; using NuGet.Configuration; using NuGet.VisualStudio; +using NuGet.VisualStudio.Console; namespace NuGet.PackageManagement.VisualStudio { [Export(typeof(ISettings))] - [Export(typeof(INuGetSolutionTelemetry))] [PartCreationPolicy(CreationPolicy.Shared)] - public sealed class VSSettings : ISettings, INuGetSolutionTelemetry, IDisposable + public sealed class VSSettings : ISettings, IDisposable { private const string NuGetSolutionSettingsFolder = ".nuget"; // to initialize SolutionSettings first time outside MEF constructor private Tuple> _solutionSettings; - // PMC, PMUI powershell telemetry consts - private const string NugetPowershellPrefix = "NugetPowershell."; // Using prefix prevent accidental same name property collission from different type telemetry. - private const string NugetVSSolutionClose = "NugetVSSolutionClose"; - private const string NugetVSInstanceClose = "NugetVSInstanceClose"; - private const string PowerShellExecuteCommand = "PowerShellExecuteCommand"; - private const string NuGetPMCExecuteCommandCount = "NuGetPMCExecuteCommandCount"; - private const string NuGetNonPMCExecuteCommandCount = "NuGetNonPMCExecuteCommandCount"; - private const string LoadedFromPMC = "LoadedFromPMC"; - private const string FirstTimeLoadedFromPMC = "FirstTimeLoadedFromPMC"; - private const string LoadedFromPMUI = "LoadedFromPMUI"; - private const string FirstTimeLoadedFromPMUI = "FirstTimeLoadedFromPMUI"; - // PMC UI Console Container telemetry consts - private const string NuGetPMCWindowLoadCount = "NuGetPMCWindowLoadCount"; - private const string ReOpenAtStart = "ReOpenAtStart"; - // _solutionTelemetryEvents hold telemetry for current VS solution session. - private List _vsSolutionTelemetryEvents; - private Dictionary _vsSolutionTelemetryEmitQueue; - // _vsInstanceTelemetryEvents hold telemetry for current VS instance session. - private List _vsInstanceTelemetryEvents; - private Dictionary _vsInstanceTelemetryEmitQueue; - private const string SolutionCount = "SolutionCount"; - private const string PMCPowershellLoadedSolutionCount = "PMCPowershellLoadedSolutionCount"; - private const string PMUIPowershellLoadedSolutionCount = "PMUIPowershellLoadedSolutionCount"; - private const string SolutionLoaded = "SolutionLoaded"; - private int _solutionCount; + private VSIntanceTelemetryEmit _vSIntanceTelemetryEmit; private ISettings SolutionSettings { @@ -66,23 +42,20 @@ private ISettings SolutionSettings public event EventHandler SettingsChanged; - public VSSettings(ISolutionManager solutionManager) - : this(solutionManager, machineWideSettings: null) + public VSSettings(ISolutionManager solutionManager, VSIntanceTelemetryEmit vsIntanceTelemetryEmit) + : this(solutionManager, vsIntanceTelemetryEmit, machineWideSettings: null) { } [ImportingConstructor] - public VSSettings(ISolutionManager solutionManager, IMachineWideSettings machineWideSettings) + public VSSettings(ISolutionManager solutionManager, VSIntanceTelemetryEmit vsIntanceTelemetryEmit, IMachineWideSettings machineWideSettings) { SolutionManager = solutionManager ?? throw new ArgumentNullException(nameof(solutionManager)); MachineWideSettings = machineWideSettings; SolutionManager.SolutionOpening += OnSolutionOpening; SolutionManager.SolutionOpened += OnSolutionOpened; SolutionManager.SolutionClosed += OnSolutionClosed; - _vsSolutionTelemetryEvents = new List(); - _vsSolutionTelemetryEmitQueue = new Dictionary(); - _vsInstanceTelemetryEvents = new List(); - _vsInstanceTelemetryEmitQueue = new Dictionary(); + _vSIntanceTelemetryEmit = vsIntanceTelemetryEmit; } private bool ResetSolutionSettingsIfNeeded() @@ -132,11 +105,6 @@ private bool ResetSolutionSettingsIfNeeded() return false; } - private void OnSolutionOpenedOrClosed(object sender, EventArgs e) - { - DetectSolutionSettingChange(); - } - private void DetectSolutionSettingChange() { var hasChanged = ResetSolutionSettingsIfNeeded(); @@ -154,19 +122,12 @@ private void OnSolutionOpening(object sender, EventArgs e) private void OnSolutionOpened(object sender, EventArgs e) { - // PMC used before any solution is loaded, let's emit what we have before loading a solution. - // Used means at least one powershell command executed, otherwise telemetry(NuGetPMCWindowLoadCount and FirstTimeLoadedFromPMC) is merged with first opened solution metric rather than sending separate nugetvssolutionclose telemetry with no data. - if (_solutionCount == 0 && _vsSolutionTelemetryEvents.Any(e => e[NuGetPMCExecuteCommandCount] is int && (int)e[NuGetPMCExecuteCommandCount] > 0)) - { - EmitVSSolutionTelemetry(); - } - - _solutionCount++; + _vSIntanceTelemetryEmit.SolutionOpenedEmit(); } private void OnSolutionClosed(object sender, EventArgs e) { - EmitVSSolutionTelemetry(); + _vSIntanceTelemetryEmit.EmitVSSolutionTelemetry(); DetectSolutionSettingChange(); } @@ -208,121 +169,10 @@ public void Dispose() SolutionManager.SolutionOpening -= OnSolutionOpening; SolutionManager.SolutionOpened -= OnSolutionOpened; SolutionManager.SolutionClosed -= OnSolutionClosed; - - EmitVSInstanceTelemetry(); - } - - public void AddSolutionTelemetryEvent(Common.TelemetryEvent telemetryData) - { - _vsSolutionTelemetryEvents.Add(telemetryData); - _vsInstanceTelemetryEvents.Add(telemetryData); + _vSIntanceTelemetryEmit.EmitVSInstanceTelemetry(); } // The value for SolutionSettings can't possibly be null, but it could be a read-only instance private bool CanChangeSettings => !ReferenceEquals(SolutionSettings, NullSettings.Instance); - - // Emit VS solution session telemetry when solution is closed. - private void EmitVSSolutionTelemetry() - { - try - { - // Queue all different types of telemetries and do some processing prior to emit. - EnqueueVSSolutionPowershellTelemetry(); - - // Add other telemetry types here in the future. You can emit many different types of telemetry other than powershell here. - // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. - // Using prefix avoid collision of property names from different types of telemetry. - - _vsSolutionTelemetryEvents.Clear(); - - // Actual emit - CombineAndEmitTelemetry(_vsSolutionTelemetryEmitQueue, NugetVSSolutionClose); - _vsSolutionTelemetryEmitQueue.Clear(); - } - catch (Exception) - { } - } - - private void EnqueueVSSolutionPowershellTelemetry() - { - var vsSolutionPowershellTelemetry = _vsSolutionTelemetryEvents.FirstOrDefault(e => e.Name == PowerShellExecuteCommand); - - // If powershell(PMC/PMUI) is not loaded at all then we need to create default telemetry event which will be emitted. - if (vsSolutionPowershellTelemetry == null) - { - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, 0); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetNonPMCExecuteCommandCount, 0); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMC, false); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMUI, false); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMC, false); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMUI, false); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + SolutionLoaded, _solutionCount > 0); // If 'false' : PMC used before any solution is loaded. - } - else - { - // PMC opened, but no command executed nor any solution opened. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore. - if (_solutionCount == 0 && (int)vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount] == 0) - { - return; - } - - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount]); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetNonPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetNonPMCExecuteCommandCount]); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMC, vsSolutionPowershellTelemetry[LoadedFromPMC]); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMUI, vsSolutionPowershellTelemetry[LoadedFromPMUI]); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMC, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMC]); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMUI, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMUI]); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + SolutionLoaded, _solutionCount > 0); // If 'false' : PMC used before any solution is loaded. - } - - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCWindowLoadCount, _vsSolutionTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); - } - - // Emit VS solution session telemetry when VS instance is closed. - private void EmitVSInstanceTelemetry() - { - try - { - EnqueueVSInstancePowershellTelemetry(); - // Add other telemetry types here in the future. You can emit many different types of telemetry here. - // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. - // Using prefix avoid collision of property names from different types of telemetry. - - CombineAndEmitTelemetry(_vsInstanceTelemetryEmitQueue, NugetVSInstanceClose); - } - catch (Exception) - { } - } - - private void EnqueueVSInstancePowershellTelemetry() - { - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + ReOpenAtStart, _vsInstanceTelemetryEvents.Where(e => e[ReOpenAtStart] != null).Any()); // Whether PMC window re-open at start by default next time VS open? - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCWindowLoadCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); // PMC Window load count - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMCExecuteCommandCount])); // PMC number of commands executed. - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetNonPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetNonPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetNonPMCExecuteCommandCount])); // PMUI number of powershell commands executed. - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + SolutionCount, _solutionCount); - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + PMCPowershellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[SolutionLoaded] is bool && (bool)e[SolutionLoaded] && e[LoadedFromPMC] is bool).Count(e => (bool)e[LoadedFromPMC] == true)); // SolutionLoaded used here to remove edge case : PMC used before any solution is loaded - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + PMUIPowershellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[LoadedFromPMUI] is bool).Count(e => (bool)e[LoadedFromPMUI] == true)); - } - - // Instead of emitting one by one we combine them into single event and each event is a property of this single event. - // Currently we emit vs.nuget.NugetVSSolutionClose, vs.nuget.NugetVSInstanceClose events. - private void CombineAndEmitTelemetry(Dictionary telemetryEvents, string telemetryType) - { - // No event to emit - if (!telemetryEvents.Keys.Any()) - { - return; - } - - var vsSolutionCloseTelemetry = new Common.TelemetryEvent(telemetryType, new Dictionary()); - - foreach (KeyValuePair telemetryEvent in telemetryEvents) - { - vsSolutionCloseTelemetry[telemetryEvent.Key] = telemetryEvent.Value; - } - - Common.TelemetryActivity.EmitTelemetryEvent(vsSolutionCloseTelemetry); - } } } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSInstanceTelemetryConsts.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSInstanceTelemetryConsts.cs new file mode 100644 index 00000000000..81e6e04c45c --- /dev/null +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSInstanceTelemetryConsts.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; +using NuGet.Common; + +namespace NuGet.VisualStudio.Console +{ + public abstract class VSInstanceTelemetryConsts + { + // PMC, PMUI powershell telemetry consts + public const string PowerShellExecuteCommand = "PowerShellExecuteCommand"; + public const string NuGetPMCExecuteCommandCount = "NuGetPMCExecuteCommandCount"; + public const string NuGetNonPMCExecuteCommandCount = "NuGetNonPMCExecuteCommandCount"; + public const string LoadedFromPMC = "LoadedFromPMC"; + public const string FirstTimeLoadedFromPMC = "FirstTimeLoadedFromPMC"; + public const string LoadedFromPMUI = "LoadedFromPMUI"; + public const string FirstTimeLoadedFromPMUI = "FirstTimeLoadedFromPMUI"; + public const string SolutionLoaded = "SolutionLoaded"; + // PMC UI Console Container telemetry consts + public const string NuGetPMCWindowLoadCount = "NuGetPMCWindowLoadCount"; + public const string ReOpenAtStart = "ReOpenAtStart"; + // Const name for emitting when VS solution close or VS instance close. + public const string NugetPowershellPrefix = "NugetPowershell."; // Using prefix prevent accidental same name property collission from different type telemetry. + public const string NugetVSSolutionClose = "NugetVSSolutionClose"; + public const string NugetVSInstanceClose = "NugetVSInstanceClose"; + public const string SolutionCount = "SolutionCount"; + public const string PMCPowershellLoadedSolutionCount = "PMCPowershellLoadedSolutionCount"; + public const string PMUIPowershellLoadedSolutionCount = "PMUIPowershellLoadedSolutionCount"; + } +} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSIntanceTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSIntanceTelemetryEmit.cs new file mode 100644 index 00000000000..13a26b2c358 --- /dev/null +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSIntanceTelemetryEmit.cs @@ -0,0 +1,159 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using NuGet.Common; +using NuGet.Common.Telemetry; + +namespace NuGet.VisualStudio.Console +{ + [Export(typeof(INuGetSolutionTelemetry))] + [Export(typeof(VSIntanceTelemetryEmit))] + [PartCreationPolicy(CreationPolicy.Shared)] + public sealed class VSIntanceTelemetryEmit : VSInstanceTelemetryConsts, INuGetSolutionTelemetry + { + // _solutionTelemetryEvents hold telemetry for current VS solution session. + private List _vsSolutionTelemetryEvents; + private Dictionary _vsSolutionTelemetryEmitQueue; + // _vsInstanceTelemetryEvents hold telemetry for current VS instance session. + private List _vsInstanceTelemetryEvents; + private Dictionary _vsInstanceTelemetryEmitQueue; + + private int _solutionCount; + + public VSIntanceTelemetryEmit() + { + _vsSolutionTelemetryEvents = new List(); + _vsSolutionTelemetryEmitQueue = new Dictionary(); + _vsInstanceTelemetryEvents = new List(); + _vsInstanceTelemetryEmitQueue = new Dictionary(); + } + + public void AddSolutionTelemetryEvent(TelemetryEvent telemetryData) + { + _vsSolutionTelemetryEvents.Add(telemetryData); + _vsInstanceTelemetryEvents.Add(telemetryData); + } + + public void SolutionOpenedEmit() + { + // PMC used before any solution is loaded, let's emit what we have before loading a solution. + // Used means at least one powershell command executed, otherwise telemetry(NuGetPMCWindowLoadCount and FirstTimeLoadedFromPMC) is merged with first opened solution metric rather than sending separate nugetvssolutionclose telemetry with no data. + if (_solutionCount == 0 && _vsSolutionTelemetryEvents.Any(e => e[VSInstanceTelemetryConsts.NuGetPMCExecuteCommandCount] is int && (int)e[NuGetPMCExecuteCommandCount] > 0)) + { + EmitVSSolutionTelemetry(); + } + + _solutionCount++; + } + + // Emit VS solution session telemetry when solution is closed. + public void EmitVSSolutionTelemetry() + { + try + { + // Queue all different types of telemetries and do some processing prior to emit. + EnqueueVSSolutionPowershellTelemetry(); + + // Add other telemetry types here in the future. You can emit many different types of telemetry other than powershell here. + // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. + // Using prefix avoid collision of property names from different types of telemetry. + + _vsSolutionTelemetryEvents.Clear(); + + // Actual emit + CombineAndEmitTelemetry(_vsSolutionTelemetryEmitQueue, NugetVSSolutionClose); + _vsSolutionTelemetryEmitQueue.Clear(); + } + catch (Exception) + { + // Currently do nothing. + } + } + + private void EnqueueVSSolutionPowershellTelemetry() + { + var vsSolutionPowershellTelemetry = _vsSolutionTelemetryEvents.FirstOrDefault(e => e.Name == PowerShellExecuteCommand); + + // If powershell(PMC/PMUI) is not loaded at all then we need to create default telemetry event which will be emitted. + if (vsSolutionPowershellTelemetry == null) + { + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, 0); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetNonPMCExecuteCommandCount, 0); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMC, false); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMUI, false); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMC, false); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMUI, false); + //_vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + SolutionLoaded, _solutionCount > 0); // If 'false' : PMC used before any solution is loaded. + } + else + { + // PMC opened, but no command executed nor any solution opened. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore. + if (_solutionCount == 0 && (int)vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount] == 0) + { + return; + } + + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount]); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetNonPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetNonPMCExecuteCommandCount]); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMC, vsSolutionPowershellTelemetry[LoadedFromPMC]); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMUI, vsSolutionPowershellTelemetry[LoadedFromPMUI]); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMC, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMC]); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMUI, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMUI]); + //_vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + SolutionLoaded, _solutionCount > 0); // If 'false' : PMC used before any solution is loaded. + } + + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCWindowLoadCount, _vsSolutionTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); + } + + // Emit VS solution session telemetry when VS instance is closed. + public void EmitVSInstanceTelemetry() + { + try + { + EnqueueVSInstancePowershellTelemetry(); + // Add other telemetry types here in the future. You can emit many different types of telemetry here. + // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. + // Using prefix avoid collision of property names from different types of telemetry. + + CombineAndEmitTelemetry(_vsInstanceTelemetryEmitQueue, NugetVSInstanceClose); + } + catch (Exception) + { } + } + + private void EnqueueVSInstancePowershellTelemetry() + { + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + ReOpenAtStart, _vsInstanceTelemetryEvents.Where(e => e[ReOpenAtStart] != null).Any()); // Whether PMC window re-open at start by default next time VS open? + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCWindowLoadCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); // PMC Window load count + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMCExecuteCommandCount])); // PMC number of commands executed. + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetNonPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetNonPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetNonPMCExecuteCommandCount])); // PMUI number of powershell commands executed. + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + SolutionCount, _solutionCount); + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + PMCPowershellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[SolutionLoaded] is bool && (bool)e[SolutionLoaded] && e[LoadedFromPMC] is bool).Count(e => (bool)e[LoadedFromPMC] == true)); // SolutionLoaded used here to remove edge case : PMC used before any solution is loaded + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + PMUIPowershellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[LoadedFromPMUI] is bool).Count(e => (bool)e[LoadedFromPMUI] == true)); + } + + // Instead of emitting one by one we combine them into single event and each event is a property of this single event. + // Currently we emit vs.nuget.NugetVSSolutionClose, vs.nuget.NugetVSInstanceClose events. + private void CombineAndEmitTelemetry(Dictionary telemetryEvents, string telemetryType) + { + // No event to emit + if (!telemetryEvents.Keys.Any()) + { + return; + } + + var vsSolutionCloseTelemetry = new TelemetryEvent(telemetryType, new Dictionary()); + + foreach (KeyValuePair telemetryEvent in telemetryEvents) + { + vsSolutionCloseTelemetry[telemetryEvent.Key] = telemetryEvent.Value; + } + + TelemetryActivity.EmitTelemetryEvent(vsSolutionCloseTelemetry); + } + } +} diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj index 466dc4ef1a7..c992563ea56 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj @@ -22,6 +22,7 @@ + @@ -33,6 +34,7 @@ CommonResources.cs + @@ -183,7 +185,6 @@ - @@ -201,16 +202,13 @@ - <_AllLocalizedXmlFiles Include="$(OutputPath)Scripts\**\NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml" /> <_XmlLocFiles Include="@(_AllLocalizedXmlFiles)"> Modules\NuGet\$([MSBuild]::MakeRelative($(OutputPath)Scripts, %(_AllLocalizedXmlFiles.Identity))) - <_TxtLocFiles Include="$(LocalizationRootDirectory)\%(VSLanguage.Identity)\15\about_NuGet.Cmdlets.help.txt"> Modules\NuGet\%(VSLanguage.Locale)\about_NuGet.Cmdlets.help.txt - diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index bebb6cf6e7b..3e44347bc86 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -29,16 +29,18 @@ using NuGet.ProjectManagement; using NuGet.Protocol.Core.Types; using NuGet.VisualStudio; +using NuGet.VisualStudio.Console; using NuGet.VisualStudio.Telemetry; using Task = System.Threading.Tasks.Task; namespace NuGetConsole.Host.PowerShell.Implementation { - internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable + internal abstract class PowerShellHost : VSInstanceTelemetryConsts, IHost, IPathExpansion, IDisposable { private static readonly string AggregateSourceName = Resources.AggregateSourceName; private static readonly TimeSpan ExecuteInitScriptsRetryDelay = TimeSpan.FromMilliseconds(400); private static readonly int MaxTasks = 16; + private readonly static object TelemetryLock = new object(); private static bool IsTelemetryEmitted; private static int PmcExecutedCount; @@ -66,14 +68,7 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private const string DTEKey = "DTE"; private const string CancellationTokenKey = "CancellationTokenKey"; private const int ExecuteInitScriptsRetriesLimit = 50; - private const string PowerShellExecuteCommand = "PowerShellExecuteCommand"; - private const string NuGetPMCExecuteCommandCount = "NuGetPMCExecuteCommandCount"; - private const string NuGetNonPMCExecuteCommandCount = "NuGetNonPMCExecuteCommandCount"; - private const string LoadedFromPMC = "LoadedFromPMC"; - private const string FirstTimeLoadedFromPMC = "FirstTimeLoadedFromPMC"; - private const string LoadedFromPMUI = "LoadedFromPMUI"; - private const string FirstTimeLoadedFromPMUI = "FirstTimeLoadedFromPMUI"; - private const string SolutionLoaded = "SolutionLoaded"; + private string _activePackageSource; private string[] _packageSources; private readonly Lazy _dte; @@ -336,15 +331,6 @@ public void Initialize(IConsole console) _solutionManager.Value.SolutionOpened += (_, __) => HandleSolutionOpened(); } - _solutionManager.Value.SolutionOpening += (o, e) => - { - if (PmcExecutedCount > 0) - { - // PMC used before any solution is loaded, let's emit what we have before loading a solution. - EmitPowershellUsageTelemetry(false); - } - }; - _solutionManager.Value.SolutionClosing += (o, e) => { EmitPowershellUsageTelemetry(true); @@ -424,8 +410,12 @@ private void HandleSolutionOpened() { _scriptExecutor.Value.Reset(); - PmcExecutedCount = 0; - NonPmcExecutedCount = 0; + if (PmcExecutedCount > 0) + { + // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event aggregation before loading a solution. + EmitPowershellUsageTelemetry(false); + } + IsTelemetryEmitted = false; // Solution opened event is raised on the UI thread @@ -549,6 +539,9 @@ private async Task ExecuteInitPs1Async(string installPath, PackageIdentity ident request.BuildInput(), outputResults: true); + // Init.ps1 is loaded + PowerShellHostInstances |= 0b10000000; + return; } } diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowershellTelemetryConsts.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowershellTelemetryConsts.cs new file mode 100644 index 00000000000..4f37a80f2cb --- /dev/null +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowershellTelemetryConsts.cs @@ -0,0 +1,11 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace NuGetConsole.Host.PowerShell +{ + public class PowershellTelemetryConsts + { + + //NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml + } +} diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml index c3219932d11..5b0d5b42f4c 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml @@ -2,6 +2,7 @@ + - \ No newline at end of file + From a99b6671bcbf7de3a8973227a2313e4d588e0710 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Mon, 9 Nov 2020 13:21:22 -0800 Subject: [PATCH 19/59] Add telemetry detecting Nuget command and load of init.ps1 for packages. --- .../Xamls/ConsoleContainer.xaml.cs | 4 +- .../Console/VSInstanceTelemetryConsts.cs | 6 +- .../Console/VSIntanceTelemetryEmit.cs | 16 +-- .../PowerShellHost.cs | 116 +++++++++++++++--- .../PublicAPI/net45/PublicAPI.Unshipped.txt | 4 +- .../PublicAPI/net472/PublicAPI.Unshipped.txt | 4 +- .../netstandard2.0/PublicAPI.Unshipped.txt | 4 +- ...emetry.cs => INuGetTelemetryAggregator.cs} | 4 +- 8 files changed, 120 insertions(+), 38 deletions(-) rename src/NuGet.Core/NuGet.Common/Telemetry/{INuGetSolutionTelemetry.cs => INuGetTelemetryAggregator.cs} (72%) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index 103d404ebc7..d059c07a619 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -28,7 +28,7 @@ public sealed partial class ConsoleContainer : UserControl, IDisposable { private INuGetSolutionManagerService _solutionManager; private IVsSolutionManager _iVsSolutionManager; - private INuGetSolutionTelemetry _nugetSolutionTelemetry; + private INuGetTelemetryAggregator _nugetSolutionTelemetry; private int _windowLoadCount; private bool _isTelemetryEmitted; @@ -69,7 +69,7 @@ await System.Threading.Tasks.Task.Run( var packageRestoreManager = ServiceLocator.GetInstance(); var deleteOnRestartManager = ServiceLocator.GetInstance(); var shell = ServiceLocator.GetGlobalService(); - _nugetSolutionTelemetry = ServiceLocator.GetInstanceSafe(); + _nugetSolutionTelemetry = ServiceLocator.GetInstanceSafe(); await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSInstanceTelemetryConsts.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSInstanceTelemetryConsts.cs index 81e6e04c45c..06400aef327 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSInstanceTelemetryConsts.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSInstanceTelemetryConsts.cs @@ -12,7 +12,11 @@ public abstract class VSInstanceTelemetryConsts // PMC, PMUI powershell telemetry consts public const string PowerShellExecuteCommand = "PowerShellExecuteCommand"; public const string NuGetPMCExecuteCommandCount = "NuGetPMCExecuteCommandCount"; - public const string NuGetNonPMCExecuteCommandCount = "NuGetNonPMCExecuteCommandCount"; + public const string NuGetPMUIExecuteCommandCount = "NuGetPMUIExecuteCommandCount"; + public const string NuGetPowerShellLoaded = "NuGetPowerShellLoaded"; + public const string LoadFromPMC = "LoadFromPMC"; + public const string NuGetCommandUsed = "NuGetCommandUsed"; + public const string InitPs1Loaded = "InitPs1Loaded"; public const string LoadedFromPMC = "LoadedFromPMC"; public const string FirstTimeLoadedFromPMC = "FirstTimeLoadedFromPMC"; public const string LoadedFromPMUI = "LoadedFromPMUI"; diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSIntanceTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSIntanceTelemetryEmit.cs index 13a26b2c358..6c55781fe6c 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSIntanceTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSIntanceTelemetryEmit.cs @@ -10,10 +10,10 @@ namespace NuGet.VisualStudio.Console { - [Export(typeof(INuGetSolutionTelemetry))] + [Export(typeof(INuGetTelemetryAggregator))] [Export(typeof(VSIntanceTelemetryEmit))] [PartCreationPolicy(CreationPolicy.Shared)] - public sealed class VSIntanceTelemetryEmit : VSInstanceTelemetryConsts, INuGetSolutionTelemetry + public sealed class VSIntanceTelemetryEmit : VSInstanceTelemetryConsts, INuGetTelemetryAggregator { // _solutionTelemetryEvents hold telemetry for current VS solution session. private List _vsSolutionTelemetryEvents; @@ -82,12 +82,13 @@ private void EnqueueVSSolutionPowershellTelemetry() if (vsSolutionPowershellTelemetry == null) { _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, 0); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetNonPMCExecuteCommandCount, 0); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMUIExecuteCommandCount, 0); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetCommandUsed, false); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + InitPs1Loaded, false); _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMC, false); _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMUI, false); _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMC, false); _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMUI, false); - //_vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + SolutionLoaded, _solutionCount > 0); // If 'false' : PMC used before any solution is loaded. } else { @@ -98,12 +99,13 @@ private void EnqueueVSSolutionPowershellTelemetry() } _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount]); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetNonPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetNonPMCExecuteCommandCount]); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMUIExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMUIExecuteCommandCount]); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetCommandUsed, vsSolutionPowershellTelemetry[NuGetCommandUsed]); + _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + InitPs1Loaded, vsSolutionPowershellTelemetry[InitPs1Loaded]); _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMC, vsSolutionPowershellTelemetry[LoadedFromPMC]); _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMUI, vsSolutionPowershellTelemetry[LoadedFromPMUI]); _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMC, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMC]); _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMUI, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMUI]); - //_vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + SolutionLoaded, _solutionCount > 0); // If 'false' : PMC used before any solution is loaded. } _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCWindowLoadCount, _vsSolutionTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); @@ -130,7 +132,7 @@ private void EnqueueVSInstancePowershellTelemetry() _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + ReOpenAtStart, _vsInstanceTelemetryEvents.Where(e => e[ReOpenAtStart] != null).Any()); // Whether PMC window re-open at start by default next time VS open? _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCWindowLoadCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); // PMC Window load count _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMCExecuteCommandCount])); // PMC number of commands executed. - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetNonPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetNonPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetNonPMCExecuteCommandCount])); // PMUI number of powershell commands executed. + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMUIExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMUIExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMUIExecuteCommandCount])); // PMUI number of powershell commands executed. _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + SolutionCount, _solutionCount); _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + PMCPowershellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[SolutionLoaded] is bool && (bool)e[SolutionLoaded] && e[LoadedFromPMC] is bool).Count(e => (bool)e[LoadedFromPMC] == true)); // SolutionLoaded used here to remove edge case : PMC used before any solution is loaded _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + PMUIPowershellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[LoadedFromPMUI] is bool).Count(e => (bool)e[LoadedFromPMUI] == true)); diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 3e44347bc86..79766809812 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -45,6 +45,16 @@ internal abstract class PowerShellHost : VSInstanceTelemetryConsts, IHost, IPath private static bool IsTelemetryEmitted; private static int PmcExecutedCount; private static int NonPmcExecutedCount; + + // There are 8 bit in byte. + // 0 - not used + // 1 - not used + // 2 - If nuget command used during current VS solution session. + // 3 - If init.ps1 is loaded during current VS solution session. + // 4 - First time load from PMUI + // 5 - First time load PMC + // 6 - LoadedFromPMUI: Indicates powershell host for PMUI already created, and stays that way until VS close. + // 7 - LoadedFromPMC: Indicates powershell host for PMC already created, and stays that way until VS close. private static byte PowerShellHostInstances; private Microsoft.VisualStudio.Threading.AsyncLazy _vsMonitorSelection; @@ -59,7 +69,7 @@ internal abstract class PowerShellHost : VSInstanceTelemetryConsts, IHost, IPath private readonly Lazy _settings; private readonly Lazy _sourceControlManagerProvider; private readonly Lazy _commonOperations; - private readonly Lazy _nugetSolutionTelemetry; + private readonly Lazy _nugetTelemetryAggregate; private readonly Lazy _deleteOnRestartManager; private readonly Lazy _scriptExecutor; private const string ActivePackageSourceKey = "activePackageSource"; @@ -124,7 +134,7 @@ protected PowerShellHost(string name, IRestoreEvents restoreEvents, IRunspaceMan _sourceControlManagerProvider = new Lazy( () => ServiceLocator.GetInstanceSafe()); _commonOperations = new Lazy(() => ServiceLocator.GetInstanceSafe()); - _nugetSolutionTelemetry = new Lazy(() => ServiceLocator.GetInstanceSafe()); + _nugetTelemetryAggregate = new Lazy(() => ServiceLocator.GetInstanceSafe()); _name = name; IsCommandEnabled = true; @@ -324,6 +334,9 @@ public void Initialize(IConsole console) UpdateWorkingDirectory(); await ExecuteInitScriptsAsync(); + // Emit first time Powershell load event to find out later how many VS instance crash after loading powershell. + EmitPowerShellLoadedTelemetry(console is IWpfConsole); + // check if PMC console is actually opened, then only hook to solution load events. if (console is IWpfConsole) { @@ -526,23 +539,27 @@ private async Task ExecuteInitPs1Async(string installPath, PackageIdentity ident AddPathToEnvironment(toolsPath); var scriptPath = Path.Combine(toolsPath, PowerShellScripts.Init); - if (File.Exists(scriptPath) && - _scriptExecutor.Value.TryMarkVisited(identity, PackageInitPS1State.FoundAndExecuted)) + if (File.Exists(scriptPath)) { - // always execute init script on a background thread - await TaskScheduler.Default; + // Init.ps1 is loaded + PowerShellHostInstances |= 0b00010000; - var request = new ScriptExecutionRequest(scriptPath, installPath, identity, project: null); + if (_scriptExecutor.Value.TryMarkVisited(identity, PackageInitPS1State.FoundAndExecuted)) + { + // always execute init script on a background thread + await TaskScheduler.Default; - Runspace.Invoke( - request.BuildCommand(), - request.BuildInput(), - outputResults: true); + var request = new ScriptExecutionRequest(scriptPath, installPath, identity, project: null); - // Init.ps1 is loaded - PowerShellHostInstances |= 0b10000000; + Runspace.Invoke( + request.BuildCommand(), + request.BuildInput(), + outputResults: true); - return; + + + return; + } } } @@ -591,6 +608,8 @@ public bool Execute(IConsole console, string command, params object[] inputs) { lock (TelemetryLock) { + // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files + // For PMC all installation done in one pass so no double counting. PmcExecutedCount++; } } @@ -598,6 +617,10 @@ public bool Execute(IConsole console, string command, params object[] inputs) { lock (TelemetryLock) { + // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files + // This one is called for both init.ps1 and install.ps1 seperately. + // For MSBuildNuGetProject projects install.ps1 can event furthure duplicate counted: MSBuildNuGetProject.cs#L377 - L396 + // Also this concern valid for dependent packages with *.ps1 files. NonPmcExecutedCount++; } } @@ -612,6 +635,9 @@ public bool Execute(IConsole console, string command, params object[] inputs) NuGetEventTrigger.Instance.TriggerEvent(NuGetEvent.PackageManagerConsoleCommandExecutionBegin); ActiveConsole = console; + // Check and log if command is NugetCommand + IsNugetCommand(command); + string fullCommand; if (ComplexCommand.AddLine(command, out fullCommand) && !string.IsNullOrEmpty(fullCommand)) @@ -901,6 +927,23 @@ private async Task> CompleteTaskAsync(List + { + { NugetPowershellPrefix + LoadFromPMC, isPMC} + }); + + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + } + } + } + private void EmitPowershellUsageTelemetry(bool withSolution) { lock (TelemetryLock) @@ -910,25 +953,58 @@ private void EmitPowershellUsageTelemetry(bool withSolution) var telemetryEvent = new TelemetryEvent(PowerShellExecuteCommand, new Dictionary { { NuGetPMCExecuteCommandCount, PmcExecutedCount}, - { NuGetNonPMCExecuteCommandCount, NonPmcExecutedCount}, - { LoadedFromPMC, (PowerShellHostInstances & 0b00000001) == 0b00000001}, - { LoadedFromPMUI, (PowerShellHostInstances & 0b00000010) == 0b00000010}, - { FirstTimeLoadedFromPMC, (PowerShellHostInstances & 0b00000100) == 0b00000100}, + { NuGetPMUIExecuteCommandCount, NonPmcExecutedCount}, + { NuGetCommandUsed, (PowerShellHostInstances & 0b00100000) == 0b00100000}, + { InitPs1Loaded, (PowerShellHostInstances & 0b00010000) == 0b00010000}, { FirstTimeLoadedFromPMUI, (PowerShellHostInstances & 0b00001000) == 0b00001000}, + { FirstTimeLoadedFromPMC, (PowerShellHostInstances & 0b00000100) == 0b00000100}, + { LoadedFromPMUI, (PowerShellHostInstances & 0b00000010) == 0b00000010}, + { LoadedFromPMC, (PowerShellHostInstances & 0b00000001) == 0b00000001}, { SolutionLoaded, withSolution} }); - _nugetSolutionTelemetry.Value.AddSolutionTelemetryEvent(telemetryEvent); + _nugetTelemetryAggregate.Value.AddSolutionTelemetryEvent(telemetryEvent); PmcExecutedCount = 0; NonPmcExecutedCount = 0; IsTelemetryEmitted = true; - //Reset first time load powershell flag bits, but keep other 2 flags for origin of powershell load. + // Keep other 2 flags for powershell host are created flags, reset all others. PowerShellHostInstances &= 0b00000011; } } } + private void IsNugetCommand(string commandStr) + { + if (!string.IsNullOrWhiteSpace(commandStr)) + { + string command = commandStr.Trim().ToUpperInvariant(); + string[] commandParts = command.Split(' '); + + if (commandParts.Count() > 1) + { + command = commandParts[0]; + } + + switch (command) + { + case "GET-HELP": + case "FIND-PACKAGE": + case "GET-PACKAGE": + case "INSTALL-PACKAGE": + case "UNINSTALL-PACKAGE": + case "UPDATE-PACKAGE": + case "SYNC-PACKAGE": + case "ADD-BINDINGREDIRECT": + case "GET-PROJECT": + case "REGISTER-TABEXPANSION": + // NugetCommand executed + PowerShellHostInstances |= 0b00100000; + break; + } + } + } + #region ITabExpansion public Task GetExpansionsAsync(string line, string lastWord, CancellationToken token) diff --git a/src/NuGet.Core/NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt index f7bb5f81732..ff479d29a81 100644 --- a/src/NuGet.Core/NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt @@ -2,5 +2,5 @@ NuGet.Common.NuGetLogCode.NU1010 = 1010 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1011 = 1011 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU5501 = 5501 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1012 = 1012 -> NuGet.Common.NuGetLogCode -NuGet.Common.Telemetry.INuGetSolutionTelemetry -NuGet.Common.Telemetry.INuGetSolutionTelemetry.AddSolutionTelemetryEvent(NuGet.Common.TelemetryEvent telemetryData) -> void +NuGet.Common.Telemetry.INuGetTelemetryAggregator +NuGet.Common.Telemetry.INuGetTelemetryAggregator.AddSolutionTelemetryEvent(NuGet.Common.TelemetryEvent telemetryData) -> void diff --git a/src/NuGet.Core/NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt index f7bb5f81732..ff479d29a81 100644 --- a/src/NuGet.Core/NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt @@ -2,5 +2,5 @@ NuGet.Common.NuGetLogCode.NU1010 = 1010 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1011 = 1011 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU5501 = 5501 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1012 = 1012 -> NuGet.Common.NuGetLogCode -NuGet.Common.Telemetry.INuGetSolutionTelemetry -NuGet.Common.Telemetry.INuGetSolutionTelemetry.AddSolutionTelemetryEvent(NuGet.Common.TelemetryEvent telemetryData) -> void +NuGet.Common.Telemetry.INuGetTelemetryAggregator +NuGet.Common.Telemetry.INuGetTelemetryAggregator.AddSolutionTelemetryEvent(NuGet.Common.TelemetryEvent telemetryData) -> void diff --git a/src/NuGet.Core/NuGet.Common/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Common/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt index f7bb5f81732..ff479d29a81 100644 --- a/src/NuGet.Core/NuGet.Common/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Common/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -2,5 +2,5 @@ NuGet.Common.NuGetLogCode.NU1010 = 1010 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1011 = 1011 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU5501 = 5501 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1012 = 1012 -> NuGet.Common.NuGetLogCode -NuGet.Common.Telemetry.INuGetSolutionTelemetry -NuGet.Common.Telemetry.INuGetSolutionTelemetry.AddSolutionTelemetryEvent(NuGet.Common.TelemetryEvent telemetryData) -> void +NuGet.Common.Telemetry.INuGetTelemetryAggregator +NuGet.Common.Telemetry.INuGetTelemetryAggregator.AddSolutionTelemetryEvent(NuGet.Common.TelemetryEvent telemetryData) -> void diff --git a/src/NuGet.Core/NuGet.Common/Telemetry/INuGetSolutionTelemetry.cs b/src/NuGet.Core/NuGet.Common/Telemetry/INuGetTelemetryAggregator.cs similarity index 72% rename from src/NuGet.Core/NuGet.Common/Telemetry/INuGetSolutionTelemetry.cs rename to src/NuGet.Core/NuGet.Common/Telemetry/INuGetTelemetryAggregator.cs index 31eae9020fc..d82079eee1d 100644 --- a/src/NuGet.Core/NuGet.Common/Telemetry/INuGetSolutionTelemetry.cs +++ b/src/NuGet.Core/NuGet.Common/Telemetry/INuGetTelemetryAggregator.cs @@ -4,9 +4,9 @@ namespace NuGet.Common.Telemetry { // This one used for emitting aggregated telemetry at VS solution close or VS instance close. - public interface INuGetSolutionTelemetry + public interface INuGetTelemetryAggregator { - /// Send a to telemetry. + /// Add a to telemetry list which will be aggregated and sent later. /// Telemetry event to send. void AddSolutionTelemetryEvent(TelemetryEvent telemetryData); } From bbe8502991bfa7431f2119bb71c787c422436a04 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Mon, 9 Nov 2020 15:13:18 -0800 Subject: [PATCH 20/59] Move telemetry logic into seperate class VsPowerShellHostTelemetryEmit for emitting telemetry in PowerShellHost.cs --- .../VSSettings.cs | 10 +- .../VsInstanceTelemetryConsts.cs} | 8 +- .../VsIntanceTelemetryEmit.cs} | 10 +- .../VsPowerShellHostTelemetryEmit.cs | 189 ++++++++++++++++++ .../PowerShellHost.cs | 173 ++-------------- 5 files changed, 215 insertions(+), 175 deletions(-) rename src/NuGet.Clients/NuGet.VisualStudio.Common/{Console/VSInstanceTelemetryConsts.cs => Telemetry/VsInstanceTelemetryConsts.cs} (92%) rename src/NuGet.Clients/NuGet.VisualStudio.Common/{Console/VSIntanceTelemetryEmit.cs => Telemetry/VsIntanceTelemetryEmit.cs} (97%) create mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs index 2b1bd953b59..caf749244f0 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs @@ -5,11 +5,9 @@ using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; -using System.Linq; -using NuGet.Common.Telemetry; using NuGet.Configuration; using NuGet.VisualStudio; -using NuGet.VisualStudio.Console; +using NuGet.VisualStudio.Telemetry; namespace NuGet.PackageManagement.VisualStudio { @@ -20,7 +18,7 @@ public sealed class VSSettings : ISettings, IDisposable private const string NuGetSolutionSettingsFolder = ".nuget"; // to initialize SolutionSettings first time outside MEF constructor private Tuple> _solutionSettings; - private VSIntanceTelemetryEmit _vSIntanceTelemetryEmit; + private VsIntanceTelemetryEmit _vSIntanceTelemetryEmit; private ISettings SolutionSettings { @@ -42,13 +40,13 @@ private ISettings SolutionSettings public event EventHandler SettingsChanged; - public VSSettings(ISolutionManager solutionManager, VSIntanceTelemetryEmit vsIntanceTelemetryEmit) + public VSSettings(ISolutionManager solutionManager, VsIntanceTelemetryEmit vsIntanceTelemetryEmit) : this(solutionManager, vsIntanceTelemetryEmit, machineWideSettings: null) { } [ImportingConstructor] - public VSSettings(ISolutionManager solutionManager, VSIntanceTelemetryEmit vsIntanceTelemetryEmit, IMachineWideSettings machineWideSettings) + public VSSettings(ISolutionManager solutionManager, VsIntanceTelemetryEmit vsIntanceTelemetryEmit, IMachineWideSettings machineWideSettings) { SolutionManager = solutionManager ?? throw new ArgumentNullException(nameof(solutionManager)); MachineWideSettings = machineWideSettings; diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSInstanceTelemetryConsts.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs similarity index 92% rename from src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSInstanceTelemetryConsts.cs rename to src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs index 06400aef327..5085720f8d6 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSInstanceTelemetryConsts.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs @@ -1,13 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Collections.Generic; -using System.Linq; -using NuGet.Common; - -namespace NuGet.VisualStudio.Console +namespace NuGet.VisualStudio.Telemetry { - public abstract class VSInstanceTelemetryConsts + public abstract class VsInstanceTelemetryConsts { // PMC, PMUI powershell telemetry consts public const string PowerShellExecuteCommand = "PowerShellExecuteCommand"; diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSIntanceTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs similarity index 97% rename from src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSIntanceTelemetryEmit.cs rename to src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs index 6c55781fe6c..5efe9698b50 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Console/VSIntanceTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs @@ -8,12 +8,12 @@ using NuGet.Common; using NuGet.Common.Telemetry; -namespace NuGet.VisualStudio.Console +namespace NuGet.VisualStudio.Telemetry { [Export(typeof(INuGetTelemetryAggregator))] - [Export(typeof(VSIntanceTelemetryEmit))] + [Export(typeof(VsIntanceTelemetryEmit))] [PartCreationPolicy(CreationPolicy.Shared)] - public sealed class VSIntanceTelemetryEmit : VSInstanceTelemetryConsts, INuGetTelemetryAggregator + public sealed class VsIntanceTelemetryEmit : VsInstanceTelemetryConsts, INuGetTelemetryAggregator { // _solutionTelemetryEvents hold telemetry for current VS solution session. private List _vsSolutionTelemetryEvents; @@ -24,7 +24,7 @@ public sealed class VSIntanceTelemetryEmit : VSInstanceTelemetryConsts, INuGetTe private int _solutionCount; - public VSIntanceTelemetryEmit() + public VsIntanceTelemetryEmit() { _vsSolutionTelemetryEvents = new List(); _vsSolutionTelemetryEmitQueue = new Dictionary(); @@ -42,7 +42,7 @@ public void SolutionOpenedEmit() { // PMC used before any solution is loaded, let's emit what we have before loading a solution. // Used means at least one powershell command executed, otherwise telemetry(NuGetPMCWindowLoadCount and FirstTimeLoadedFromPMC) is merged with first opened solution metric rather than sending separate nugetvssolutionclose telemetry with no data. - if (_solutionCount == 0 && _vsSolutionTelemetryEvents.Any(e => e[VSInstanceTelemetryConsts.NuGetPMCExecuteCommandCount] is int && (int)e[NuGetPMCExecuteCommandCount] > 0)) + if (_solutionCount == 0 && _vsSolutionTelemetryEvents.Any(e => e[VsInstanceTelemetryConsts.NuGetPMCExecuteCommandCount] is int && (int)e[NuGetPMCExecuteCommandCount] > 0)) { EmitVSSolutionTelemetry(); } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs new file mode 100644 index 00000000000..1b775681ff3 --- /dev/null +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs @@ -0,0 +1,189 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using NuGet.Common; +using NuGet.Common.Telemetry; + +namespace NuGet.VisualStudio.Telemetry +{ + [Export(typeof(VsPowerShellHostTelemetryEmit))] + [PartCreationPolicy(CreationPolicy.Shared)] + public class VsPowerShellHostTelemetryEmit : VsInstanceTelemetryConsts + { + private readonly object _telemetryLock = new object(); + private bool _isTelemetryEmitted; + private int _pmcExecutedCount; + private int _nonPmcExecutedCount; + private readonly Lazy _nugetTelemetryAggregate; + + // There are 8 bit in byte. + // 0 - not used + // 1 - not used + // 2 - If nuget command used during current VS solution session. + // 3 - If init.ps1 is loaded during current VS solution session. + // 4 - First time load from PMUI + // 5 - First time load PMC + // 6 - LoadedFromPMUI: Indicates powershell host for PMUI already created, and stays that way until VS close. + // 7 - LoadedFromPMC: Indicates powershell host for PMC already created, and stays that way until VS close. + private static byte PowerShellHostInstances; + + public VsPowerShellHostTelemetryEmit() + { + _nugetTelemetryAggregate = new Lazy(() => ServiceLocator.GetInstanceSafe()); + } + + public void CheckInitOrigin(bool isPMC) + { + lock (_telemetryLock) + { + //There is edge case where PMC is opened but user doesn't execute any command on it. + if (isPMC) + { + if ((PowerShellHostInstances & 0b00000001) == 0) + { + // First time load PMC + PowerShellHostInstances |= 0b00000100; + } + + // LoadedFromPMC + PowerShellHostInstances |= 0b00000001; + } + else + { + if ((PowerShellHostInstances & 0b00000010) == 0) + { + // First time load from PMUI + PowerShellHostInstances |= 0b00001000; + } + + // LoadedFromPMUI + PowerShellHostInstances |= 0b00000010; + } + } + } + + public void IncreaseCommandCounter(bool isPMC) + { + if (isPMC) + { + lock (_telemetryLock) + { + // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files + // For PMC all installation done in one pass so no double counting. + _pmcExecutedCount++; + } + } + else + { + lock (_telemetryLock) + { + // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files + // This one is called for both init.ps1 and install.ps1 seperately. + // For MSBuildNuGetProject projects install.ps1 can event furthure duplicate counted: MSBuildNuGetProject.cs#L377 - L396 + // Also this concern valid for dependent packages with *.ps1 files. + _nonPmcExecutedCount++; + } + } + } + + public void HandleSolutionOpenedEmit() + { + if (_pmcExecutedCount > 0) + { + // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event aggregation before loading a solution. + EmitPowershellUsageTelemetry(false); + } + + _isTelemetryEmitted = false; + } + + + public void EmitPowerShellLoadedTelemetry(bool isPMC) + { + lock (_telemetryLock) + { + // This is PowerShellHost load first time. + if ((PowerShellHostInstances & 0b00000011) == 0) + { + var telemetryEvent = new TelemetryEvent(NuGetPowerShellLoaded, new Dictionary + { + { NugetPowershellPrefix + LoadFromPMC, isPMC} + }); + + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + } + } + } + + public void EmitPowershellUsageTelemetry(bool withSolution) + { + lock (_telemetryLock) + { + if (!_isTelemetryEmitted) + { + var telemetryEvent = new TelemetryEvent(PowerShellExecuteCommand, new Dictionary + { + { NuGetPMCExecuteCommandCount, _pmcExecutedCount}, + { NuGetPMUIExecuteCommandCount, _nonPmcExecutedCount}, + { NuGetCommandUsed, (PowerShellHostInstances & 0b00100000) == 0b00100000}, + { InitPs1Loaded, (PowerShellHostInstances & 0b00010000) == 0b00010000}, + { FirstTimeLoadedFromPMUI, (PowerShellHostInstances & 0b00001000) == 0b00001000}, + { FirstTimeLoadedFromPMC, (PowerShellHostInstances & 0b00000100) == 0b00000100}, + { LoadedFromPMUI, (PowerShellHostInstances & 0b00000010) == 0b00000010}, + { LoadedFromPMC, (PowerShellHostInstances & 0b00000001) == 0b00000001}, + { SolutionLoaded, withSolution} + }); + _nugetTelemetryAggregate.Value.AddSolutionTelemetryEvent(telemetryEvent); + + _pmcExecutedCount = 0; + _nonPmcExecutedCount = 0; + _isTelemetryEmitted = true; + + // Keep other 2 flags for powershell host are created flags, reset all others. + PowerShellHostInstances &= 0b00000011; + } + } + } + + public void RecordInitPs1loaded() + { + // Init.ps1 is loaded + PowerShellHostInstances |= 0b00010000; + } + + public void IsNugetCommand(string commandStr) + { + if (!string.IsNullOrWhiteSpace(commandStr)) + { + string command = commandStr.Trim().ToUpperInvariant(); + string[] commandParts = command.Split(' '); + + if (commandParts.Count() > 1) + { + command = commandParts[0]; + } + + switch (command) + { + case "GET-HELP": + case "FIND-PACKAGE": + case "GET-PACKAGE": + case "INSTALL-PACKAGE": + case "UNINSTALL-PACKAGE": + case "UPDATE-PACKAGE": + case "SYNC-PACKAGE": + case "ADD-BINDINGREDIRECT": + case "GET-PROJECT": + case "REGISTER-TABEXPANSION": + // NugetCommand executed + PowerShellHostInstances |= 0b00100000; + break; + } + } + } + } +} diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 79766809812..f6587a9f77d 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -21,7 +21,6 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; using NuGet.Common; -using NuGet.Common.Telemetry; using NuGet.Configuration; using NuGet.PackageManagement; using NuGet.PackageManagement.VisualStudio; @@ -29,34 +28,17 @@ using NuGet.ProjectManagement; using NuGet.Protocol.Core.Types; using NuGet.VisualStudio; -using NuGet.VisualStudio.Console; using NuGet.VisualStudio.Telemetry; using Task = System.Threading.Tasks.Task; namespace NuGetConsole.Host.PowerShell.Implementation { - internal abstract class PowerShellHost : VSInstanceTelemetryConsts, IHost, IPathExpansion, IDisposable + internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable { private static readonly string AggregateSourceName = Resources.AggregateSourceName; private static readonly TimeSpan ExecuteInitScriptsRetryDelay = TimeSpan.FromMilliseconds(400); private static readonly int MaxTasks = 16; - private readonly static object TelemetryLock = new object(); - private static bool IsTelemetryEmitted; - private static int PmcExecutedCount; - private static int NonPmcExecutedCount; - - // There are 8 bit in byte. - // 0 - not used - // 1 - not used - // 2 - If nuget command used during current VS solution session. - // 3 - If init.ps1 is loaded during current VS solution session. - // 4 - First time load from PMUI - // 5 - First time load PMC - // 6 - LoadedFromPMUI: Indicates powershell host for PMUI already created, and stays that way until VS close. - // 7 - LoadedFromPMC: Indicates powershell host for PMC already created, and stays that way until VS close. - private static byte PowerShellHostInstances; - private Microsoft.VisualStudio.Threading.AsyncLazy _vsMonitorSelection; private IVsMonitorSelection VsMonitorSelection => ThreadHelper.JoinableTaskFactory.Run(_vsMonitorSelection.GetValueAsync); @@ -69,7 +51,7 @@ internal abstract class PowerShellHost : VSInstanceTelemetryConsts, IHost, IPath private readonly Lazy _settings; private readonly Lazy _sourceControlManagerProvider; private readonly Lazy _commonOperations; - private readonly Lazy _nugetTelemetryAggregate; + private readonly Lazy _vsPowerShellHostTelemetryEmit; private readonly Lazy _deleteOnRestartManager; private readonly Lazy _scriptExecutor; private const string ActivePackageSourceKey = "activePackageSource"; @@ -134,7 +116,7 @@ protected PowerShellHost(string name, IRestoreEvents restoreEvents, IRunspaceMan _sourceControlManagerProvider = new Lazy( () => ServiceLocator.GetInstanceSafe()); _commonOperations = new Lazy(() => ServiceLocator.GetInstanceSafe()); - _nugetTelemetryAggregate = new Lazy(() => ServiceLocator.GetInstanceSafe()); + _vsPowerShellHostTelemetryEmit = new Lazy(() => ServiceLocator.GetInstanceSafe()); _name = name; IsCommandEnabled = true; @@ -335,7 +317,7 @@ public void Initialize(IConsole console) await ExecuteInitScriptsAsync(); // Emit first time Powershell load event to find out later how many VS instance crash after loading powershell. - EmitPowerShellLoadedTelemetry(console is IWpfConsole); + _vsPowerShellHostTelemetryEmit.Value.EmitPowerShellLoadedTelemetry(console is IWpfConsole); // check if PMC console is actually opened, then only hook to solution load events. if (console is IWpfConsole) @@ -346,7 +328,8 @@ public void Initialize(IConsole console) _solutionManager.Value.SolutionClosing += (o, e) => { - EmitPowershellUsageTelemetry(true); + // Hook up solution events + _vsPowerShellHostTelemetryEmit.Value.EmitPowershellUsageTelemetry(true); }; _solutionManager.Value.SolutionClosed += (o, e) => @@ -379,32 +362,8 @@ public void Initialize(IConsole console) StartAsyncDefaultProjectUpdate(); - lock (TelemetryLock) - { - //There is edge case where PMC is opened but user doesn't execute any command on it. - if (console is IWpfConsole) - { - if ((PowerShellHostInstances & 0b00000001) == 0) - { - // First time load PMC - PowerShellHostInstances |= 0b00000100; - } - - // LoadedFromPMC - PowerShellHostInstances |= 0b00000001; - } - else - { - if ((PowerShellHostInstances & 0b00000010) == 0) - { - // First time load from PMUI - PowerShellHostInstances |= 0b00001000; - } - - // LoadedFromPMUI - PowerShellHostInstances |= 0b00000010; - } - } + // Record if PowerShellHost initiated from PMC or PMUI + _vsPowerShellHostTelemetryEmit.Value.CheckInitOrigin(console is IWpfConsole); } catch (Exception ex) { @@ -423,13 +382,8 @@ private void HandleSolutionOpened() { _scriptExecutor.Value.Reset(); - if (PmcExecutedCount > 0) - { - // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event aggregation before loading a solution. - EmitPowershellUsageTelemetry(false); - } - - IsTelemetryEmitted = false; + // Check if PMC is used before without any solution. + _vsPowerShellHostTelemetryEmit.Value.HandleSolutionOpenedEmit(); // Solution opened event is raised on the UI thread // Go off the UI thread before calling likely expensive call of ExecuteInitScriptsAsync @@ -541,8 +495,8 @@ private async Task ExecuteInitPs1Async(string installPath, PackageIdentity ident var scriptPath = Path.Combine(toolsPath, PowerShellScripts.Init); if (File.Exists(scriptPath)) { - // Init.ps1 is loaded - PowerShellHostInstances |= 0b00010000; + // Record if init.ps1 is loaded. + _vsPowerShellHostTelemetryEmit.Value.RecordInitPs1loaded(); if (_scriptExecutor.Value.TryMarkVisited(identity, PackageInitPS1State.FoundAndExecuted)) { @@ -604,26 +558,7 @@ public bool Execute(IConsole console, string command, params object[] inputs) } // Command can come here from both PMC and PM UI. - if (console is IWpfConsole) - { - lock (TelemetryLock) - { - // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files - // For PMC all installation done in one pass so no double counting. - PmcExecutedCount++; - } - } - else - { - lock (TelemetryLock) - { - // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files - // This one is called for both init.ps1 and install.ps1 seperately. - // For MSBuildNuGetProject projects install.ps1 can event furthure duplicate counted: MSBuildNuGetProject.cs#L377 - L396 - // Also this concern valid for dependent packages with *.ps1 files. - NonPmcExecutedCount++; - } - } + _vsPowerShellHostTelemetryEmit.Value.IncreaseCommandCounter(console is IWpfConsole); // since install.ps1/uninstall.ps1 could depend on init scripts, so we need to make sure // to run it once for each solution @@ -636,7 +571,7 @@ public bool Execute(IConsole console, string command, params object[] inputs) ActiveConsole = console; // Check and log if command is NugetCommand - IsNugetCommand(command); + _vsPowerShellHostTelemetryEmit.Value.IsNugetCommand(command); string fullCommand; if (ComplexCommand.AddLine(command, out fullCommand) @@ -927,84 +862,6 @@ private async Task> CompleteTaskAsync(List - { - { NugetPowershellPrefix + LoadFromPMC, isPMC} - }); - - TelemetryActivity.EmitTelemetryEvent(telemetryEvent); - } - } - } - - private void EmitPowershellUsageTelemetry(bool withSolution) - { - lock (TelemetryLock) - { - if (!IsTelemetryEmitted) - { - var telemetryEvent = new TelemetryEvent(PowerShellExecuteCommand, new Dictionary - { - { NuGetPMCExecuteCommandCount, PmcExecutedCount}, - { NuGetPMUIExecuteCommandCount, NonPmcExecutedCount}, - { NuGetCommandUsed, (PowerShellHostInstances & 0b00100000) == 0b00100000}, - { InitPs1Loaded, (PowerShellHostInstances & 0b00010000) == 0b00010000}, - { FirstTimeLoadedFromPMUI, (PowerShellHostInstances & 0b00001000) == 0b00001000}, - { FirstTimeLoadedFromPMC, (PowerShellHostInstances & 0b00000100) == 0b00000100}, - { LoadedFromPMUI, (PowerShellHostInstances & 0b00000010) == 0b00000010}, - { LoadedFromPMC, (PowerShellHostInstances & 0b00000001) == 0b00000001}, - { SolutionLoaded, withSolution} - }); - _nugetTelemetryAggregate.Value.AddSolutionTelemetryEvent(telemetryEvent); - - PmcExecutedCount = 0; - NonPmcExecutedCount = 0; - IsTelemetryEmitted = true; - - // Keep other 2 flags for powershell host are created flags, reset all others. - PowerShellHostInstances &= 0b00000011; - } - } - } - - private void IsNugetCommand(string commandStr) - { - if (!string.IsNullOrWhiteSpace(commandStr)) - { - string command = commandStr.Trim().ToUpperInvariant(); - string[] commandParts = command.Split(' '); - - if (commandParts.Count() > 1) - { - command = commandParts[0]; - } - - switch (command) - { - case "GET-HELP": - case "FIND-PACKAGE": - case "GET-PACKAGE": - case "INSTALL-PACKAGE": - case "UNINSTALL-PACKAGE": - case "UPDATE-PACKAGE": - case "SYNC-PACKAGE": - case "ADD-BINDINGREDIRECT": - case "GET-PROJECT": - case "REGISTER-TABEXPANSION": - // NugetCommand executed - PowerShellHostInstances |= 0b00100000; - break; - } - } - } - #region ITabExpansion public Task GetExpansionsAsync(string line, string lastWord, CancellationToken token) @@ -1092,7 +949,7 @@ public void Dispose() // Below emits telemetry in there was no solution was loaded at all, but if there were any solution then this one will ignored because actual data already emitted with solution SolutionClosing event previously. // If no solution loaded nor PMC is engaged at then this will be ignored. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore. - EmitPowershellUsageTelemetry(false); + _vsPowerShellHostTelemetryEmit.Value.EmitPowershellUsageTelemetry(false); } #endregion From 3a139c464d2600da6616d41dda611982c42e17af Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Mon, 9 Nov 2020 17:28:09 -0800 Subject: [PATCH 21/59] Fix formatting. --- .../VSSettings.cs | 38 ++++++++++--------- .../Telemetry/VsInstanceTelemetryConsts.cs | 4 +- .../Telemetry/VsIntanceTelemetryEmit.cs | 8 ++-- .../NuGetConsole.Host.PowerShell.csproj | 1 - .../PowerShellHost.cs | 29 +++++++------- .../PowershellTelemetryConsts.cs | 11 ------ 6 files changed, 40 insertions(+), 51 deletions(-) delete mode 100644 src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowershellTelemetryConsts.cs diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs index caf749244f0..36094aa6fd7 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; +using Microsoft.VisualStudio.Threading; using NuGet.Configuration; using NuGet.VisualStudio; using NuGet.VisualStudio.Telemetry; @@ -16,9 +17,10 @@ namespace NuGet.PackageManagement.VisualStudio public sealed class VSSettings : ISettings, IDisposable { private const string NuGetSolutionSettingsFolder = ".nuget"; + // to initialize SolutionSettings first time outside MEF constructor - private Tuple> _solutionSettings; - private VsIntanceTelemetryEmit _vSIntanceTelemetryEmit; + private Tuple> _solutionSettings; + private VsIntanceTelemetryEmit _vsIntanceTelemetryEmit; private ISettings SolutionSettings { @@ -53,7 +55,7 @@ public VSSettings(ISolutionManager solutionManager, VsIntanceTelemetryEmit vsInt SolutionManager.SolutionOpening += OnSolutionOpening; SolutionManager.SolutionOpened += OnSolutionOpened; SolutionManager.SolutionClosed += OnSolutionClosed; - _vSIntanceTelemetryEmit = vsIntanceTelemetryEmit; + _vsIntanceTelemetryEmit = vsIntanceTelemetryEmit; } private bool ResetSolutionSettingsIfNeeded() @@ -78,9 +80,9 @@ private bool ResetSolutionSettingsIfNeeded() // That however is not the case for solution close and same session close -> open events. Those will be on the UI thread. if (_solutionSettings == null || !string.Equals(root, _solutionSettings.Item1)) { - _solutionSettings = new Tuple>( + _solutionSettings = new Tuple>( item1: root, - item2: new Microsoft.VisualStudio.Threading.AsyncLazy(async () => + item2: new AsyncLazy(async () => { ISettings settings = null; try @@ -103,16 +105,6 @@ private bool ResetSolutionSettingsIfNeeded() return false; } - private void DetectSolutionSettingChange() - { - var hasChanged = ResetSolutionSettingsIfNeeded(); - - if (hasChanged) - { - SettingsChanged?.Invoke(this, EventArgs.Empty); - } - } - private void OnSolutionOpening(object sender, EventArgs e) { DetectSolutionSettingChange(); @@ -120,15 +112,25 @@ private void OnSolutionOpening(object sender, EventArgs e) private void OnSolutionOpened(object sender, EventArgs e) { - _vSIntanceTelemetryEmit.SolutionOpenedEmit(); + _vsIntanceTelemetryEmit.SolutionOpenedEmit(); } private void OnSolutionClosed(object sender, EventArgs e) { - _vSIntanceTelemetryEmit.EmitVSSolutionTelemetry(); + _vsIntanceTelemetryEmit.EmitVSSolutionTelemetry(); DetectSolutionSettingChange(); } + private void DetectSolutionSettingChange() + { + var hasChanged = ResetSolutionSettingsIfNeeded(); + + if (hasChanged) + { + SettingsChanged?.Invoke(this, EventArgs.Empty); + } + } + public SettingSection GetSection(string sectionName) { return SolutionSettings.GetSection(sectionName); @@ -167,7 +169,7 @@ public void Dispose() SolutionManager.SolutionOpening -= OnSolutionOpening; SolutionManager.SolutionOpened -= OnSolutionOpened; SolutionManager.SolutionClosed -= OnSolutionClosed; - _vSIntanceTelemetryEmit.EmitVSInstanceTelemetry(); + _vsIntanceTelemetryEmit.EmitVSInstanceTelemetry(); } // The value for SolutionSettings can't possibly be null, but it could be a read-only instance diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs index 5085720f8d6..00174f53c8c 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs @@ -26,7 +26,7 @@ public abstract class VsInstanceTelemetryConsts public const string NugetVSSolutionClose = "NugetVSSolutionClose"; public const string NugetVSInstanceClose = "NugetVSInstanceClose"; public const string SolutionCount = "SolutionCount"; - public const string PMCPowershellLoadedSolutionCount = "PMCPowershellLoadedSolutionCount"; - public const string PMUIPowershellLoadedSolutionCount = "PMUIPowershellLoadedSolutionCount"; + public const string PMCPowerShellLoadedSolutionCount = "PMCPowerShellLoadedSolutionCount"; + public const string PMUIPowerShellLoadedSolutionCount = "PMUIPowerShellLoadedSolutionCount"; } } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs index 5efe9698b50..9a764692a9a 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs @@ -124,7 +124,9 @@ public void EmitVSInstanceTelemetry() CombineAndEmitTelemetry(_vsInstanceTelemetryEmitQueue, NugetVSInstanceClose); } catch (Exception) - { } + { + // Currently do nothing. + } } private void EnqueueVSInstancePowershellTelemetry() @@ -134,8 +136,8 @@ private void EnqueueVSInstancePowershellTelemetry() _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMCExecuteCommandCount])); // PMC number of commands executed. _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMUIExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMUIExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMUIExecuteCommandCount])); // PMUI number of powershell commands executed. _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + SolutionCount, _solutionCount); - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + PMCPowershellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[SolutionLoaded] is bool && (bool)e[SolutionLoaded] && e[LoadedFromPMC] is bool).Count(e => (bool)e[LoadedFromPMC] == true)); // SolutionLoaded used here to remove edge case : PMC used before any solution is loaded - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + PMUIPowershellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[LoadedFromPMUI] is bool).Count(e => (bool)e[LoadedFromPMUI] == true)); + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + PMCPowerShellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[SolutionLoaded] is bool && (bool)e[SolutionLoaded] && e[LoadedFromPMC] is bool).Count(e => (bool)e[LoadedFromPMC] == true)); // SolutionLoaded used here to remove edge case : PMC used before any solution is loaded + _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + PMUIPowerShellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[LoadedFromPMUI] is bool).Count(e => (bool)e[LoadedFromPMUI] == true)); } // Instead of emitting one by one we combine them into single event and each event is a property of this single event. diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj index c992563ea56..6a252b731b3 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj @@ -34,7 +34,6 @@ CommonResources.cs - diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index f6587a9f77d..3f7478a8494 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -60,7 +60,6 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private const string DTEKey = "DTE"; private const string CancellationTokenKey = "CancellationTokenKey"; private const int ExecuteInitScriptsRetriesLimit = 50; - private string _activePackageSource; private string[] _packageSources; private readonly Lazy _dte; @@ -324,24 +323,21 @@ public void Initialize(IConsole console) { // Hook up solution events _solutionManager.Value.SolutionOpened += (_, __) => HandleSolutionOpened(); - } - - _solutionManager.Value.SolutionClosing += (o, e) => - { - // Hook up solution events - _vsPowerShellHostTelemetryEmit.Value.EmitPowershellUsageTelemetry(true); - }; - _solutionManager.Value.SolutionClosed += (o, e) => - { - if (console is IWpfConsole) + _solutionManager.Value.SolutionClosed += (o, e) => { UpdateWorkingDirectory(); DefaultProject = null; NuGetUIThreadHelper.JoinableTaskFactory.Run(CommandUiUtilities.InvalidateDefaultProjectAsync); - } + }; + } + + _solutionManager.Value.SolutionClosing += (o, e) => + { + // Hook up solution events, we emit telemetry data from current VS solution session. + _vsPowerShellHostTelemetryEmit.Value.EmitPowershellUsageTelemetry(true); }; _solutionManager.Value.NuGetProjectAdded += (o, e) => UpdateWorkingDirectoryAndAvailableProjects(); @@ -557,7 +553,7 @@ public bool Execute(IConsole console, string command, params object[] inputs) throw new ArgumentNullException(nameof(command)); } - // Command can come here from both PMC and PM UI. + // Increase command execution counters for PMC/PMUI _vsPowerShellHostTelemetryEmit.Value.IncreaseCommandCounter(console is IWpfConsole); // since install.ps1/uninstall.ps1 could depend on init scripts, so we need to make sure @@ -570,7 +566,7 @@ public bool Execute(IConsole console, string command, params object[] inputs) NuGetEventTrigger.Instance.TriggerEvent(NuGetEvent.PackageManagerConsoleCommandExecutionBegin); ActiveConsole = console; - // Check and log if command is NugetCommand + // Check and add to telemetery if a command is NugetCommand like 'install-package' etc. _vsPowerShellHostTelemetryEmit.Value.IsNugetCommand(command); string fullCommand; @@ -947,8 +943,9 @@ public void Dispose() _initScriptsLock.Dispose(); Runspace?.Dispose(); - // Below emits telemetry in there was no solution was loaded at all, but if there were any solution then this one will ignored because actual data already emitted with solution SolutionClosing event previously. - // If no solution loaded nor PMC is engaged at then this will be ignored. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore. + // Below emits telemetry in case there was no solution was loaded at all. + // But in case there were any solution then this one will ignored because actual data already emitted with solution SolutionClosing event previously. + // If no solution loaded nor PMC is engaged at then this will be ignored internally. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore then. _vsPowerShellHostTelemetryEmit.Value.EmitPowershellUsageTelemetry(false); } diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowershellTelemetryConsts.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowershellTelemetryConsts.cs deleted file mode 100644 index 4f37a80f2cb..00000000000 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowershellTelemetryConsts.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace NuGetConsole.Host.PowerShell -{ - public class PowershellTelemetryConsts - { - - //NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml - } -} From 1c3e9a199060c86dca2227f515f5ef44508f0483 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Mon, 9 Nov 2020 18:14:09 -0800 Subject: [PATCH 22/59] Remove manually typed consts with centralized one. --- .../NuGet.Console/Xamls/ConsoleContainer.xaml.cs | 15 ++++++--------- .../Telemetry/VsInstanceTelemetryConsts.cs | 1 + 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index d059c07a619..9b657502d5b 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -18,6 +18,7 @@ using NuGet.VisualStudio; using NuGet.VisualStudio.Common; using NuGet.VisualStudio.Internal.Contracts; +using NuGet.VisualStudio.Telemetry; namespace NuGetConsole { @@ -32,10 +33,6 @@ public sealed partial class ConsoleContainer : UserControl, IDisposable private int _windowLoadCount; private bool _isTelemetryEmitted; - private const string PackageManagerConsoleWindowsLoad = "PackageManagerConsoleWindowsLoad"; - private const string NuGetPMCWindowLoadCount = "NuGetPMCWindowLoadCount"; - private const string ReOpenAtStart = "ReOpenAtStart"; - public ConsoleContainer() { InitializeComponent(); @@ -103,10 +100,10 @@ public void Dispose() { // Work around to detect if PMC loaded automatically because it was last focused window. var reopenAtStart = IsLoaded; - var telemetryEvent = new TelemetryEvent(PackageManagerConsoleWindowsLoad, new Dictionary + var telemetryEvent = new TelemetryEvent(VsInstanceTelemetryConsts.PackageManagerConsoleWindowsLoad, new Dictionary { - { NuGetPMCWindowLoadCount, _windowLoadCount}, // Useful if PMC used without any solution load at all then VS instance closed. - { ReOpenAtStart, reopenAtStart} + { VsInstanceTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, // Useful if PMC used without any solution load at all then VS instance closed. + { VsInstanceTelemetryConsts.ReOpenAtStart, reopenAtStart} }); _nugetSolutionTelemetry.AddSolutionTelemetryEvent(telemetryEvent); @@ -132,9 +129,9 @@ void ConsoleContainer_Loaded(object sender, RoutedEventArgs e) private void EmitPowershellUsageTelemetry() { - var telemetryEvent = new TelemetryEvent(PackageManagerConsoleWindowsLoad, new Dictionary + var telemetryEvent = new TelemetryEvent(VsInstanceTelemetryConsts.PackageManagerConsoleWindowsLoad, new Dictionary { - { NuGetPMCWindowLoadCount, _windowLoadCount}, + { VsInstanceTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, }); _nugetSolutionTelemetry.AddSolutionTelemetryEvent(telemetryEvent); diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs index 00174f53c8c..6db84b4565a 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs @@ -19,6 +19,7 @@ public abstract class VsInstanceTelemetryConsts public const string FirstTimeLoadedFromPMUI = "FirstTimeLoadedFromPMUI"; public const string SolutionLoaded = "SolutionLoaded"; // PMC UI Console Container telemetry consts + public const string PackageManagerConsoleWindowsLoad = "PackageManagerConsoleWindowsLoad"; public const string NuGetPMCWindowLoadCount = "NuGetPMCWindowLoadCount"; public const string ReOpenAtStart = "ReOpenAtStart"; // Const name for emitting when VS solution close or VS instance close. From 3ad32a155727ccaf9d590a7483a44d3aee425109 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Mon, 9 Nov 2020 20:54:42 -0800 Subject: [PATCH 23/59] Move INuGetTelemetryAggregator so we don't have to publish new api. --- .../NuGet.Console/Xamls/ConsoleContainer.xaml.cs | 1 - .../Telemetry/INuGetTelemetryAggregator.cs | 4 +++- .../Telemetry/VsIntanceTelemetryEmit.cs | 1 - .../Telemetry/VsPowerShellHostTelemetryEmit.cs | 1 - .../NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt | 2 -- .../NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt | 2 -- .../PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt | 2 -- 7 files changed, 3 insertions(+), 10 deletions(-) rename src/{NuGet.Core/NuGet.Common => NuGet.Clients/NuGet.VisualStudio.Common}/Telemetry/INuGetTelemetryAggregator.cs (90%) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index 9b657502d5b..5623bc4a1a0 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -11,7 +11,6 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using NuGet.Common; -using NuGet.Common.Telemetry; using NuGet.PackageManagement; using NuGet.PackageManagement.UI; using NuGet.PackageManagement.VisualStudio; diff --git a/src/NuGet.Core/NuGet.Common/Telemetry/INuGetTelemetryAggregator.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryAggregator.cs similarity index 90% rename from src/NuGet.Core/NuGet.Common/Telemetry/INuGetTelemetryAggregator.cs rename to src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryAggregator.cs index d82079eee1d..f3796bdf04d 100644 --- a/src/NuGet.Core/NuGet.Common/Telemetry/INuGetTelemetryAggregator.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryAggregator.cs @@ -1,7 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -namespace NuGet.Common.Telemetry +using NuGet.Common; + +namespace NuGet.VisualStudio.Telemetry { // This one used for emitting aggregated telemetry at VS solution close or VS instance close. public interface INuGetTelemetryAggregator diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs index 9a764692a9a..89151dba74c 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs @@ -6,7 +6,6 @@ using System.ComponentModel.Composition; using System.Linq; using NuGet.Common; -using NuGet.Common.Telemetry; namespace NuGet.VisualStudio.Telemetry { diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs index 1b775681ff3..92a1002345c 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs @@ -6,7 +6,6 @@ using System.ComponentModel.Composition; using System.Linq; using NuGet.Common; -using NuGet.Common.Telemetry; namespace NuGet.VisualStudio.Telemetry { diff --git a/src/NuGet.Core/NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt index ff479d29a81..27e57301d51 100644 --- a/src/NuGet.Core/NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt @@ -2,5 +2,3 @@ NuGet.Common.NuGetLogCode.NU1010 = 1010 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1011 = 1011 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU5501 = 5501 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1012 = 1012 -> NuGet.Common.NuGetLogCode -NuGet.Common.Telemetry.INuGetTelemetryAggregator -NuGet.Common.Telemetry.INuGetTelemetryAggregator.AddSolutionTelemetryEvent(NuGet.Common.TelemetryEvent telemetryData) -> void diff --git a/src/NuGet.Core/NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt index ff479d29a81..27e57301d51 100644 --- a/src/NuGet.Core/NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt @@ -2,5 +2,3 @@ NuGet.Common.NuGetLogCode.NU1010 = 1010 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1011 = 1011 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU5501 = 5501 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1012 = 1012 -> NuGet.Common.NuGetLogCode -NuGet.Common.Telemetry.INuGetTelemetryAggregator -NuGet.Common.Telemetry.INuGetTelemetryAggregator.AddSolutionTelemetryEvent(NuGet.Common.TelemetryEvent telemetryData) -> void diff --git a/src/NuGet.Core/NuGet.Common/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Common/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt index ff479d29a81..27e57301d51 100644 --- a/src/NuGet.Core/NuGet.Common/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Common/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -2,5 +2,3 @@ NuGet.Common.NuGetLogCode.NU1010 = 1010 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1011 = 1011 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU5501 = 5501 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1012 = 1012 -> NuGet.Common.NuGetLogCode -NuGet.Common.Telemetry.INuGetTelemetryAggregator -NuGet.Common.Telemetry.INuGetTelemetryAggregator.AddSolutionTelemetryEvent(NuGet.Common.TelemetryEvent telemetryData) -> void From b236d9424055db1bd5cf6c4e7c9504c04780328e Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Tue, 10 Nov 2020 10:28:20 -0800 Subject: [PATCH 24/59] Clean up --- .../NuGet.Console/Xamls/ConsoleContainer.xaml.cs | 6 +++--- ...etTelemetryAggregator.cs => INuGetTelemetryCollector.cs} | 6 +++--- .../Telemetry/VsIntanceTelemetryEmit.cs | 4 ++-- .../Telemetry/VsPowerShellHostTelemetryEmit.cs | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) rename src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/{INuGetTelemetryAggregator.cs => INuGetTelemetryCollector.cs} (70%) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index 5623bc4a1a0..ece5c53ce79 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -28,7 +28,7 @@ public sealed partial class ConsoleContainer : UserControl, IDisposable { private INuGetSolutionManagerService _solutionManager; private IVsSolutionManager _iVsSolutionManager; - private INuGetTelemetryAggregator _nugetSolutionTelemetry; + private INuGetTelemetryCollector _nugetSolutionTelemetry; private int _windowLoadCount; private bool _isTelemetryEmitted; @@ -65,7 +65,7 @@ await System.Threading.Tasks.Task.Run( var packageRestoreManager = ServiceLocator.GetInstance(); var deleteOnRestartManager = ServiceLocator.GetInstance(); var shell = ServiceLocator.GetGlobalService(); - _nugetSolutionTelemetry = ServiceLocator.GetInstanceSafe(); + _nugetSolutionTelemetry = ServiceLocator.GetInstance(); await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -101,7 +101,7 @@ public void Dispose() var reopenAtStart = IsLoaded; var telemetryEvent = new TelemetryEvent(VsInstanceTelemetryConsts.PackageManagerConsoleWindowsLoad, new Dictionary { - { VsInstanceTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, // Useful if PMC used without any solution load at all then VS instance closed. + { VsInstanceTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, { VsInstanceTelemetryConsts.ReOpenAtStart, reopenAtStart} }); diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryAggregator.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs similarity index 70% rename from src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryAggregator.cs rename to src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs index f3796bdf04d..025850019cc 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryAggregator.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs @@ -6,10 +6,10 @@ namespace NuGet.VisualStudio.Telemetry { // This one used for emitting aggregated telemetry at VS solution close or VS instance close. - public interface INuGetTelemetryAggregator + public interface INuGetTelemetryCollector { - /// Add a to telemetry list which will be aggregated and sent later. - /// Telemetry event to send. + /// Add a to telemetry list which will be aggregated and emitted later. + /// Telemetry event to add into aggregation. void AddSolutionTelemetryEvent(TelemetryEvent telemetryData); } } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs index 89151dba74c..d452917cb82 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs @@ -9,10 +9,10 @@ namespace NuGet.VisualStudio.Telemetry { - [Export(typeof(INuGetTelemetryAggregator))] + [Export(typeof(INuGetTelemetryCollector))] [Export(typeof(VsIntanceTelemetryEmit))] [PartCreationPolicy(CreationPolicy.Shared)] - public sealed class VsIntanceTelemetryEmit : VsInstanceTelemetryConsts, INuGetTelemetryAggregator + public sealed class VsIntanceTelemetryEmit : VsInstanceTelemetryConsts, INuGetTelemetryCollector { // _solutionTelemetryEvents hold telemetry for current VS solution session. private List _vsSolutionTelemetryEvents; diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs index 92a1002345c..3f60bec7884 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs @@ -17,7 +17,7 @@ public class VsPowerShellHostTelemetryEmit : VsInstanceTelemetryConsts private bool _isTelemetryEmitted; private int _pmcExecutedCount; private int _nonPmcExecutedCount; - private readonly Lazy _nugetTelemetryAggregate; + private readonly Lazy _nugetTelemetryAggregate; // There are 8 bit in byte. // 0 - not used @@ -32,7 +32,7 @@ public class VsPowerShellHostTelemetryEmit : VsInstanceTelemetryConsts public VsPowerShellHostTelemetryEmit() { - _nugetTelemetryAggregate = new Lazy(() => ServiceLocator.GetInstanceSafe()); + _nugetTelemetryAggregate = new Lazy(() => ServiceLocator.GetInstanceSafe()); } public void CheckInitOrigin(bool isPMC) From 6509fdc0a6740d6837ed2ffe8dcf64f4abeb8f32 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Tue, 10 Nov 2020 12:32:28 -0800 Subject: [PATCH 25/59] Fix typos --- .../Telemetry/VsInstanceTelemetryConsts.cs | 8 +-- .../Telemetry/VsIntanceTelemetryEmit.cs | 52 +++++++++---------- .../VsPowerShellHostTelemetryEmit.cs | 5 +- 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs index 6db84b4565a..dba3a61a4b5 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs @@ -6,7 +6,7 @@ namespace NuGet.VisualStudio.Telemetry public abstract class VsInstanceTelemetryConsts { // PMC, PMUI powershell telemetry consts - public const string PowerShellExecuteCommand = "PowerShellExecuteCommand"; + public const string PowerShellExecuteCommand = "PowerShellExecuteCommand"; public const string NuGetPMCExecuteCommandCount = "NuGetPMCExecuteCommandCount"; public const string NuGetPMUIExecuteCommandCount = "NuGetPMUIExecuteCommandCount"; public const string NuGetPowerShellLoaded = "NuGetPowerShellLoaded"; @@ -23,9 +23,9 @@ public abstract class VsInstanceTelemetryConsts public const string NuGetPMCWindowLoadCount = "NuGetPMCWindowLoadCount"; public const string ReOpenAtStart = "ReOpenAtStart"; // Const name for emitting when VS solution close or VS instance close. - public const string NugetPowershellPrefix = "NugetPowershell."; // Using prefix prevent accidental same name property collission from different type telemetry. - public const string NugetVSSolutionClose = "NugetVSSolutionClose"; - public const string NugetVSInstanceClose = "NugetVSInstanceClose"; + public const string NuGetPowershellPrefix = "NuGetPowershellPrefix."; // Using prefix prevent accidental same name property collission from different type telemetry. + public const string NuGetVSSolutionClose = "NuGetVSSolutionClose"; + public const string NuGetVSInstanceClose = "NuGetVSInstanceClose"; public const string SolutionCount = "SolutionCount"; public const string PMCPowerShellLoadedSolutionCount = "PMCPowerShellLoadedSolutionCount"; public const string PMUIPowerShellLoadedSolutionCount = "PMUIPowerShellLoadedSolutionCount"; diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs index d452917cb82..71ef27e9b27 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs @@ -64,7 +64,7 @@ public void EmitVSSolutionTelemetry() _vsSolutionTelemetryEvents.Clear(); // Actual emit - CombineAndEmitTelemetry(_vsSolutionTelemetryEmitQueue, NugetVSSolutionClose); + CombineAndEmitTelemetry(_vsSolutionTelemetryEmitQueue, NuGetVSSolutionClose); _vsSolutionTelemetryEmitQueue.Clear(); } catch (Exception) @@ -80,14 +80,14 @@ private void EnqueueVSSolutionPowershellTelemetry() // If powershell(PMC/PMUI) is not loaded at all then we need to create default telemetry event which will be emitted. if (vsSolutionPowershellTelemetry == null) { - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, 0); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMUIExecuteCommandCount, 0); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetCommandUsed, false); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + InitPs1Loaded, false); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMC, false); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMUI, false); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMC, false); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMUI, false); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, 0); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, 0); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetCommandUsed, false); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1Loaded, false); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMC, false); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMUI, false); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMC, false); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMUI, false); } else { @@ -97,17 +97,17 @@ private void EnqueueVSSolutionPowershellTelemetry() return; } - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount]); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMUIExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMUIExecuteCommandCount]); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetCommandUsed, vsSolutionPowershellTelemetry[NuGetCommandUsed]); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + InitPs1Loaded, vsSolutionPowershellTelemetry[InitPs1Loaded]); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMC, vsSolutionPowershellTelemetry[LoadedFromPMC]); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + LoadedFromPMUI, vsSolutionPowershellTelemetry[LoadedFromPMUI]); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMC, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMC]); - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + FirstTimeLoadedFromPMUI, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMUI]); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount]); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMUIExecuteCommandCount]); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetCommandUsed, vsSolutionPowershellTelemetry[NuGetCommandUsed]); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1Loaded, vsSolutionPowershellTelemetry[InitPs1Loaded]); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMC, vsSolutionPowershellTelemetry[LoadedFromPMC]); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMUI, vsSolutionPowershellTelemetry[LoadedFromPMUI]); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMC, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMC]); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMUI, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMUI]); } - _vsSolutionTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCWindowLoadCount, _vsSolutionTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCWindowLoadCount, _vsSolutionTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); } // Emit VS solution session telemetry when VS instance is closed. @@ -120,7 +120,7 @@ public void EmitVSInstanceTelemetry() // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. // Using prefix avoid collision of property names from different types of telemetry. - CombineAndEmitTelemetry(_vsInstanceTelemetryEmitQueue, NugetVSInstanceClose); + CombineAndEmitTelemetry(_vsInstanceTelemetryEmitQueue, NuGetVSInstanceClose); } catch (Exception) { @@ -130,13 +130,13 @@ public void EmitVSInstanceTelemetry() private void EnqueueVSInstancePowershellTelemetry() { - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + ReOpenAtStart, _vsInstanceTelemetryEvents.Where(e => e[ReOpenAtStart] != null).Any()); // Whether PMC window re-open at start by default next time VS open? - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCWindowLoadCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); // PMC Window load count - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMCExecuteCommandCount])); // PMC number of commands executed. - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + NuGetPMUIExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMUIExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMUIExecuteCommandCount])); // PMUI number of powershell commands executed. - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + SolutionCount, _solutionCount); - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + PMCPowerShellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[SolutionLoaded] is bool && (bool)e[SolutionLoaded] && e[LoadedFromPMC] is bool).Count(e => (bool)e[LoadedFromPMC] == true)); // SolutionLoaded used here to remove edge case : PMC used before any solution is loaded - _vsInstanceTelemetryEmitQueue.Add(NugetPowershellPrefix + PMUIPowerShellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[LoadedFromPMUI] is bool).Count(e => (bool)e[LoadedFromPMUI] == true)); + _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + ReOpenAtStart, _vsInstanceTelemetryEvents.Where(e => e[ReOpenAtStart] != null).Any()); // Whether PMC window re-open at start by default next time VS open? + _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCWindowLoadCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); // PMC Window load count + _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMCExecuteCommandCount])); // PMC number of commands executed. + _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMUIExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMUIExecuteCommandCount])); // PMUI number of powershell commands executed. + _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + SolutionCount, _solutionCount); + _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + PMCPowerShellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[SolutionLoaded] is bool && (bool)e[SolutionLoaded] && e[LoadedFromPMC] is bool).Count(e => (bool)e[LoadedFromPMC] == true)); // SolutionLoaded used here to remove edge case : PMC used before any solution is loaded + _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + PMUIPowerShellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[LoadedFromPMUI] is bool).Count(e => (bool)e[LoadedFromPMUI] == true)); } // Instead of emitting one by one we combine them into single event and each event is a property of this single event. diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs index 3f60bec7884..a1aa3a89d12 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs @@ -39,7 +39,6 @@ public void CheckInitOrigin(bool isPMC) { lock (_telemetryLock) { - //There is edge case where PMC is opened but user doesn't execute any command on it. if (isPMC) { if ((PowerShellHostInstances & 0b00000001) == 0) @@ -105,12 +104,12 @@ public void EmitPowerShellLoadedTelemetry(bool isPMC) { lock (_telemetryLock) { - // This is PowerShellHost load first time. + // This is PowerShellHost load first time, let's emit this to find out later how many VS instance crash after loading powershell. if ((PowerShellHostInstances & 0b00000011) == 0) { var telemetryEvent = new TelemetryEvent(NuGetPowerShellLoaded, new Dictionary { - { NugetPowershellPrefix + LoadFromPMC, isPMC} + { NuGetPowershellPrefix + LoadFromPMC, isPMC} }); TelemetryActivity.EmitTelemetryEvent(telemetryEvent); From 3bacebb4595ba35cb3d7df83fb4e0d145f6f8244 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Thu, 12 Nov 2020 10:40:41 -0800 Subject: [PATCH 26/59] Start recording PMC usage without any solution load as seperate 'NuGetVSInstanceClose' event with 'SolutionLoaded' = false flag. Otherwise it's almost impossible to separate them. --- .../Telemetry/VsInstanceTelemetryConsts.cs | 15 ++- .../Telemetry/VsIntanceTelemetryEmit.cs | 55 +++++++-- .../VsPowerShellHostTelemetryEmit.cs | 115 ++++++++++++------ .../PowerShellHost.cs | 23 ++-- 4 files changed, 146 insertions(+), 62 deletions(-) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs index dba3a61a4b5..f58c4d9df37 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs @@ -6,22 +6,25 @@ namespace NuGet.VisualStudio.Telemetry public abstract class VsInstanceTelemetryConsts { // PMC, PMUI powershell telemetry consts - public const string PowerShellExecuteCommand = "PowerShellExecuteCommand"; public const string NuGetPMCExecuteCommandCount = "NuGetPMCExecuteCommandCount"; public const string NuGetPMUIExecuteCommandCount = "NuGetPMUIExecuteCommandCount"; - public const string NuGetPowerShellLoaded = "NuGetPowerShellLoaded"; - public const string LoadFromPMC = "LoadFromPMC"; public const string NuGetCommandUsed = "NuGetCommandUsed"; - public const string InitPs1Loaded = "InitPs1Loaded"; - public const string LoadedFromPMC = "LoadedFromPMC"; - public const string FirstTimeLoadedFromPMC = "FirstTimeLoadedFromPMC"; + public const string InitPs1LoadPMUI = "InitPs1LoadPMUI"; + public const string InitPs1LoadPMC = "InitPs1LoadPMC"; + public const string InitPs1LoadedFromPMCFirst = "InitPs1LoadedFromPMCFirst"; public const string LoadedFromPMUI = "LoadedFromPMUI"; public const string FirstTimeLoadedFromPMUI = "FirstTimeLoadedFromPMUI"; + public const string LoadedFromPMC = "LoadedFromPMC"; + public const string FirstTimeLoadedFromPMC = "FirstTimeLoadedFromPMC"; public const string SolutionLoaded = "SolutionLoaded"; + public const string PowerShellExecuteCommand = "PowerShellExecuteCommand"; + public const string NuGetPowerShellLoaded = "NuGetPowerShellLoaded"; + // PMC UI Console Container telemetry consts public const string PackageManagerConsoleWindowsLoad = "PackageManagerConsoleWindowsLoad"; public const string NuGetPMCWindowLoadCount = "NuGetPMCWindowLoadCount"; public const string ReOpenAtStart = "ReOpenAtStart"; + // Const name for emitting when VS solution close or VS instance close. public const string NuGetPowershellPrefix = "NuGetPowershellPrefix."; // Using prefix prevent accidental same name property collission from different type telemetry. public const string NuGetVSSolutionClose = "NuGetVSSolutionClose"; diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs index 71ef27e9b27..e05aeacda9c 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs @@ -39,16 +39,40 @@ public void AddSolutionTelemetryEvent(TelemetryEvent telemetryData) public void SolutionOpenedEmit() { - // PMC used before any solution is loaded, let's emit what we have before loading a solution. - // Used means at least one powershell command executed, otherwise telemetry(NuGetPMCWindowLoadCount and FirstTimeLoadedFromPMC) is merged with first opened solution metric rather than sending separate nugetvssolutionclose telemetry with no data. - if (_solutionCount == 0 && _vsSolutionTelemetryEvents.Any(e => e[VsInstanceTelemetryConsts.NuGetPMCExecuteCommandCount] is int && (int)e[NuGetPMCExecuteCommandCount] > 0)) + try { - EmitVSSolutionTelemetry(); + // Handle edge cases. + EmitPMCUsedWithoutSolution(); + } + catch (Exception) + { + // Currently do nothing. } + _vsSolutionTelemetryEvents.Clear(); _solutionCount++; } + private void EmitPMCUsedWithoutSolution() + { + // Edge case: PMC window can be opened without any solution at all, but sometimes TelemetryActivity.NuGetTelemetryService is not ready yet. + // In general we want to emit this telemetry right away. + var nuGetPowerShellLoadedEvent = _vsInstanceTelemetryEvents.FirstOrDefault(e => e.Name == VsInstanceTelemetryConsts.NuGetPowerShellLoaded); + + if (nuGetPowerShellLoadedEvent != null) + { + TelemetryActivity.EmitTelemetryEvent(nuGetPowerShellLoadedEvent); + _vsInstanceTelemetryEvents.Remove(nuGetPowerShellLoadedEvent); + } + + + // If there is not emitted PowerShellExecuteCommand telemetry. + if (_vsSolutionTelemetryEvents.Any(e => e.Name == PowerShellExecuteCommand)) + { + EmitVSSolutionTelemetry(); + } + } + // Emit VS solution session telemetry when solution is closed. public void EmitVSSolutionTelemetry() { @@ -61,16 +85,17 @@ public void EmitVSSolutionTelemetry() // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. // Using prefix avoid collision of property names from different types of telemetry. - _vsSolutionTelemetryEvents.Clear(); - // Actual emit CombineAndEmitTelemetry(_vsSolutionTelemetryEmitQueue, NuGetVSSolutionClose); - _vsSolutionTelemetryEmitQueue.Clear(); + } catch (Exception) { // Currently do nothing. } + + _vsSolutionTelemetryEvents.Clear(); + _vsSolutionTelemetryEmitQueue.Clear(); } private void EnqueueVSSolutionPowershellTelemetry() @@ -83,16 +108,18 @@ private void EnqueueVSSolutionPowershellTelemetry() _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, 0); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, 0); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetCommandUsed, false); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1Loaded, false); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadedFromPMCFirst, false); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadPMC, false); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMC, false); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMUI, false); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMC, false); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMUI, false); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + SolutionLoaded, true); } else { - // PMC opened, but no command executed nor any solution opened. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore. - if (_solutionCount == 0 && (int)vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount] == 0) + // PMC opened, but no command executed nor any solution was loaded. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore. + if (!(bool)vsSolutionPowershellTelemetry[SolutionLoaded] && (int)vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount] == 0) { return; } @@ -100,11 +127,13 @@ private void EnqueueVSSolutionPowershellTelemetry() _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount]); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMUIExecuteCommandCount]); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetCommandUsed, vsSolutionPowershellTelemetry[NuGetCommandUsed]); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1Loaded, vsSolutionPowershellTelemetry[InitPs1Loaded]); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadedFromPMCFirst, vsSolutionPowershellTelemetry[InitPs1LoadedFromPMCFirst]); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadPMC, vsSolutionPowershellTelemetry[InitPs1LoadPMC]); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMC, vsSolutionPowershellTelemetry[LoadedFromPMC]); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMUI, vsSolutionPowershellTelemetry[LoadedFromPMUI]); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMC, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMC]); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMUI, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMUI]); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + SolutionLoaded, vsSolutionPowershellTelemetry[SolutionLoaded]); } _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCWindowLoadCount, _vsSolutionTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); @@ -115,7 +144,11 @@ public void EmitVSInstanceTelemetry() { try { + // Handle edge cases. + EmitPMCUsedWithoutSolution(); + EnqueueVSInstancePowershellTelemetry(); + // Add other telemetry types here in the future. You can emit many different types of telemetry here. // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. // Using prefix avoid collision of property names from different types of telemetry. diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs index a1aa3a89d12..39083da000e 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs @@ -19,15 +19,15 @@ public class VsPowerShellHostTelemetryEmit : VsInstanceTelemetryConsts private int _nonPmcExecutedCount; private readonly Lazy _nugetTelemetryAggregate; - // There are 8 bit in byte. - // 0 - not used - // 1 - not used - // 2 - If nuget command used during current VS solution session. - // 3 - If init.ps1 is loaded during current VS solution session. - // 4 - First time load from PMUI - // 5 - First time load PMC - // 6 - LoadedFromPMUI: Indicates powershell host for PMUI already created, and stays that way until VS close. - // 7 - LoadedFromPMC: Indicates powershell host for PMC already created, and stays that way until VS close. + // There are 8 bits in byte which used as boolean flags. + // 0 - Did any nuget command execute during current VS solution session? + // 1 - Did init.ps1 is load during current VS solution session from PMUI? + // 2 - Did init.ps1 is load during current VS solution session from PMC? + // 3 - Did init.ps1 load first from PMC or PMUI for above 2 cases? + // 4 - Did PowerShellHost for PMUI created during current VS solution session first time? + // 5 - Did PowerShellHost for PMC created during current VS solution session first time? + // 6 - Did PowerShellHost for PMUI created during current VS instance session? + // 7 - Did PowerShellHost for PMC created during current VS instance session? private static byte PowerShellHostInstances; public VsPowerShellHostTelemetryEmit() @@ -35,13 +35,13 @@ public VsPowerShellHostTelemetryEmit() _nugetTelemetryAggregate = new Lazy(() => ServiceLocator.GetInstanceSafe()); } - public void CheckInitOrigin(bool isPMC) + public void RecordPSHostInitializeOrigin(bool isPMC) { lock (_telemetryLock) { if (isPMC) { - if ((PowerShellHostInstances & 0b00000001) == 0) + if (TestAnyBitNotSet(PowerShellHostInstances, 0b00000001)) { // First time load PMC PowerShellHostInstances |= 0b00000100; @@ -52,7 +52,7 @@ public void CheckInitOrigin(bool isPMC) } else { - if ((PowerShellHostInstances & 0b00000010) == 0) + if (TestAnyBitNotSet(PowerShellHostInstances, 0b00000010)) { // First time load from PMUI PowerShellHostInstances |= 0b00001000; @@ -66,18 +66,15 @@ public void CheckInitOrigin(bool isPMC) public void IncreaseCommandCounter(bool isPMC) { - if (isPMC) + lock (_telemetryLock) { - lock (_telemetryLock) + if (isPMC) { // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files // For PMC all installation done in one pass so no double counting. _pmcExecutedCount++; } - } - else - { - lock (_telemetryLock) + else { // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files // This one is called for both init.ps1 and install.ps1 seperately. @@ -99,20 +96,37 @@ public void HandleSolutionOpenedEmit() _isTelemetryEmitted = false; } + public void HandleSolutionClosingEmit() + { + EmitPowershellUsageTelemetry(true); + + // PMC can still used after solution is closed, so reset _isTelemetryEmitted make it possible to remaining telemetry. + _isTelemetryEmitted = false; + } public void EmitPowerShellLoadedTelemetry(bool isPMC) { lock (_telemetryLock) { // This is PowerShellHost load first time, let's emit this to find out later how many VS instance crash after loading powershell. - if ((PowerShellHostInstances & 0b00000011) == 0) + if (TestAnyBitNotSet(PowerShellHostInstances, 0b00000011)) { var telemetryEvent = new TelemetryEvent(NuGetPowerShellLoaded, new Dictionary { - { NuGetPowershellPrefix + LoadFromPMC, isPMC} + { NuGetPowershellPrefix + LoadedFromPMC, isPMC} }); - TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + + // Edge case: PMC window can be opened without any solution at all, but sometimes TelemetryActivity.NuGetTelemetryService is not ready yet. + // In general we want to emit this telemetry right away. + if (TelemetryActivity.NuGetTelemetryService != null) + { + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + } + else + { + _nugetTelemetryAggregate.Value.AddSolutionTelemetryEvent(telemetryEvent); + } } } } @@ -127,34 +141,57 @@ public void EmitPowershellUsageTelemetry(bool withSolution) { { NuGetPMCExecuteCommandCount, _pmcExecutedCount}, { NuGetPMUIExecuteCommandCount, _nonPmcExecutedCount}, - { NuGetCommandUsed, (PowerShellHostInstances & 0b00100000) == 0b00100000}, - { InitPs1Loaded, (PowerShellHostInstances & 0b00010000) == 0b00010000}, - { FirstTimeLoadedFromPMUI, (PowerShellHostInstances & 0b00001000) == 0b00001000}, - { FirstTimeLoadedFromPMC, (PowerShellHostInstances & 0b00000100) == 0b00000100}, - { LoadedFromPMUI, (PowerShellHostInstances & 0b00000010) == 0b00000010}, - { LoadedFromPMC, (PowerShellHostInstances & 0b00000001) == 0b00000001}, + { NuGetCommandUsed, TestAllBitsSet(PowerShellHostInstances, 0b10000000) }, + { InitPs1LoadPMUI, TestAllBitsSet(PowerShellHostInstances, 0b01000000) }, + { InitPs1LoadPMC, TestAllBitsSet(PowerShellHostInstances, 0b00100000) }, + { InitPs1LoadedFromPMCFirst, TestAllBitsSet(PowerShellHostInstances, 0b00010000) }, + { FirstTimeLoadedFromPMUI, TestAllBitsSet(PowerShellHostInstances, 0b00001000) }, + { FirstTimeLoadedFromPMC, TestAllBitsSet(PowerShellHostInstances, 0b00000100) }, + { LoadedFromPMUI, TestAllBitsSet(PowerShellHostInstances, 0b00000010) }, + { LoadedFromPMC, TestAllBitsSet(PowerShellHostInstances, 0b00000001) }, { SolutionLoaded, withSolution} }); _nugetTelemetryAggregate.Value.AddSolutionTelemetryEvent(telemetryEvent); _pmcExecutedCount = 0; _nonPmcExecutedCount = 0; - _isTelemetryEmitted = true; - // Keep other 2 flags for powershell host are created flags, reset all others. + // Keep 2 flags for current VS instance,but reset all others because they're for current VS session. PowerShellHostInstances &= 0b00000011; } + + _isTelemetryEmitted = true; } } - public void RecordInitPs1loaded() + public void RecordInitPs1loaded(bool isPMC) { - // Init.ps1 is loaded - PowerShellHostInstances |= 0b00010000; + // Test bit flag for if init.ps1 already loaded from PMC or PMUI + if (TestAnyBitNotSet(PowerShellHostInstances, 0b01100000) && isPMC) + { + // if not then set initialization origin bit + PowerShellHostInstances |= 0b00010000; + } + + if (isPMC) + { + PowerShellHostInstances |= 0b00100000; + } + else + { + PowerShellHostInstances |= 0b01000000; + } + } - public void IsNugetCommand(string commandStr) + public void IsNuGetCommand(string commandStr) { + if (TestAllBitsSet(PowerShellHostInstances, 0b10000000)) + { + // NuGetCommand used and flag is already set + return; + } + if (!string.IsNullOrWhiteSpace(commandStr)) { string command = commandStr.Trim().ToUpperInvariant(); @@ -178,10 +215,20 @@ public void IsNugetCommand(string commandStr) case "GET-PROJECT": case "REGISTER-TABEXPANSION": // NugetCommand executed - PowerShellHostInstances |= 0b00100000; + PowerShellHostInstances |= 0b10000000; break; } } } + + private bool TestAllBitsSet(byte input, byte mask) + { + return (input & mask) == mask; + } + + private bool TestAnyBitNotSet(byte input, byte mask) + { + return (input & mask) == 0; + } } } diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 3f7478a8494..08e92975615 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -318,6 +318,12 @@ public void Initialize(IConsole console) // Emit first time Powershell load event to find out later how many VS instance crash after loading powershell. _vsPowerShellHostTelemetryEmit.Value.EmitPowerShellLoadedTelemetry(console is IWpfConsole); + _solutionManager.Value.SolutionOpening += (o, e) => + { + // Check if PMC is used before without any solution. + _vsPowerShellHostTelemetryEmit.Value.HandleSolutionOpenedEmit(); + }; + // check if PMC console is actually opened, then only hook to solution load events. if (console is IWpfConsole) { @@ -337,7 +343,7 @@ public void Initialize(IConsole console) _solutionManager.Value.SolutionClosing += (o, e) => { // Hook up solution events, we emit telemetry data from current VS solution session. - _vsPowerShellHostTelemetryEmit.Value.EmitPowershellUsageTelemetry(true); + _vsPowerShellHostTelemetryEmit.Value.HandleSolutionClosingEmit(); }; _solutionManager.Value.NuGetProjectAdded += (o, e) => UpdateWorkingDirectoryAndAvailableProjects(); @@ -359,7 +365,7 @@ public void Initialize(IConsole console) StartAsyncDefaultProjectUpdate(); // Record if PowerShellHost initiated from PMC or PMUI - _vsPowerShellHostTelemetryEmit.Value.CheckInitOrigin(console is IWpfConsole); + _vsPowerShellHostTelemetryEmit.Value.RecordPSHostInitializeOrigin(console is IWpfConsole); } catch (Exception ex) { @@ -378,9 +384,6 @@ private void HandleSolutionOpened() { _scriptExecutor.Value.Reset(); - // Check if PMC is used before without any solution. - _vsPowerShellHostTelemetryEmit.Value.HandleSolutionOpenedEmit(); - // Solution opened event is raised on the UI thread // Go off the UI thread before calling likely expensive call of ExecuteInitScriptsAsync // Also, it uses semaphores, do not call it from the UI thread @@ -492,7 +495,7 @@ private async Task ExecuteInitPs1Async(string installPath, PackageIdentity ident if (File.Exists(scriptPath)) { // Record if init.ps1 is loaded. - _vsPowerShellHostTelemetryEmit.Value.RecordInitPs1loaded(); + _vsPowerShellHostTelemetryEmit.Value.RecordInitPs1loaded(_activeConsole is IWpfConsole); if (_scriptExecutor.Value.TryMarkVisited(identity, PackageInitPS1State.FoundAndExecuted)) { @@ -506,8 +509,6 @@ private async Task ExecuteInitPs1Async(string installPath, PackageIdentity ident request.BuildInput(), outputResults: true); - - return; } } @@ -556,6 +557,9 @@ public bool Execute(IConsole console, string command, params object[] inputs) // Increase command execution counters for PMC/PMUI _vsPowerShellHostTelemetryEmit.Value.IncreaseCommandCounter(console is IWpfConsole); + // Check and add to telemetery if a command is NugetCommand like 'install-package' etc. + _vsPowerShellHostTelemetryEmit.Value.IsNuGetCommand(command); + // since install.ps1/uninstall.ps1 could depend on init scripts, so we need to make sure // to run it once for each solution NuGetUIThreadHelper.JoinableTaskFactory.Run(async () => @@ -566,9 +570,6 @@ public bool Execute(IConsole console, string command, params object[] inputs) NuGetEventTrigger.Instance.TriggerEvent(NuGetEvent.PackageManagerConsoleCommandExecutionBegin); ActiveConsole = console; - // Check and add to telemetery if a command is NugetCommand like 'install-package' etc. - _vsPowerShellHostTelemetryEmit.Value.IsNugetCommand(command); - string fullCommand; if (ComplexCommand.AddLine(command, out fullCommand) && !string.IsNullOrEmpty(fullCommand)) From 04b0b2269a1f7b8fb32f0c1cad1cde590fa0ba09 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Thu, 12 Nov 2020 11:53:36 -0800 Subject: [PATCH 27/59] Clean up --- .../Telemetry/VsIntanceTelemetryEmit.cs | 28 +++++++++++-------- .../VsPowerShellHostTelemetryEmit.cs | 3 +- .../GlobalSuppressions.cs | 1 - .../PowerShellHost.cs | 11 ++++---- ...eManagement.PowerShellCmdlets.dll-Help.xml | 2 +- 5 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs index e05aeacda9c..fca575bcce7 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs @@ -31,6 +31,7 @@ public VsIntanceTelemetryEmit() _vsInstanceTelemetryEmitQueue = new Dictionary(); } + // Adds telemetry into list which will be aggregated by end of VS solution sessions or VS instance session. public void AddSolutionTelemetryEvent(TelemetryEvent telemetryData) { _vsSolutionTelemetryEvents.Add(telemetryData); @@ -55,8 +56,8 @@ public void SolutionOpenedEmit() private void EmitPMCUsedWithoutSolution() { - // Edge case: PMC window can be opened without any solution at all, but sometimes TelemetryActivity.NuGetTelemetryService is not ready yet. - // In general we want to emit this telemetry right away. + // Edge case: PMC window can be opened without any solution at all, but sometimes TelemetryActivity.NuGetTelemetryService is not ready yet when PMC open. + // In general we want to emit this telemetry right away, but not possible then emit later. var nuGetPowerShellLoadedEvent = _vsInstanceTelemetryEvents.FirstOrDefault(e => e.Name == VsInstanceTelemetryConsts.NuGetPowerShellLoaded); if (nuGetPowerShellLoadedEvent != null) @@ -65,7 +66,6 @@ private void EmitPMCUsedWithoutSolution() _vsInstanceTelemetryEvents.Remove(nuGetPowerShellLoadedEvent); } - // If there is not emitted PowerShellExecuteCommand telemetry. if (_vsSolutionTelemetryEvents.Any(e => e.Name == PowerShellExecuteCommand)) { @@ -81,13 +81,12 @@ public void EmitVSSolutionTelemetry() // Queue all different types of telemetries and do some processing prior to emit. EnqueueVSSolutionPowershellTelemetry(); - // Add other telemetry types here in the future. You can emit many different types of telemetry other than powershell here. + // Add other telemetry type queuing here in the future. You can emit many different types of telemetry other than powershell here. // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. // Using prefix avoid collision of property names from different types of telemetry. // Actual emit CombineAndEmitTelemetry(_vsSolutionTelemetryEmitQueue, NuGetVSSolutionClose); - } catch (Exception) { @@ -149,7 +148,7 @@ public void EmitVSInstanceTelemetry() EnqueueVSInstancePowershellTelemetry(); - // Add other telemetry types here in the future. You can emit many different types of telemetry here. + // Add other telemetry type queuing here in the future. You can emit many different types of telemetry here. // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. // Using prefix avoid collision of property names from different types of telemetry. @@ -163,12 +162,19 @@ public void EmitVSInstanceTelemetry() private void EnqueueVSInstancePowershellTelemetry() { - _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + ReOpenAtStart, _vsInstanceTelemetryEvents.Where(e => e[ReOpenAtStart] != null).Any()); // Whether PMC window re-open at start by default next time VS open? - _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCWindowLoadCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); // PMC Window load count - _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMCExecuteCommandCount])); // PMC number of commands executed. - _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMUIExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMUIExecuteCommandCount])); // PMUI number of powershell commands executed. + // Whether PMC window re-open at start by default next time VS open? + _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + ReOpenAtStart, _vsInstanceTelemetryEvents.Where(e => e[ReOpenAtStart] != null).Any()); + // PMC Window load count + _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCWindowLoadCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); + // PMC number of commands executed. + _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMCExecuteCommandCount])); + // PMUI number of powershell commands executed. + _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMUIExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMUIExecuteCommandCount])); + // Number of actual solutions loaded during VS instance duration. _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + SolutionCount, _solutionCount); - _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + PMCPowerShellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[SolutionLoaded] is bool && (bool)e[SolutionLoaded] && e[LoadedFromPMC] is bool).Count(e => (bool)e[LoadedFromPMC] == true)); // SolutionLoaded used here to remove edge case : PMC used before any solution is loaded + // Number of solutions solutions where PMC PowerShellHost was loaded. + _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + PMCPowerShellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[SolutionLoaded] is bool && (bool)e[SolutionLoaded] && e[LoadedFromPMC] is bool).Count(e => (bool)e[LoadedFromPMC] == true)); + // Number of solutions solutions where PMUI PowerShellHost was loaded. _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + PMUIPowerShellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[LoadedFromPMUI] is bool).Count(e => (bool)e[LoadedFromPMUI] == true)); } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs index 39083da000e..414fe9829d1 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs @@ -78,7 +78,7 @@ public void IncreaseCommandCounter(bool isPMC) { // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files // This one is called for both init.ps1 and install.ps1 seperately. - // For MSBuildNuGetProject projects install.ps1 can event furthure duplicate counted: MSBuildNuGetProject.cs#L377 - L396 + // For MSBuildNuGetProject projects install.ps1 can even furthure increase duplicate counting: See MSBuildNuGetProject.cs#L377 - L396 // Also this concern valid for dependent packages with *.ps1 files. _nonPmcExecutedCount++; } @@ -181,7 +181,6 @@ public void RecordInitPs1loaded(bool isPMC) { PowerShellHostInstances |= 0b01000000; } - } public void IsNuGetCommand(string commandStr) diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/GlobalSuppressions.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/GlobalSuppressions.cs index 87168dad383..567e0e83672 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/GlobalSuppressions.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/GlobalSuppressions.cs @@ -29,4 +29,3 @@ [assembly: SuppressMessage("Build", "CA1303:Method 'T TypeWrapper.GetInterface(object scriptValue, Type interfaceType, Func> getTypeWrapper)' passes a literal string as parameter 'message' of a call to 'ArgumentException.ArgumentException(string message, string paramName)'. Retrieve the following string(s) from a resource table instead: \"Invalid argument\".", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.TypeWrapper`1.GetInterface(System.Object,System.Type,System.Func{System.Object,NuGetConsole.Host.TypeWrapper`1})~`0")] [assembly: SuppressMessage("Build", "CA1507:Use nameof in place of string literal 'interfaceType'", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.TypeWrapper`1.GetInterface(System.Object,System.Type,System.Func{System.Object,NuGetConsole.Host.TypeWrapper`1})~`0")] [assembly: SuppressMessage("Build", "CA1062:In externally visible method 'T TypeWrapper.GetInterface(Type interfaceType)', validate parameter 'interfaceType' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.TypeWrapper`1.GetInterface(System.Type)~`0")] -[assembly: SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "", Scope = "member", Target = "~M:NuGetConsole.Host.PowerShell.Implementation.PowerShellHost.ExecuteInitPs1Async(System.String,NuGet.Packaging.Core.PackageIdentity)~System.Threading.Tasks.Task")] diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 08e92975615..f2b6a9538c3 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -313,14 +313,17 @@ public void Initialize(IConsole console) } UpdateWorkingDirectory(); - await ExecuteInitScriptsAsync(); // Emit first time Powershell load event to find out later how many VS instance crash after loading powershell. _vsPowerShellHostTelemetryEmit.Value.EmitPowerShellLoadedTelemetry(console is IWpfConsole); + // Record if PowerShellHost initiated from PMC or PMUI + _vsPowerShellHostTelemetryEmit.Value.RecordPSHostInitializeOrigin(console is IWpfConsole); + + await ExecuteInitScriptsAsync(); _solutionManager.Value.SolutionOpening += (o, e) => { - // Check if PMC is used before without any solution. + // Hook up solution events, check if PMC is used before without any solution. _vsPowerShellHostTelemetryEmit.Value.HandleSolutionOpenedEmit(); }; @@ -329,7 +332,6 @@ public void Initialize(IConsole console) { // Hook up solution events _solutionManager.Value.SolutionOpened += (_, __) => HandleSolutionOpened(); - _solutionManager.Value.SolutionClosed += (o, e) => { UpdateWorkingDirectory(); @@ -363,9 +365,6 @@ public void Initialize(IConsole console) SetPrivateDataOnHost(false); StartAsyncDefaultProjectUpdate(); - - // Record if PowerShellHost initiated from PMC or PMUI - _vsPowerShellHostTelemetryEmit.Value.RecordPSHostInitializeOrigin(console is IWpfConsole); } catch (Exception ex) { diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml index 5b0d5b42f4c..46fd1391066 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml @@ -2,7 +2,7 @@ - + Date: Fri, 13 Nov 2020 09:10:53 -0800 Subject: [PATCH 28/59] Add missing telemetry property --- .../Telemetry/VsIntanceTelemetryEmit.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs index fca575bcce7..e9c14477c0d 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs @@ -107,12 +107,13 @@ private void EnqueueVSSolutionPowershellTelemetry() _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, 0); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, 0); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetCommandUsed, false); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadedFromPMCFirst, false); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadPMC, false); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMC, false); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMUI, false); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMC, false); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadPMUI, false); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadedFromPMCFirst, false); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMUI, false); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMC, false); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMUI, false); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMC, false); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + SolutionLoaded, true); } else @@ -126,12 +127,13 @@ private void EnqueueVSSolutionPowershellTelemetry() _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount]); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMUIExecuteCommandCount]); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetCommandUsed, vsSolutionPowershellTelemetry[NuGetCommandUsed]); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadedFromPMCFirst, vsSolutionPowershellTelemetry[InitPs1LoadedFromPMCFirst]); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadPMC, vsSolutionPowershellTelemetry[InitPs1LoadPMC]); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMC, vsSolutionPowershellTelemetry[LoadedFromPMC]); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMUI, vsSolutionPowershellTelemetry[LoadedFromPMUI]); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMC, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMC]); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadPMUI, vsSolutionPowershellTelemetry[InitPs1LoadPMUI]); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadedFromPMCFirst, vsSolutionPowershellTelemetry[InitPs1LoadedFromPMCFirst]); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMUI, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMUI]); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMC, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMC]); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMUI, vsSolutionPowershellTelemetry[LoadedFromPMUI]); + _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMC, vsSolutionPowershellTelemetry[LoadedFromPMC]); _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + SolutionLoaded, vsSolutionPowershellTelemetry[SolutionLoaded]); } From ba3a265a4ab16535d4678a92cdd2494e7d456cff Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Fri, 13 Nov 2020 11:37:24 -0800 Subject: [PATCH 29/59] INuGetTelemetryCollector doesn't need to be Lazy since it's used immediately. --- .../Telemetry/VsPowerShellHostTelemetryEmit.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs index 414fe9829d1..5a855444824 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs @@ -17,7 +17,7 @@ public class VsPowerShellHostTelemetryEmit : VsInstanceTelemetryConsts private bool _isTelemetryEmitted; private int _pmcExecutedCount; private int _nonPmcExecutedCount; - private readonly Lazy _nugetTelemetryAggregate; + private readonly INuGetTelemetryCollector _nugetTelemetryAggregate; // There are 8 bits in byte which used as boolean flags. // 0 - Did any nuget command execute during current VS solution session? @@ -32,7 +32,7 @@ public class VsPowerShellHostTelemetryEmit : VsInstanceTelemetryConsts public VsPowerShellHostTelemetryEmit() { - _nugetTelemetryAggregate = new Lazy(() => ServiceLocator.GetInstanceSafe()); + _nugetTelemetryAggregate = ServiceLocator.GetInstance(); } public void RecordPSHostInitializeOrigin(bool isPMC) @@ -125,7 +125,7 @@ public void EmitPowerShellLoadedTelemetry(bool isPMC) } else { - _nugetTelemetryAggregate.Value.AddSolutionTelemetryEvent(telemetryEvent); + _nugetTelemetryAggregate.AddSolutionTelemetryEvent(telemetryEvent); } } } @@ -151,7 +151,7 @@ public void EmitPowershellUsageTelemetry(bool withSolution) { LoadedFromPMC, TestAllBitsSet(PowerShellHostInstances, 0b00000001) }, { SolutionLoaded, withSolution} }); - _nugetTelemetryAggregate.Value.AddSolutionTelemetryEvent(telemetryEvent); + _nugetTelemetryAggregate.AddSolutionTelemetryEvent(telemetryEvent); _pmcExecutedCount = 0; _nonPmcExecutedCount = 0; From f5d3a0f148545d133efdd8e2ab1df162a3637133 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Mon, 23 Nov 2020 17:12:22 -0800 Subject: [PATCH 30/59] Address code review comment by Nikolche. --- .../Telemetry/INuGetTelemetryCollector.cs | 2 +- .../NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml | 1 - .../NuGetConsole.Host.PowerShell/Scripts/nuget.psm1 | 4 +++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs index 025850019cc..5d8ae3cc8ed 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs @@ -5,7 +5,7 @@ namespace NuGet.VisualStudio.Telemetry { - // This one used for emitting aggregated telemetry at VS solution close or VS instance close. + // Emit aggregated telemetry at VS solution close or VS instance close. public interface INuGetTelemetryCollector { /// Add a to telemetry list which will be aggregated and emitted later. diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml index 46fd1391066..ba6de3f41a1 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml @@ -2,7 +2,6 @@ - Date: Mon, 23 Nov 2020 18:04:43 -0800 Subject: [PATCH 31/59] Remove unnecessary load of assembly. --- .../NuGetConsole.Host.PowerShell.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj index 6a252b731b3..a50ff024e4c 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj @@ -129,10 +129,6 @@ {26dc17ac-a390-4515-a2c0-07a0950036c5} NuGet.PackageManagement.PowerShellCmdlets - - {306cddfa-ff0b-4299-930c-9ec6c9308160} - NuGet.PackageManagement.VisualStudio - {eea49a74-6efc-410e-9745-bad367ac151d} NuGet.VisualStudio.Common From 1ff19e8ece97d9ef1740b2e2bca2038ef5e998e2 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Fri, 15 Jan 2021 14:35:46 -0800 Subject: [PATCH 32/59] Revert unneeded change. --- .../Telemetry/VsPowerShellHostTelemetryEmit.cs | 2 +- .../NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs index 5a855444824..683e4e7b836 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs @@ -78,7 +78,7 @@ public void IncreaseCommandCounter(bool isPMC) { // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files // This one is called for both init.ps1 and install.ps1 seperately. - // For MSBuildNuGetProject projects install.ps1 can even furthure increase duplicate counting: See MSBuildNuGetProject.cs#L377 - L396 + // For MSBuildNuGetProject projects install.ps1 can even further increase duplicate counting: See MSBuildNuGetProject.cs#L377 - L396 // Also this concern valid for dependent packages with *.ps1 files. _nonPmcExecutedCount++; } diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml index ba6de3f41a1..c3219932d11 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml @@ -1371,4 +1371,4 @@ - + \ No newline at end of file From 7656aa691859d7bed90f23c2b9acf2ee155b8a4e Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Sat, 16 Jan 2021 07:28:08 -0800 Subject: [PATCH 33/59] Separate telemtry collector and emitter. --- ...uGet.PackageManagement.VisualStudio.csproj | 8 +- .../PMC/NugetPowershellLoadedEvent.cs | 15 ++ .../Telemetry/PMC/NugetVSSolutionClose.cs | 15 ++ .../VSSettings.cs | 16 +- .../Telemetry/INuGetTelemetryCollector.cs | 3 + .../Telemetry/NuGetTelemetryCollector.cs | 36 ++++ .../VsInstancePowershellTelemetryEmitter.cs | 196 +++++++++++++++++ .../Telemetry/VsIntanceTelemetryEmit.cs | 203 ------------------ 8 files changed, 280 insertions(+), 212 deletions(-) create mode 100644 src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/PMC/NugetPowershellLoadedEvent.cs create mode 100644 src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/PMC/NugetVSSolutionClose.cs create mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs create mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs delete mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/NuGet.PackageManagement.VisualStudio.csproj b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/NuGet.PackageManagement.VisualStudio.csproj index c10ea336c63..3ba4fc82b39 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/NuGet.PackageManagement.VisualStudio.csproj +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/NuGet.PackageManagement.VisualStudio.csproj @@ -173,6 +173,8 @@ + + @@ -320,7 +322,11 @@ + + + + - + \ No newline at end of file diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/PMC/NugetPowershellLoadedEvent.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/PMC/NugetPowershellLoadedEvent.cs new file mode 100644 index 00000000000..ac5db8c0f0e --- /dev/null +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/PMC/NugetPowershellLoadedEvent.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using NuGet.Common; + +namespace NuGet.PackageManagement.VisualStudio.Telemetry.PMC +{ + public class NugetPowershellLoadedEvent : TelemetryEvent + { + public NugetPowershellLoadedEvent() : base("nugetpowershellloaded") + { + } + } +} diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/PMC/NugetVSSolutionClose.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/PMC/NugetVSSolutionClose.cs new file mode 100644 index 00000000000..b05c8d63a45 --- /dev/null +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/PMC/NugetVSSolutionClose.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NuGet.PackageManagement.VisualStudio.Telemetry.PMC +{ + public class NugetVSSolutionClose + { + } +} diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs index 36094aa6fd7..fc64ff8affd 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs @@ -20,7 +20,7 @@ public sealed class VSSettings : ISettings, IDisposable // to initialize SolutionSettings first time outside MEF constructor private Tuple> _solutionSettings; - private VsIntanceTelemetryEmit _vsIntanceTelemetryEmit; + private VsInstancePowershellTelemetryEmitter _vsIntanceTelemetryEmitter; private ISettings SolutionSettings { @@ -42,20 +42,20 @@ private ISettings SolutionSettings public event EventHandler SettingsChanged; - public VSSettings(ISolutionManager solutionManager, VsIntanceTelemetryEmit vsIntanceTelemetryEmit) - : this(solutionManager, vsIntanceTelemetryEmit, machineWideSettings: null) + public VSSettings(ISolutionManager solutionManager, VsInstancePowershellTelemetryEmitter vsIntanceTelemetryEmitter) + : this(solutionManager, vsIntanceTelemetryEmitter, machineWideSettings: null) { } [ImportingConstructor] - public VSSettings(ISolutionManager solutionManager, VsIntanceTelemetryEmit vsIntanceTelemetryEmit, IMachineWideSettings machineWideSettings) + public VSSettings(ISolutionManager solutionManager, VsInstancePowershellTelemetryEmitter vsIntanceTelemetryEmitter, IMachineWideSettings machineWideSettings) { SolutionManager = solutionManager ?? throw new ArgumentNullException(nameof(solutionManager)); MachineWideSettings = machineWideSettings; SolutionManager.SolutionOpening += OnSolutionOpening; SolutionManager.SolutionOpened += OnSolutionOpened; SolutionManager.SolutionClosed += OnSolutionClosed; - _vsIntanceTelemetryEmit = vsIntanceTelemetryEmit; + _vsIntanceTelemetryEmitter = vsIntanceTelemetryEmitter; } private bool ResetSolutionSettingsIfNeeded() @@ -112,12 +112,12 @@ private void OnSolutionOpening(object sender, EventArgs e) private void OnSolutionOpened(object sender, EventArgs e) { - _vsIntanceTelemetryEmit.SolutionOpenedEmit(); + _vsIntanceTelemetryEmitter.SolutionOpenedEmit(); } private void OnSolutionClosed(object sender, EventArgs e) { - _vsIntanceTelemetryEmit.EmitVSSolutionTelemetry(); + _vsIntanceTelemetryEmitter.EmitVSSolutionTelemetry(); DetectSolutionSettingChange(); } @@ -169,7 +169,7 @@ public void Dispose() SolutionManager.SolutionOpening -= OnSolutionOpening; SolutionManager.SolutionOpened -= OnSolutionOpened; SolutionManager.SolutionClosed -= OnSolutionClosed; - _vsIntanceTelemetryEmit.EmitVSInstanceTelemetry(); + _vsIntanceTelemetryEmitter.EmitVSInstanceTelemetry(); } // The value for SolutionSettings can't possibly be null, but it could be a read-only instance diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs index 5d8ae3cc8ed..da39c1f01a0 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Collections.Generic; using NuGet.Common; namespace NuGet.VisualStudio.Telemetry @@ -11,5 +12,7 @@ public interface INuGetTelemetryCollector /// Add a to telemetry list which will be aggregated and emitted later. /// Telemetry event to add into aggregation. void AddSolutionTelemetryEvent(TelemetryEvent telemetryData); + IReadOnlyList GetSolutionTelemetryEvents(); + IReadOnlyList GetVSIntanceTelemetryEvents(); } } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs new file mode 100644 index 00000000000..836c1288c27 --- /dev/null +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs @@ -0,0 +1,36 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using NuGet.Common; + +namespace NuGet.VisualStudio.Telemetry +{ + [Export(typeof(INuGetTelemetryCollector))] + [PartCreationPolicy(CreationPolicy.Shared)] + public sealed class NuGetTelemetryCollector : INuGetTelemetryCollector + { + // _solutionTelemetryEvents hold telemetry for current VS solution session. + private readonly List _vsSolutionTelemetryEvents; + // _vsInstanceTelemetryEvents hold telemetry for current VS instance session. + private readonly List _vsInstanceTelemetryEvents; + + public NuGetTelemetryCollector() + { + _vsSolutionTelemetryEvents = new List(); + _vsInstanceTelemetryEvents = new List(); + } + + // Adds telemetry into list which will be aggregated by end of VS solution sessions or VS instance session. + public void AddSolutionTelemetryEvent(TelemetryEvent telemetryData) + { + _vsSolutionTelemetryEvents.Add(telemetryData); + _vsInstanceTelemetryEvents.Add(telemetryData); + } + + public IReadOnlyList GetSolutionTelemetryEvents() => _vsSolutionTelemetryEvents.AsReadOnly(); + public IReadOnlyList GetVSIntanceTelemetryEvents() => _vsInstanceTelemetryEvents.AsReadOnly(); + } +} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs new file mode 100644 index 00000000000..33f6e55c0fe --- /dev/null +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs @@ -0,0 +1,196 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using NuGet.Common; + +namespace NuGet.VisualStudio.Telemetry +{ + [Export(typeof(VsInstancePowershellTelemetryEmitter))] + [PartCreationPolicy(CreationPolicy.Shared)] + public sealed class VsInstancePowershellTelemetryEmitter : VsInstanceTelemetryConsts + { + private int _solutionCount; + private Lazy> _vsSolutionTelemetryEmitQueue; + private Lazy> _vsInstanceTelemetryEmitQueue; + private INuGetTelemetryCollector _nuGetTelemetryCollector; + private Lazy> _vsInstanceTelemetryEvents; + + [ImportingConstructor] + public VsInstancePowershellTelemetryEmitter(INuGetTelemetryCollector nuGetTelemetryCollector) + { + _vsSolutionTelemetryEmitQueue = new Lazy>(() => new Dictionary()); + _vsInstanceTelemetryEmitQueue = new Lazy>(() => new Dictionary()); + _nuGetTelemetryCollector = new NuGetTelemetryCollector(); + _vsInstanceTelemetryEvents = new Lazy>(() => + { + return _nuGetTelemetryCollector.GetVSIntanceTelemetryEvents(); + }); + } + + public void SolutionOpenedEmit() + { + try + { + // Handle edge cases. + EmitPMCUsedWithoutSolution(); + } + catch (Exception) + { + // Currently do nothing. + } + + //vsSolutionTelemetryEvents.Clear(); + _solutionCount++; + } + + // Emit VS solution session telemetry when VS instance is closed. + public void EmitVSInstanceTelemetry() + { + try + { + // Handle edge cases. + EmitPMCUsedWithoutSolution(); + + EnqueueVSInstancePowershellTelemetry(); + + // Add other telemetry type queuing here in the future. You can emit many different types of telemetry here. + // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. + // Using prefix avoid collision of property names from different types of telemetry. + + CombineAndEmitTelemetry(_vsInstanceTelemetryEmitQueue.Value, NuGetVSInstanceClose); + } + catch (Exception) + { + // Currently do nothing. + } + } + + private void EmitPMCUsedWithoutSolution() + { + // Edge case: PMC window can be opened without any solution at all, but sometimes TelemetryActivity.NuGetTelemetryService is not ready yet when PMC open. + // In general we want to emit this telemetry right away, but not possible then emit later. + var nuGetPowerShellLoadedEvent = _nuGetTelemetryCollector.GetSolutionTelemetryEvents().FirstOrDefault(e => e.Name == VsInstanceTelemetryConsts.NuGetPowerShellLoaded); + + if (nuGetPowerShellLoadedEvent != null) + { + TelemetryActivity.EmitTelemetryEvent(nuGetPowerShellLoadedEvent); + //vsSolutionTelemetryEvents.Remove(nuGetPowerShellLoadedEvent); + } + + // If there is not emitted PowerShellExecuteCommand telemetry. + if (_nuGetTelemetryCollector.GetSolutionTelemetryEvents().Any(e => e.Name == PowerShellExecuteCommand)) + { + EmitVSSolutionTelemetry(); + } + } + + // Emit VS solution session telemetry when solution is closed. + public void EmitVSSolutionTelemetry() + { + try + { + // Queue all different types of telemetries and do some processing prior to emit. + EnqueueVSSolutionPowershellTelemetry(); + + // Add other telemetry type queuing here in the future. You can emit many different types of telemetry other than powershell here. + // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. + // Using prefix avoid collision of property names from different types of telemetry. + + // Actual emit + CombineAndEmitTelemetry(_vsSolutionTelemetryEmitQueue.Value, NuGetVSSolutionClose); + } + catch (Exception) + { + // Currently do nothing. + } + + //_vsSolutionTelemetryEvents.Clear(); + _vsSolutionTelemetryEmitQueue.Value.Clear(); + } + + private void EnqueueVSSolutionPowershellTelemetry() + { + var vsSolutionPowershellTelemetry = _nuGetTelemetryCollector.GetSolutionTelemetryEvents().FirstOrDefault(e => e.Name == PowerShellExecuteCommand); + + // If powershell(PMC/PMUI) is not loaded at all then we need to create default telemetry event which will be emitted. + if (vsSolutionPowershellTelemetry == null) + { + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, 0); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, 0); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetCommandUsed, false); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + InitPs1LoadPMC, false); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + InitPs1LoadPMUI, false); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + InitPs1LoadedFromPMCFirst, false); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMUI, false); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMC, false); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + LoadedFromPMUI, false); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + LoadedFromPMC, false); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + SolutionLoaded, true); + } + else + { + // PMC opened, but no command executed nor any solution was loaded. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore. + if (!(bool)vsSolutionPowershellTelemetry[SolutionLoaded] && (int)vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount] == 0) + { + return; + } + + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount]); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMUIExecuteCommandCount]); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetCommandUsed, vsSolutionPowershellTelemetry[NuGetCommandUsed]); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + InitPs1LoadPMC, vsSolutionPowershellTelemetry[InitPs1LoadPMC]); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + InitPs1LoadPMUI, vsSolutionPowershellTelemetry[InitPs1LoadPMUI]); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + InitPs1LoadedFromPMCFirst, vsSolutionPowershellTelemetry[InitPs1LoadedFromPMCFirst]); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMUI, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMUI]); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMC, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMC]); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + LoadedFromPMUI, vsSolutionPowershellTelemetry[LoadedFromPMUI]); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + LoadedFromPMC, vsSolutionPowershellTelemetry[LoadedFromPMC]); + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + SolutionLoaded, vsSolutionPowershellTelemetry[SolutionLoaded]); + } + + _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetPMCWindowLoadCount, _nuGetTelemetryCollector.GetSolutionTelemetryEvents().Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); + } + + private void EnqueueVSInstancePowershellTelemetry() + { + // Whether PMC window re-open at start by default next time VS open? + _vsInstanceTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + ReOpenAtStart, _vsInstanceTelemetryEvents.Value.Where(e => e[ReOpenAtStart] != null).Any()); + // PMC Window load count + _vsInstanceTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetPMCWindowLoadCount, _vsInstanceTelemetryEvents.Value.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); + // PMC number of commands executed. + _vsInstanceTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Value.Where(e => e[NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMCExecuteCommandCount])); + // PMUI number of powershell commands executed. + _vsInstanceTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, _vsInstanceTelemetryEvents.Value.Where(e => e[NuGetPMUIExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMUIExecuteCommandCount])); + // Number of actual solutions loaded during VS instance duration. + _vsInstanceTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + SolutionCount, _solutionCount); + // Number of solutions solutions where PMC PowerShellHost was loaded. + _vsInstanceTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + PMCPowerShellLoadedSolutionCount, _vsInstanceTelemetryEvents.Value.Where(e => e[SolutionLoaded] is bool && (bool)e[SolutionLoaded] && e[LoadedFromPMC] is bool).Count(e => (bool)e[LoadedFromPMC] == true)); + // Number of solutions solutions where PMUI PowerShellHost was loaded. + _vsInstanceTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + PMUIPowerShellLoadedSolutionCount, _vsInstanceTelemetryEvents.Value.Where(e => e[LoadedFromPMUI] is bool).Count(e => (bool)e[LoadedFromPMUI] == true)); + } + + // Instead of emitting one by one we combine them into single event and each event is a property of this single event. + // Currently we emit vs.nuget.NugetVSSolutionClose, vs.nuget.NugetVSInstanceClose events. + private void CombineAndEmitTelemetry(Dictionary telemetryEvents, string telemetryType) + { + // No event to emit + if (!telemetryEvents.Keys.Any()) + { + return; + } + + var vsSolutionCloseTelemetry = new TelemetryEvent(telemetryType, new Dictionary()); + + foreach (KeyValuePair telemetryEvent in telemetryEvents) + { + vsSolutionCloseTelemetry[telemetryEvent.Key] = telemetryEvent.Value; + } + + TelemetryActivity.EmitTelemetryEvent(vsSolutionCloseTelemetry); + } + } +} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs deleted file mode 100644 index e9c14477c0d..00000000000 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsIntanceTelemetryEmit.cs +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Linq; -using NuGet.Common; - -namespace NuGet.VisualStudio.Telemetry -{ - [Export(typeof(INuGetTelemetryCollector))] - [Export(typeof(VsIntanceTelemetryEmit))] - [PartCreationPolicy(CreationPolicy.Shared)] - public sealed class VsIntanceTelemetryEmit : VsInstanceTelemetryConsts, INuGetTelemetryCollector - { - // _solutionTelemetryEvents hold telemetry for current VS solution session. - private List _vsSolutionTelemetryEvents; - private Dictionary _vsSolutionTelemetryEmitQueue; - // _vsInstanceTelemetryEvents hold telemetry for current VS instance session. - private List _vsInstanceTelemetryEvents; - private Dictionary _vsInstanceTelemetryEmitQueue; - - private int _solutionCount; - - public VsIntanceTelemetryEmit() - { - _vsSolutionTelemetryEvents = new List(); - _vsSolutionTelemetryEmitQueue = new Dictionary(); - _vsInstanceTelemetryEvents = new List(); - _vsInstanceTelemetryEmitQueue = new Dictionary(); - } - - // Adds telemetry into list which will be aggregated by end of VS solution sessions or VS instance session. - public void AddSolutionTelemetryEvent(TelemetryEvent telemetryData) - { - _vsSolutionTelemetryEvents.Add(telemetryData); - _vsInstanceTelemetryEvents.Add(telemetryData); - } - - public void SolutionOpenedEmit() - { - try - { - // Handle edge cases. - EmitPMCUsedWithoutSolution(); - } - catch (Exception) - { - // Currently do nothing. - } - - _vsSolutionTelemetryEvents.Clear(); - _solutionCount++; - } - - private void EmitPMCUsedWithoutSolution() - { - // Edge case: PMC window can be opened without any solution at all, but sometimes TelemetryActivity.NuGetTelemetryService is not ready yet when PMC open. - // In general we want to emit this telemetry right away, but not possible then emit later. - var nuGetPowerShellLoadedEvent = _vsInstanceTelemetryEvents.FirstOrDefault(e => e.Name == VsInstanceTelemetryConsts.NuGetPowerShellLoaded); - - if (nuGetPowerShellLoadedEvent != null) - { - TelemetryActivity.EmitTelemetryEvent(nuGetPowerShellLoadedEvent); - _vsInstanceTelemetryEvents.Remove(nuGetPowerShellLoadedEvent); - } - - // If there is not emitted PowerShellExecuteCommand telemetry. - if (_vsSolutionTelemetryEvents.Any(e => e.Name == PowerShellExecuteCommand)) - { - EmitVSSolutionTelemetry(); - } - } - - // Emit VS solution session telemetry when solution is closed. - public void EmitVSSolutionTelemetry() - { - try - { - // Queue all different types of telemetries and do some processing prior to emit. - EnqueueVSSolutionPowershellTelemetry(); - - // Add other telemetry type queuing here in the future. You can emit many different types of telemetry other than powershell here. - // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. - // Using prefix avoid collision of property names from different types of telemetry. - - // Actual emit - CombineAndEmitTelemetry(_vsSolutionTelemetryEmitQueue, NuGetVSSolutionClose); - } - catch (Exception) - { - // Currently do nothing. - } - - _vsSolutionTelemetryEvents.Clear(); - _vsSolutionTelemetryEmitQueue.Clear(); - } - - private void EnqueueVSSolutionPowershellTelemetry() - { - var vsSolutionPowershellTelemetry = _vsSolutionTelemetryEvents.FirstOrDefault(e => e.Name == PowerShellExecuteCommand); - - // If powershell(PMC/PMUI) is not loaded at all then we need to create default telemetry event which will be emitted. - if (vsSolutionPowershellTelemetry == null) - { - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, 0); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, 0); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetCommandUsed, false); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadPMC, false); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadPMUI, false); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadedFromPMCFirst, false); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMUI, false); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMC, false); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMUI, false); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMC, false); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + SolutionLoaded, true); - } - else - { - // PMC opened, but no command executed nor any solution was loaded. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore. - if (!(bool)vsSolutionPowershellTelemetry[SolutionLoaded] && (int)vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount] == 0) - { - return; - } - - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount]); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMUIExecuteCommandCount]); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetCommandUsed, vsSolutionPowershellTelemetry[NuGetCommandUsed]); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadPMC, vsSolutionPowershellTelemetry[InitPs1LoadPMC]); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadPMUI, vsSolutionPowershellTelemetry[InitPs1LoadPMUI]); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + InitPs1LoadedFromPMCFirst, vsSolutionPowershellTelemetry[InitPs1LoadedFromPMCFirst]); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMUI, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMUI]); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMC, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMC]); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMUI, vsSolutionPowershellTelemetry[LoadedFromPMUI]); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + LoadedFromPMC, vsSolutionPowershellTelemetry[LoadedFromPMC]); - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + SolutionLoaded, vsSolutionPowershellTelemetry[SolutionLoaded]); - } - - _vsSolutionTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCWindowLoadCount, _vsSolutionTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); - } - - // Emit VS solution session telemetry when VS instance is closed. - public void EmitVSInstanceTelemetry() - { - try - { - // Handle edge cases. - EmitPMCUsedWithoutSolution(); - - EnqueueVSInstancePowershellTelemetry(); - - // Add other telemetry type queuing here in the future. You can emit many different types of telemetry here. - // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. - // Using prefix avoid collision of property names from different types of telemetry. - - CombineAndEmitTelemetry(_vsInstanceTelemetryEmitQueue, NuGetVSInstanceClose); - } - catch (Exception) - { - // Currently do nothing. - } - } - - private void EnqueueVSInstancePowershellTelemetry() - { - // Whether PMC window re-open at start by default next time VS open? - _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + ReOpenAtStart, _vsInstanceTelemetryEvents.Where(e => e[ReOpenAtStart] != null).Any()); - // PMC Window load count - _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCWindowLoadCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); - // PMC number of commands executed. - _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMCExecuteCommandCount])); - // PMUI number of powershell commands executed. - _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, _vsInstanceTelemetryEvents.Where(e => e[NuGetPMUIExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMUIExecuteCommandCount])); - // Number of actual solutions loaded during VS instance duration. - _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + SolutionCount, _solutionCount); - // Number of solutions solutions where PMC PowerShellHost was loaded. - _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + PMCPowerShellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[SolutionLoaded] is bool && (bool)e[SolutionLoaded] && e[LoadedFromPMC] is bool).Count(e => (bool)e[LoadedFromPMC] == true)); - // Number of solutions solutions where PMUI PowerShellHost was loaded. - _vsInstanceTelemetryEmitQueue.Add(NuGetPowershellPrefix + PMUIPowerShellLoadedSolutionCount, _vsInstanceTelemetryEvents.Where(e => e[LoadedFromPMUI] is bool).Count(e => (bool)e[LoadedFromPMUI] == true)); - } - - // Instead of emitting one by one we combine them into single event and each event is a property of this single event. - // Currently we emit vs.nuget.NugetVSSolutionClose, vs.nuget.NugetVSInstanceClose events. - private void CombineAndEmitTelemetry(Dictionary telemetryEvents, string telemetryType) - { - // No event to emit - if (!telemetryEvents.Keys.Any()) - { - return; - } - - var vsSolutionCloseTelemetry = new TelemetryEvent(telemetryType, new Dictionary()); - - foreach (KeyValuePair telemetryEvent in telemetryEvents) - { - vsSolutionCloseTelemetry[telemetryEvent.Key] = telemetryEvent.Value; - } - - TelemetryActivity.EmitTelemetryEvent(vsSolutionCloseTelemetry); - } - } -} From 1c4705c86943812c1cc905572d5563b81815e567 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Sat, 16 Jan 2021 07:47:01 -0800 Subject: [PATCH 34/59] Move solution events to VSSolutionManager.cs --- .../IDE/VSSolutionManager.cs | 28 ++++++++++++- .../VSSettings.cs | 39 +++++-------------- 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs index b9d94e4d944..6ac4a58ecfc 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs @@ -26,6 +26,7 @@ using NuGet.Protocol; using NuGet.Protocol.Core.Types; using NuGet.VisualStudio; +using NuGet.VisualStudio.Telemetry; using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; using Task = System.Threading.Tasks.Task; @@ -34,7 +35,7 @@ namespace NuGet.PackageManagement.VisualStudio [Export(typeof(ISolutionManager))] [Export(typeof(IVsSolutionManager))] [PartCreationPolicy(CreationPolicy.Shared)] - public sealed class VSSolutionManager : IVsSolutionManager, IVsSelectionEvents + public sealed class VSSolutionManager : IVsSolutionManager, IVsSelectionEvents, IDisposable { private static readonly INuGetProjectContext EmptyNuGetProjectContext = new EmptyNuGetProjectContext(); private static readonly string VSNuGetClientName = "NuGet VS VSIX"; @@ -55,6 +56,7 @@ public sealed class VSSolutionManager : IVsSolutionManager, IVsSelectionEvents private readonly IVsProjectAdapterProvider _vsProjectAdapterProvider; private readonly Common.ILogger _logger; private readonly Lazy _settings; + private VsInstancePowershellTelemetryEmitter _vsIntanceTelemetryEmitter; private bool _initialized; private bool _cacheInitialized; @@ -126,6 +128,7 @@ internal VSSolutionManager( [Import("VisualStudioActivityLogger")] Common.ILogger logger, Lazy settings, + VsInstancePowershellTelemetryEmitter vsIntanceTelemetryEmitter, JoinableTaskContext joinableTaskContext) : this(AsyncServiceProvider.GlobalProvider, projectSystemCache, @@ -134,6 +137,7 @@ internal VSSolutionManager( vsProjectAdapterProvider, logger, settings, + vsIntanceTelemetryEmitter, joinableTaskContext) { } @@ -146,6 +150,7 @@ internal VSSolutionManager( IVsProjectAdapterProvider vsProjectAdapterProvider, ILogger logger, Lazy settings, + VsInstancePowershellTelemetryEmitter vsIntanceTelemetryEmitter, JoinableTaskContext joinableTaskContext) { Assumes.Present(asyncServiceProvider); @@ -164,7 +169,11 @@ internal VSSolutionManager( _vsProjectAdapterProvider = vsProjectAdapterProvider; _logger = logger; _settings = settings; + _vsIntanceTelemetryEmitter = vsIntanceTelemetryEmitter; _initLock = new NuGetLockService(joinableTaskContext); + + SolutionOpened += OnSolutionOpened; + SolutionClosed += OnSolutionClosed; } private async Task InitializeAsync() @@ -1027,6 +1036,23 @@ public async Task UpgradeProjectToPackageReferenceAsync(NuGetProje return nuGetProject; } + private void OnSolutionOpened(object sender, EventArgs e) + { + _vsIntanceTelemetryEmitter.SolutionOpenedEmit(); + } + + private void OnSolutionClosed(object sender, EventArgs e) + { + _vsIntanceTelemetryEmitter.EmitVSSolutionTelemetry(); + } + + public void Dispose() + { + SolutionOpened -= OnSolutionOpened; + SolutionClosed -= OnSolutionClosed; + _vsIntanceTelemetryEmitter.EmitVSInstanceTelemetry(); + } + #endregion } } diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs index fc64ff8affd..a495beaa98e 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/VSSettings.cs @@ -5,10 +5,10 @@ using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; +using System.Threading.Tasks; using Microsoft.VisualStudio.Threading; using NuGet.Configuration; using NuGet.VisualStudio; -using NuGet.VisualStudio.Telemetry; namespace NuGet.PackageManagement.VisualStudio { @@ -20,7 +20,6 @@ public sealed class VSSettings : ISettings, IDisposable // to initialize SolutionSettings first time outside MEF constructor private Tuple> _solutionSettings; - private VsInstancePowershellTelemetryEmitter _vsIntanceTelemetryEmitter; private ISettings SolutionSettings { @@ -42,20 +41,18 @@ private ISettings SolutionSettings public event EventHandler SettingsChanged; - public VSSettings(ISolutionManager solutionManager, VsInstancePowershellTelemetryEmitter vsIntanceTelemetryEmitter) - : this(solutionManager, vsIntanceTelemetryEmitter, machineWideSettings: null) + public VSSettings(ISolutionManager solutionManager) + : this(solutionManager, machineWideSettings: null) { } [ImportingConstructor] - public VSSettings(ISolutionManager solutionManager, VsInstancePowershellTelemetryEmitter vsIntanceTelemetryEmitter, IMachineWideSettings machineWideSettings) + public VSSettings(ISolutionManager solutionManager, IMachineWideSettings machineWideSettings) { SolutionManager = solutionManager ?? throw new ArgumentNullException(nameof(solutionManager)); MachineWideSettings = machineWideSettings; - SolutionManager.SolutionOpening += OnSolutionOpening; - SolutionManager.SolutionOpened += OnSolutionOpened; - SolutionManager.SolutionClosed += OnSolutionClosed; - _vsIntanceTelemetryEmitter = vsIntanceTelemetryEmitter; + SolutionManager.SolutionOpening += OnSolutionOpenedOrClosed; + SolutionManager.SolutionClosed += OnSolutionOpenedOrClosed; } private bool ResetSolutionSettingsIfNeeded() @@ -105,23 +102,7 @@ private bool ResetSolutionSettingsIfNeeded() return false; } - private void OnSolutionOpening(object sender, EventArgs e) - { - DetectSolutionSettingChange(); - } - - private void OnSolutionOpened(object sender, EventArgs e) - { - _vsIntanceTelemetryEmitter.SolutionOpenedEmit(); - } - - private void OnSolutionClosed(object sender, EventArgs e) - { - _vsIntanceTelemetryEmitter.EmitVSSolutionTelemetry(); - DetectSolutionSettingChange(); - } - - private void DetectSolutionSettingChange() + private void OnSolutionOpenedOrClosed(object sender, EventArgs e) { var hasChanged = ResetSolutionSettingsIfNeeded(); @@ -166,10 +147,8 @@ public void SaveToDisk() public void Dispose() { - SolutionManager.SolutionOpening -= OnSolutionOpening; - SolutionManager.SolutionOpened -= OnSolutionOpened; - SolutionManager.SolutionClosed -= OnSolutionClosed; - _vsIntanceTelemetryEmitter.EmitVSInstanceTelemetry(); + SolutionManager.SolutionOpening -= OnSolutionOpenedOrClosed; + SolutionManager.SolutionClosed -= OnSolutionOpenedOrClosed; } // The value for SolutionSettings can't possibly be null, but it could be a read-only instance From 687d35cd541f582390a440b7c51b939512062cfc Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Sat, 16 Jan 2021 09:33:26 -0800 Subject: [PATCH 35/59] Make telemetry collector thread safe. --- .../Xamls/ConsoleContainer.xaml.cs | 4 +- .../IDE/VSSolutionManager.cs | 6 +- .../Telemetry/INuGetTelemetryCollector.cs | 1 + .../Telemetry/NuGetTelemetryCollector.cs | 24 ++++--- .../VsInstancePowershellTelemetryEmitter.cs | 62 +++++++++---------- ...cs => VsPowerShellHostTelemetryHandler.cs} | 9 ++- 6 files changed, 58 insertions(+), 48 deletions(-) rename src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/{VsPowerShellHostTelemetryEmit.cs => VsPowerShellHostTelemetryHandler.cs} (96%) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index ece5c53ce79..37cb4de0618 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -105,7 +105,7 @@ public void Dispose() { VsInstanceTelemetryConsts.ReOpenAtStart, reopenAtStart} }); - _nugetSolutionTelemetry.AddSolutionTelemetryEvent(telemetryEvent); + _nugetSolutionTelemetry?.AddSolutionTelemetryEvent(telemetryEvent); _isTelemetryEmitted = true; } @@ -132,7 +132,7 @@ private void EmitPowershellUsageTelemetry() { { VsInstanceTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, }); - _nugetSolutionTelemetry.AddSolutionTelemetryEvent(telemetryEvent); + _nugetSolutionTelemetry?.AddSolutionTelemetryEvent(telemetryEvent); _windowLoadCount = 0; } diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs index 6ac4a58ecfc..2ffa244633a 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs @@ -1038,19 +1038,19 @@ public async Task UpgradeProjectToPackageReferenceAsync(NuGetProje private void OnSolutionOpened(object sender, EventArgs e) { - _vsIntanceTelemetryEmitter.SolutionOpenedEmit(); + _vsIntanceTelemetryEmitter.SolutionOpenedTelemetryEmit(); } private void OnSolutionClosed(object sender, EventArgs e) { - _vsIntanceTelemetryEmitter.EmitVSSolutionTelemetry(); + _vsIntanceTelemetryEmitter.SolutionClosedEmit(); } public void Dispose() { SolutionOpened -= OnSolutionOpened; SolutionClosed -= OnSolutionClosed; - _vsIntanceTelemetryEmitter.EmitVSInstanceTelemetry(); + _vsIntanceTelemetryEmitter.VSInstanceClosedTelemetryEmit(); } #endregion diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs index da39c1f01a0..e3e0b79f3e1 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs @@ -14,5 +14,6 @@ public interface INuGetTelemetryCollector void AddSolutionTelemetryEvent(TelemetryEvent telemetryData); IReadOnlyList GetSolutionTelemetryEvents(); IReadOnlyList GetVSIntanceTelemetryEvents(); + void ClearSolutionTelemetryEvents(); } } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs index 836c1288c27..daf380357ac 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs @@ -2,25 +2,28 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel.Composition; +using System.Linq; +using System.Threading; using NuGet.Common; namespace NuGet.VisualStudio.Telemetry { [Export(typeof(INuGetTelemetryCollector))] [PartCreationPolicy(CreationPolicy.Shared)] - public sealed class NuGetTelemetryCollector : INuGetTelemetryCollector + internal sealed class NuGetTelemetryCollector : INuGetTelemetryCollector { // _solutionTelemetryEvents hold telemetry for current VS solution session. - private readonly List _vsSolutionTelemetryEvents; + private ConcurrentBag _vsSolutionTelemetryEvents; // _vsInstanceTelemetryEvents hold telemetry for current VS instance session. - private readonly List _vsInstanceTelemetryEvents; + private readonly ConcurrentBag _vsInstanceTelemetryEvents; public NuGetTelemetryCollector() { - _vsSolutionTelemetryEvents = new List(); - _vsInstanceTelemetryEvents = new List(); + _vsSolutionTelemetryEvents = new ConcurrentBag(); + _vsInstanceTelemetryEvents = new ConcurrentBag(); } // Adds telemetry into list which will be aggregated by end of VS solution sessions or VS instance session. @@ -30,7 +33,14 @@ public void AddSolutionTelemetryEvent(TelemetryEvent telemetryData) _vsInstanceTelemetryEvents.Add(telemetryData); } - public IReadOnlyList GetSolutionTelemetryEvents() => _vsSolutionTelemetryEvents.AsReadOnly(); - public IReadOnlyList GetVSIntanceTelemetryEvents() => _vsInstanceTelemetryEvents.AsReadOnly(); + public IReadOnlyList GetSolutionTelemetryEvents() => _vsSolutionTelemetryEvents.ToList().AsReadOnly(); + public IReadOnlyList GetVSIntanceTelemetryEvents() => _vsInstanceTelemetryEvents.ToList().AsReadOnly(); + + // If open new solution then need ability to reset existing solution events. + public void ClearSolutionTelemetryEvents() + { + var newBag = new ConcurrentBag(); + Interlocked.Exchange>(ref _vsSolutionTelemetryEvents, newBag); + } } } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs index 33f6e55c0fe..58689f738c8 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs @@ -24,31 +24,55 @@ public VsInstancePowershellTelemetryEmitter(INuGetTelemetryCollector nuGetTeleme { _vsSolutionTelemetryEmitQueue = new Lazy>(() => new Dictionary()); _vsInstanceTelemetryEmitQueue = new Lazy>(() => new Dictionary()); - _nuGetTelemetryCollector = new NuGetTelemetryCollector(); + _nuGetTelemetryCollector = nuGetTelemetryCollector; _vsInstanceTelemetryEvents = new Lazy>(() => { - return _nuGetTelemetryCollector.GetVSIntanceTelemetryEvents(); + return _nuGetTelemetryCollector?.GetVSIntanceTelemetryEvents(); }); } - public void SolutionOpenedEmit() + public void SolutionOpenedTelemetryEmit() { try { + // Handle edge cases. EmitPMCUsedWithoutSolution(); + _nuGetTelemetryCollector.ClearSolutionTelemetryEvents(); + _solutionCount++; } catch (Exception) { // Currently do nothing. } + } - //vsSolutionTelemetryEvents.Clear(); - _solutionCount++; + // Emit VS solution session telemetry when solution is closed. + public void SolutionClosedEmit() + { + try + { + // Queue all different types of telemetries and do some processing prior to emit. + EnqueueVSSolutionPowershellTelemetry(); + + // Add other telemetry type queuing here in the future. You can emit many different types of telemetry other than powershell here. + // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. + // Using prefix avoid collision of property names from different types of telemetry. + + // Actual emit + CombineAndEmitTelemetry(_vsSolutionTelemetryEmitQueue.Value, NuGetVSSolutionClose); + + _nuGetTelemetryCollector.ClearSolutionTelemetryEvents(); + _vsSolutionTelemetryEmitQueue.Value.Clear(); + } + catch (Exception) + { + // Currently do nothing. + } } // Emit VS solution session telemetry when VS instance is closed. - public void EmitVSInstanceTelemetry() + public void VSInstanceClosedTelemetryEmit() { try { @@ -84,34 +108,10 @@ private void EmitPMCUsedWithoutSolution() // If there is not emitted PowerShellExecuteCommand telemetry. if (_nuGetTelemetryCollector.GetSolutionTelemetryEvents().Any(e => e.Name == PowerShellExecuteCommand)) { - EmitVSSolutionTelemetry(); + SolutionClosedEmit(); } } - // Emit VS solution session telemetry when solution is closed. - public void EmitVSSolutionTelemetry() - { - try - { - // Queue all different types of telemetries and do some processing prior to emit. - EnqueueVSSolutionPowershellTelemetry(); - - // Add other telemetry type queuing here in the future. You can emit many different types of telemetry other than powershell here. - // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. - // Using prefix avoid collision of property names from different types of telemetry. - - // Actual emit - CombineAndEmitTelemetry(_vsSolutionTelemetryEmitQueue.Value, NuGetVSSolutionClose); - } - catch (Exception) - { - // Currently do nothing. - } - - //_vsSolutionTelemetryEvents.Clear(); - _vsSolutionTelemetryEmitQueue.Value.Clear(); - } - private void EnqueueVSSolutionPowershellTelemetry() { var vsSolutionPowershellTelemetry = _nuGetTelemetryCollector.GetSolutionTelemetryEvents().FirstOrDefault(e => e.Name == PowerShellExecuteCommand); diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryHandler.cs similarity index 96% rename from src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs rename to src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryHandler.cs index 683e4e7b836..7c7e803de71 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryEmit.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryHandler.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Linq; @@ -17,7 +16,7 @@ public class VsPowerShellHostTelemetryEmit : VsInstanceTelemetryConsts private bool _isTelemetryEmitted; private int _pmcExecutedCount; private int _nonPmcExecutedCount; - private readonly INuGetTelemetryCollector _nugetTelemetryAggregate; + private readonly INuGetTelemetryCollector _nuGetTelemetryCollector; // There are 8 bits in byte which used as boolean flags. // 0 - Did any nuget command execute during current VS solution session? @@ -32,7 +31,7 @@ public class VsPowerShellHostTelemetryEmit : VsInstanceTelemetryConsts public VsPowerShellHostTelemetryEmit() { - _nugetTelemetryAggregate = ServiceLocator.GetInstance(); + _nuGetTelemetryCollector = ServiceLocator.GetInstance(); } public void RecordPSHostInitializeOrigin(bool isPMC) @@ -125,7 +124,7 @@ public void EmitPowerShellLoadedTelemetry(bool isPMC) } else { - _nugetTelemetryAggregate.AddSolutionTelemetryEvent(telemetryEvent); + _nuGetTelemetryCollector?.AddSolutionTelemetryEvent(telemetryEvent); } } } @@ -151,7 +150,7 @@ public void EmitPowershellUsageTelemetry(bool withSolution) { LoadedFromPMC, TestAllBitsSet(PowerShellHostInstances, 0b00000001) }, { SolutionLoaded, withSolution} }); - _nugetTelemetryAggregate.AddSolutionTelemetryEvent(telemetryEvent); + _nuGetTelemetryCollector?.AddSolutionTelemetryEvent(telemetryEvent); _pmcExecutedCount = 0; _nonPmcExecutedCount = 0; From c00e38083d07a611576e5b6ca7091e6c45a99350 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Sat, 16 Jan 2021 17:03:20 -0800 Subject: [PATCH 36/59] Refactor to use separate telemetry-event classes. --- .../Xamls/ConsoleContainer.xaml.cs | 10 +- ...uGet.PackageManagement.VisualStudio.csproj | 7 +- .../PMC/NugetPowershellLoadedEvent.cs | 15 --- .../Telemetry/PMC/NugetVSSolutionClose.cs | 15 --- .../Telemetry/INuGetTelemetryCollector.cs | 2 - .../Telemetry/NuGetTelemetryCollector.cs | 3 +- .../Powershell/NuGetPowershellLoadedEvent.cs | 17 +++ .../NuGetPowershellVSInstanceCloseEvent.cs | 30 +++++ .../NuGetPowershellVSSolutionCloseEvent.cs | 55 ++++++++ ...nsts.cs => VSPowershellTelemetryConsts.cs} | 2 +- .../VsInstancePowershellTelemetryEmitter.cs | 119 +++++------------- .../VsPowerShellHostTelemetryHandler.cs | 39 +++--- 12 files changed, 166 insertions(+), 148 deletions(-) delete mode 100644 src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/PMC/NugetPowershellLoadedEvent.cs delete mode 100644 src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/PMC/NugetVSSolutionClose.cs create mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellLoadedEvent.cs create mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSInstanceCloseEvent.cs create mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs rename src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/{VsInstanceTelemetryConsts.cs => VSPowershellTelemetryConsts.cs} (97%) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index 37cb4de0618..f9d25846a23 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -99,10 +99,10 @@ public void Dispose() { // Work around to detect if PMC loaded automatically because it was last focused window. var reopenAtStart = IsLoaded; - var telemetryEvent = new TelemetryEvent(VsInstanceTelemetryConsts.PackageManagerConsoleWindowsLoad, new Dictionary + var telemetryEvent = new TelemetryEvent(VSPowershellTelemetryConsts.PackageManagerConsoleWindowsLoad, new Dictionary { - { VsInstanceTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, - { VsInstanceTelemetryConsts.ReOpenAtStart, reopenAtStart} + { VSPowershellTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, + { VSPowershellTelemetryConsts.ReOpenAtStart, reopenAtStart} }); _nugetSolutionTelemetry?.AddSolutionTelemetryEvent(telemetryEvent); @@ -128,9 +128,9 @@ void ConsoleContainer_Loaded(object sender, RoutedEventArgs e) private void EmitPowershellUsageTelemetry() { - var telemetryEvent = new TelemetryEvent(VsInstanceTelemetryConsts.PackageManagerConsoleWindowsLoad, new Dictionary + var telemetryEvent = new TelemetryEvent(VSPowershellTelemetryConsts.PackageManagerConsoleWindowsLoad, new Dictionary { - { VsInstanceTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, + { VSPowershellTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, }); _nugetSolutionTelemetry?.AddSolutionTelemetryEvent(telemetryEvent); diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/NuGet.PackageManagement.VisualStudio.csproj b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/NuGet.PackageManagement.VisualStudio.csproj index 3ba4fc82b39..3eb58d80716 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/NuGet.PackageManagement.VisualStudio.csproj +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/NuGet.PackageManagement.VisualStudio.csproj @@ -173,8 +173,6 @@ - - @@ -322,10 +320,7 @@ - - - - + diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/PMC/NugetPowershellLoadedEvent.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/PMC/NugetPowershellLoadedEvent.cs deleted file mode 100644 index ac5db8c0f0e..00000000000 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/PMC/NugetPowershellLoadedEvent.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using NuGet.Common; - -namespace NuGet.PackageManagement.VisualStudio.Telemetry.PMC -{ - public class NugetPowershellLoadedEvent : TelemetryEvent - { - public NugetPowershellLoadedEvent() : base("nugetpowershellloaded") - { - } - } -} diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/PMC/NugetVSSolutionClose.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/PMC/NugetVSSolutionClose.cs deleted file mode 100644 index b05c8d63a45..00000000000 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/PMC/NugetVSSolutionClose.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace NuGet.PackageManagement.VisualStudio.Telemetry.PMC -{ - public class NugetVSSolutionClose - { - } -} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs index e3e0b79f3e1..6ab1bfdeabe 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs @@ -9,8 +9,6 @@ namespace NuGet.VisualStudio.Telemetry // Emit aggregated telemetry at VS solution close or VS instance close. public interface INuGetTelemetryCollector { - /// Add a to telemetry list which will be aggregated and emitted later. - /// Telemetry event to add into aggregation. void AddSolutionTelemetryEvent(TelemetryEvent telemetryData); IReadOnlyList GetSolutionTelemetryEvents(); IReadOnlyList GetVSIntanceTelemetryEvents(); diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs index daf380357ac..09fbf417eac 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs @@ -26,7 +26,8 @@ public NuGetTelemetryCollector() _vsInstanceTelemetryEvents = new ConcurrentBag(); } - // Adds telemetry into list which will be aggregated by end of VS solution sessions or VS instance session. + /// Add a to telemetry list which will be aggregated and emitted later. + /// Telemetry event to add into aggregation. public void AddSolutionTelemetryEvent(TelemetryEvent telemetryData) { _vsSolutionTelemetryEvents.Add(telemetryData); diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellLoadedEvent.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellLoadedEvent.cs new file mode 100644 index 00000000000..90d52c58a46 --- /dev/null +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellLoadedEvent.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using NuGet.Common; +using TelemetryConst = NuGet.VisualStudio.Telemetry.VSPowershellTelemetryConsts; + +namespace NuGet.VisualStudio.Telemetry.Powershell +{ + internal class NuGetPowershellLoadedEvent : TelemetryEvent + { + // Emitted first time powershell gets loaded, so we can detect if VS crash while powershell in use. + public NuGetPowershellLoadedEvent(bool loadedfrompmc) : base(TelemetryConst.NuGetPowerShellLoaded) + { + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.LoadedFromPMC] = TelemetryConst.LoadedFromPMC; + } + } +} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSInstanceCloseEvent.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSInstanceCloseEvent.cs new file mode 100644 index 00000000000..fa9495eaa8e --- /dev/null +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSInstanceCloseEvent.cs @@ -0,0 +1,30 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using NuGet.Common; +using TelemetryConst = NuGet.VisualStudio.Telemetry.VSPowershellTelemetryConsts; + +namespace NuGet.VisualStudio.Telemetry.Powershell +{ + internal class NuGetPowershellVSInstanceCloseEvent : TelemetryEvent + { + // Emitted first time powershell gets loaded, so we can detect if VS crash while powershell in use. + public NuGetPowershellVSInstanceCloseEvent( + int nugetpmcexecutecommandcount, + int nugetpmcwindowloadcount, + int nugetpmuiexecutecommandcount, + int pmcpowershellloadedsolutioncount, + int pmuipowershellloadedsolutioncount, + bool reopenatstart, + int solutioncount) : base(TelemetryConst.NuGetVSInstanceClose) + { + base[TelemetryConst.NuGetPowershellPrefix + "nugetpmcexecutecommandcount"] = nugetpmcexecutecommandcount; + base[TelemetryConst.NuGetPowershellPrefix + "nugetpmcwindowloadcount"] = nugetpmcwindowloadcount; + base[TelemetryConst.NuGetPowershellPrefix + "nugetpmuiexecutecommandcount"] = nugetpmuiexecutecommandcount; + base[TelemetryConst.NuGetPowershellPrefix + "pmcpowershellloadedsolutioncount"] = pmcpowershellloadedsolutioncount; + base[TelemetryConst.NuGetPowershellPrefix + "pmuipowershellloadedsolutioncount"] = pmuipowershellloadedsolutioncount; + base[TelemetryConst.NuGetPowershellPrefix + "reopenatstart"] = reopenatstart; + base[TelemetryConst.NuGetPowershellPrefix + "solutioncount"] = solutioncount; + } + } +} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs new file mode 100644 index 00000000000..6c82baf348a --- /dev/null +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs @@ -0,0 +1,55 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using NuGet.Common; +using TelemetryConst = NuGet.VisualStudio.Telemetry.VSPowershellTelemetryConsts; + +namespace NuGet.VisualStudio.Telemetry.Powershell +{ + internal class NuGetPowershellVSSolutionCloseEvent : TelemetryEvent + { + // Emitted first time powershell gets loaded, so we can detect if VS crash while powershell in use. + public NuGetPowershellVSSolutionCloseEvent() : this( + FirstTimeLoadedFromPMC : false, + FirstTimeLoadedFromPMUI : false, + InitPs1LoadedFromPMCFirst : false, + InitPs1LoadPMC : false, + InitPs1LoadPMUI : false, + LoadedFromPMC : false, + LoadedFromPMUI: false, + NuGetCommandUsed : false, + NuGetPMCExecuteCommandCount : 0, + NuGetPMCWindowLoadCount : 0, + NuGetPMUIExecuteCommandCount : 0, + SolutionLoaded : false) + {} + + public NuGetPowershellVSSolutionCloseEvent( + bool FirstTimeLoadedFromPMC, + bool FirstTimeLoadedFromPMUI, + bool InitPs1LoadedFromPMCFirst, + bool InitPs1LoadPMC, + bool InitPs1LoadPMUI, + bool LoadedFromPMC, + bool LoadedFromPMUI, + bool NuGetCommandUsed, + int NuGetPMCExecuteCommandCount, + int NuGetPMCWindowLoadCount, + int NuGetPMUIExecuteCommandCount, + bool SolutionLoaded) : base(TelemetryConst.NuGetVSSolutionClose) + { + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.FirstTimeLoadedFromPMC] = FirstTimeLoadedFromPMC; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.FirstTimeLoadedFromPMUI] = FirstTimeLoadedFromPMUI; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.InitPs1LoadedFromPMCFirst] = InitPs1LoadedFromPMCFirst; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.InitPs1LoadPMC] = InitPs1LoadPMC; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.InitPs1LoadPMUI] = InitPs1LoadPMUI; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.LoadedFromPMC] = LoadedFromPMC; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.LoadedFromPMUI] = LoadedFromPMUI; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetCommandUsed] = NuGetCommandUsed; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCExecuteCommandCount] = NuGetPMCExecuteCommandCount; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCWindowLoadCount] = NuGetPMCWindowLoadCount; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMUIExecuteCommandCount] = NuGetPMUIExecuteCommandCount; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.SolutionLoaded] = SolutionLoaded; + } + } +} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VSPowershellTelemetryConsts.cs similarity index 97% rename from src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs rename to src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VSPowershellTelemetryConsts.cs index f58c4d9df37..9b7604acea8 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstanceTelemetryConsts.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VSPowershellTelemetryConsts.cs @@ -3,7 +3,7 @@ namespace NuGet.VisualStudio.Telemetry { - public abstract class VsInstanceTelemetryConsts + public sealed class VSPowershellTelemetryConsts { // PMC, PMUI powershell telemetry consts public const string NuGetPMCExecuteCommandCount = "NuGetPMCExecuteCommandCount"; diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs index 58689f738c8..360690785e5 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs @@ -2,28 +2,35 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Linq; using NuGet.Common; +using NuGet.VisualStudio.Telemetry.Powershell; +using TelemetryConst = NuGet.VisualStudio.Telemetry.VSPowershellTelemetryConsts; namespace NuGet.VisualStudio.Telemetry { [Export(typeof(VsInstancePowershellTelemetryEmitter))] [PartCreationPolicy(CreationPolicy.Shared)] - public sealed class VsInstancePowershellTelemetryEmitter : VsInstanceTelemetryConsts + public sealed class VsInstancePowershellTelemetryEmitter { private int _solutionCount; - private Lazy> _vsSolutionTelemetryEmitQueue; - private Lazy> _vsInstanceTelemetryEmitQueue; + //private Lazy> _vsSolutionTelemetryEmitQueue; + //private Lazy> _vsInstanceTelemetryEmitQueue; + private Lazy> _vsSolutionTelemetryEmitQueue; + private Lazy> _vsInstanceTelemetryEmitQueue; private INuGetTelemetryCollector _nuGetTelemetryCollector; private Lazy> _vsInstanceTelemetryEvents; [ImportingConstructor] public VsInstancePowershellTelemetryEmitter(INuGetTelemetryCollector nuGetTelemetryCollector) { - _vsSolutionTelemetryEmitQueue = new Lazy>(() => new Dictionary()); - _vsInstanceTelemetryEmitQueue = new Lazy>(() => new Dictionary()); + //_vsSolutionTelemetryEmitQueue = new Lazy>(() => new Dictionary()); + //_vsInstanceTelemetryEmitQueue = new Lazy>(() => new Dictionary()); + _vsSolutionTelemetryEmitQueue = new Lazy>(() => new ConcurrentBag()); + _vsInstanceTelemetryEmitQueue = new Lazy>(() => new ConcurrentBag()); _nuGetTelemetryCollector = nuGetTelemetryCollector; _vsInstanceTelemetryEvents = new Lazy>(() => { @@ -53,17 +60,8 @@ public void SolutionClosedEmit() try { // Queue all different types of telemetries and do some processing prior to emit. - EnqueueVSSolutionPowershellTelemetry(); - - // Add other telemetry type queuing here in the future. You can emit many different types of telemetry other than powershell here. - // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. - // Using prefix avoid collision of property names from different types of telemetry. - - // Actual emit - CombineAndEmitTelemetry(_vsSolutionTelemetryEmitQueue.Value, NuGetVSSolutionClose); - + VSSolutionPowershellTelemetryEmit(); _nuGetTelemetryCollector.ClearSolutionTelemetryEvents(); - _vsSolutionTelemetryEmitQueue.Value.Clear(); } catch (Exception) { @@ -79,13 +77,7 @@ public void VSInstanceClosedTelemetryEmit() // Handle edge cases. EmitPMCUsedWithoutSolution(); - EnqueueVSInstancePowershellTelemetry(); - - // Add other telemetry type queuing here in the future. You can emit many different types of telemetry here. - // Each of them differentiate by prefix. i.e vs.nuget.nugetpowershell.xxxx here nugetpowershell (NugetPowershellPrefix) differentiating prefix. - // Using prefix avoid collision of property names from different types of telemetry. - - CombineAndEmitTelemetry(_vsInstanceTelemetryEmitQueue.Value, NuGetVSInstanceClose); + VSInstancePowershellTelemetryEmit(); } catch (Exception) { @@ -97,7 +89,7 @@ private void EmitPMCUsedWithoutSolution() { // Edge case: PMC window can be opened without any solution at all, but sometimes TelemetryActivity.NuGetTelemetryService is not ready yet when PMC open. // In general we want to emit this telemetry right away, but not possible then emit later. - var nuGetPowerShellLoadedEvent = _nuGetTelemetryCollector.GetSolutionTelemetryEvents().FirstOrDefault(e => e.Name == VsInstanceTelemetryConsts.NuGetPowerShellLoaded); + var nuGetPowerShellLoadedEvent = _nuGetTelemetryCollector.GetSolutionTelemetryEvents().FirstOrDefault(e => e.Name == TelemetryConst.NuGetPowerShellLoaded); if (nuGetPowerShellLoadedEvent != null) { @@ -106,91 +98,48 @@ private void EmitPMCUsedWithoutSolution() } // If there is not emitted PowerShellExecuteCommand telemetry. - if (_nuGetTelemetryCollector.GetSolutionTelemetryEvents().Any(e => e.Name == PowerShellExecuteCommand)) + if (_nuGetTelemetryCollector.GetSolutionTelemetryEvents().Any(e => e is NuGetPowershellVSSolutionCloseEvent)) { SolutionClosedEmit(); } } - private void EnqueueVSSolutionPowershellTelemetry() + private void VSSolutionPowershellTelemetryEmit() { - var vsSolutionPowershellTelemetry = _nuGetTelemetryCollector.GetSolutionTelemetryEvents().FirstOrDefault(e => e.Name == PowerShellExecuteCommand); + NuGetPowershellVSSolutionCloseEvent vsSolutionPowershellTelemetry = _nuGetTelemetryCollector.GetSolutionTelemetryEvents().FirstOrDefault(e => e is NuGetPowershellVSSolutionCloseEvent) as NuGetPowershellVSSolutionCloseEvent; // If powershell(PMC/PMUI) is not loaded at all then we need to create default telemetry event which will be emitted. if (vsSolutionPowershellTelemetry == null) { - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, 0); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, 0); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetCommandUsed, false); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + InitPs1LoadPMC, false); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + InitPs1LoadPMUI, false); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + InitPs1LoadedFromPMCFirst, false); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMUI, false); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMC, false); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + LoadedFromPMUI, false); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + LoadedFromPMC, false); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + SolutionLoaded, true); + vsSolutionPowershellTelemetry = new NuGetPowershellVSSolutionCloseEvent(); } else { // PMC opened, but no command executed nor any solution was loaded. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore. - if (!(bool)vsSolutionPowershellTelemetry[SolutionLoaded] && (int)vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount] == 0) + if (!(bool)vsSolutionPowershellTelemetry[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.SolutionLoaded] && (int)vsSolutionPowershellTelemetry[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCExecuteCommandCount] == 0) { return; } - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMCExecuteCommandCount]); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, vsSolutionPowershellTelemetry[NuGetPMUIExecuteCommandCount]); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetCommandUsed, vsSolutionPowershellTelemetry[NuGetCommandUsed]); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + InitPs1LoadPMC, vsSolutionPowershellTelemetry[InitPs1LoadPMC]); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + InitPs1LoadPMUI, vsSolutionPowershellTelemetry[InitPs1LoadPMUI]); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + InitPs1LoadedFromPMCFirst, vsSolutionPowershellTelemetry[InitPs1LoadedFromPMCFirst]); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMUI, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMUI]); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + FirstTimeLoadedFromPMC, vsSolutionPowershellTelemetry[FirstTimeLoadedFromPMC]); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + LoadedFromPMUI, vsSolutionPowershellTelemetry[LoadedFromPMUI]); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + LoadedFromPMC, vsSolutionPowershellTelemetry[LoadedFromPMC]); - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + SolutionLoaded, vsSolutionPowershellTelemetry[SolutionLoaded]); + vsSolutionPowershellTelemetry[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCWindowLoadCount] = _nuGetTelemetryCollector.GetSolutionTelemetryEvents().Where(e => e[TelemetryConst.NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPMCWindowLoadCount]); } - _vsSolutionTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetPMCWindowLoadCount, _nuGetTelemetryCollector.GetSolutionTelemetryEvents().Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); - } - - private void EnqueueVSInstancePowershellTelemetry() - { - // Whether PMC window re-open at start by default next time VS open? - _vsInstanceTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + ReOpenAtStart, _vsInstanceTelemetryEvents.Value.Where(e => e[ReOpenAtStart] != null).Any()); - // PMC Window load count - _vsInstanceTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetPMCWindowLoadCount, _vsInstanceTelemetryEvents.Value.Where(e => e[NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[NuGetPMCWindowLoadCount])); - // PMC number of commands executed. - _vsInstanceTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetPMCExecuteCommandCount, _vsInstanceTelemetryEvents.Value.Where(e => e[NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMCExecuteCommandCount])); - // PMUI number of powershell commands executed. - _vsInstanceTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount, _vsInstanceTelemetryEvents.Value.Where(e => e[NuGetPMUIExecuteCommandCount] is int).Sum(e => (int)e[NuGetPMUIExecuteCommandCount])); - // Number of actual solutions loaded during VS instance duration. - _vsInstanceTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + SolutionCount, _solutionCount); - // Number of solutions solutions where PMC PowerShellHost was loaded. - _vsInstanceTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + PMCPowerShellLoadedSolutionCount, _vsInstanceTelemetryEvents.Value.Where(e => e[SolutionLoaded] is bool && (bool)e[SolutionLoaded] && e[LoadedFromPMC] is bool).Count(e => (bool)e[LoadedFromPMC] == true)); - // Number of solutions solutions where PMUI PowerShellHost was loaded. - _vsInstanceTelemetryEmitQueue.Value.Add(NuGetPowershellPrefix + PMUIPowerShellLoadedSolutionCount, _vsInstanceTelemetryEvents.Value.Where(e => e[LoadedFromPMUI] is bool).Count(e => (bool)e[LoadedFromPMUI] == true)); + TelemetryActivity.EmitTelemetryEvent(vsSolutionPowershellTelemetry); } - // Instead of emitting one by one we combine them into single event and each event is a property of this single event. - // Currently we emit vs.nuget.NugetVSSolutionClose, vs.nuget.NugetVSInstanceClose events. - private void CombineAndEmitTelemetry(Dictionary telemetryEvents, string telemetryType) + private void VSInstancePowershellTelemetryEmit() { - // No event to emit - if (!telemetryEvents.Keys.Any()) - { - return; - } - - var vsSolutionCloseTelemetry = new TelemetryEvent(telemetryType, new Dictionary()); - - foreach (KeyValuePair telemetryEvent in telemetryEvents) - { - vsSolutionCloseTelemetry[telemetryEvent.Key] = telemetryEvent.Value; - } - - TelemetryActivity.EmitTelemetryEvent(vsSolutionCloseTelemetry); + NuGetPowershellVSInstanceCloseEvent nuGetPowershellVSInstanceCloseEvent = new NuGetPowershellVSInstanceCloseEvent( + nugetpmcexecutecommandcount : _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCExecuteCommandCount]), + nugetpmcwindowloadcount : _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPMCWindowLoadCount]), + nugetpmuiexecutecommandcount: _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMUIExecuteCommandCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMUIExecuteCommandCount]), + pmcpowershellloadedsolutioncount: _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.SolutionLoaded] is bool && (bool)e[TelemetryConst.SolutionLoaded] && e[TelemetryConst.LoadedFromPMC] is bool).Count(e => (bool)e[TelemetryConst.LoadedFromPMC] == true), + pmuipowershellloadedsolutioncount: _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.LoadedFromPMUI] is bool).Count(e => (bool)e[TelemetryConst.LoadedFromPMUI] == true), + reopenatstart: _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.ReOpenAtStart] != null).Any(), + solutioncount: _solutionCount + ); + + TelemetryActivity.EmitTelemetryEvent(nuGetPowershellVSInstanceCloseEvent); } } } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryHandler.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryHandler.cs index 7c7e803de71..99de12dbb11 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryHandler.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryHandler.cs @@ -5,12 +5,14 @@ using System.ComponentModel.Composition; using System.Linq; using NuGet.Common; +using NuGet.VisualStudio.Telemetry.Powershell; +using TelemetryConst = NuGet.VisualStudio.Telemetry.VSPowershellTelemetryConsts; namespace NuGet.VisualStudio.Telemetry { [Export(typeof(VsPowerShellHostTelemetryEmit))] [PartCreationPolicy(CreationPolicy.Shared)] - public class VsPowerShellHostTelemetryEmit : VsInstanceTelemetryConsts + public class VsPowerShellHostTelemetryEmit { private readonly object _telemetryLock = new object(); private bool _isTelemetryEmitted; @@ -110,9 +112,9 @@ public void EmitPowerShellLoadedTelemetry(bool isPMC) // This is PowerShellHost load first time, let's emit this to find out later how many VS instance crash after loading powershell. if (TestAnyBitNotSet(PowerShellHostInstances, 0b00000011)) { - var telemetryEvent = new TelemetryEvent(NuGetPowerShellLoaded, new Dictionary + var telemetryEvent = new TelemetryEvent(TelemetryConst.NuGetPowerShellLoaded, new Dictionary { - { NuGetPowershellPrefix + LoadedFromPMC, isPMC} + { TelemetryConst.NuGetPowershellPrefix + TelemetryConst.LoadedFromPMC, isPMC} }); @@ -136,21 +138,22 @@ public void EmitPowershellUsageTelemetry(bool withSolution) { if (!_isTelemetryEmitted) { - var telemetryEvent = new TelemetryEvent(PowerShellExecuteCommand, new Dictionary - { - { NuGetPMCExecuteCommandCount, _pmcExecutedCount}, - { NuGetPMUIExecuteCommandCount, _nonPmcExecutedCount}, - { NuGetCommandUsed, TestAllBitsSet(PowerShellHostInstances, 0b10000000) }, - { InitPs1LoadPMUI, TestAllBitsSet(PowerShellHostInstances, 0b01000000) }, - { InitPs1LoadPMC, TestAllBitsSet(PowerShellHostInstances, 0b00100000) }, - { InitPs1LoadedFromPMCFirst, TestAllBitsSet(PowerShellHostInstances, 0b00010000) }, - { FirstTimeLoadedFromPMUI, TestAllBitsSet(PowerShellHostInstances, 0b00001000) }, - { FirstTimeLoadedFromPMC, TestAllBitsSet(PowerShellHostInstances, 0b00000100) }, - { LoadedFromPMUI, TestAllBitsSet(PowerShellHostInstances, 0b00000010) }, - { LoadedFromPMC, TestAllBitsSet(PowerShellHostInstances, 0b00000001) }, - { SolutionLoaded, withSolution} - }); - _nuGetTelemetryCollector?.AddSolutionTelemetryEvent(telemetryEvent); + var nugetVSSolutionCloseEvent = new NuGetPowershellVSSolutionCloseEvent( + FirstTimeLoadedFromPMC: TestAllBitsSet(PowerShellHostInstances, 0b00000100), + FirstTimeLoadedFromPMUI: TestAllBitsSet(PowerShellHostInstances, 0b00001000), + InitPs1LoadedFromPMCFirst: TestAllBitsSet(PowerShellHostInstances, 0b00010000), + InitPs1LoadPMC: TestAllBitsSet(PowerShellHostInstances, 0b00100000), + InitPs1LoadPMUI: TestAllBitsSet(PowerShellHostInstances, 0b01000000), + LoadedFromPMC: TestAllBitsSet(PowerShellHostInstances, 0b00000001), + LoadedFromPMUI: TestAllBitsSet(PowerShellHostInstances, 0b00000010), + NuGetCommandUsed: TestAllBitsSet(PowerShellHostInstances, 0b10000000), + NuGetPMCExecuteCommandCount: _pmcExecutedCount, + NuGetPMCWindowLoadCount: 0, + NuGetPMUIExecuteCommandCount: _nonPmcExecutedCount, + SolutionLoaded: withSolution + ); + + _nuGetTelemetryCollector?.AddSolutionTelemetryEvent(nugetVSSolutionCloseEvent); _pmcExecutedCount = 0; _nonPmcExecutedCount = 0; From a3d322c74dddfae6314f13d4e16949913a7d5e8e Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Sat, 16 Jan 2021 19:45:15 -0800 Subject: [PATCH 37/59] Correct class name same to file name. --- .../Telemetry/VsInstancePowershellTelemetryEmitter.cs | 2 +- ...etryHandler.cs => VsPowerShellHostTelemetryProcessor.cs} | 6 +++--- .../NuGetConsole.Host.PowerShell/PowerShellHost.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/{VsPowerShellHostTelemetryHandler.cs => VsPowerShellHostTelemetryProcessor.cs} (98%) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs index 360690785e5..e2a8c332a14 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs @@ -25,7 +25,7 @@ public sealed class VsInstancePowershellTelemetryEmitter private Lazy> _vsInstanceTelemetryEvents; [ImportingConstructor] - public VsInstancePowershellTelemetryEmitter(INuGetTelemetryCollector nuGetTelemetryCollector) + internal VsInstancePowershellTelemetryEmitter(INuGetTelemetryCollector nuGetTelemetryCollector) { //_vsSolutionTelemetryEmitQueue = new Lazy>(() => new Dictionary()); //_vsInstanceTelemetryEmitQueue = new Lazy>(() => new Dictionary()); diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryHandler.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryProcessor.cs similarity index 98% rename from src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryHandler.cs rename to src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryProcessor.cs index 99de12dbb11..13b882efc6b 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryHandler.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryProcessor.cs @@ -10,9 +10,9 @@ namespace NuGet.VisualStudio.Telemetry { - [Export(typeof(VsPowerShellHostTelemetryEmit))] + [Export(typeof(VsPowerShellHostTelemetryProcessor))] [PartCreationPolicy(CreationPolicy.Shared)] - public class VsPowerShellHostTelemetryEmit + public class VsPowerShellHostTelemetryProcessor { private readonly object _telemetryLock = new object(); private bool _isTelemetryEmitted; @@ -31,7 +31,7 @@ public class VsPowerShellHostTelemetryEmit // 7 - Did PowerShellHost for PMC created during current VS instance session? private static byte PowerShellHostInstances; - public VsPowerShellHostTelemetryEmit() + public VsPowerShellHostTelemetryProcessor() { _nuGetTelemetryCollector = ServiceLocator.GetInstance(); } diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index f2b6a9538c3..03f266a94bd 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -51,7 +51,7 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private readonly Lazy _settings; private readonly Lazy _sourceControlManagerProvider; private readonly Lazy _commonOperations; - private readonly Lazy _vsPowerShellHostTelemetryEmit; + private readonly Lazy _vsPowerShellHostTelemetryEmit; private readonly Lazy _deleteOnRestartManager; private readonly Lazy _scriptExecutor; private const string ActivePackageSourceKey = "activePackageSource"; @@ -115,7 +115,7 @@ protected PowerShellHost(string name, IRestoreEvents restoreEvents, IRunspaceMan _sourceControlManagerProvider = new Lazy( () => ServiceLocator.GetInstanceSafe()); _commonOperations = new Lazy(() => ServiceLocator.GetInstanceSafe()); - _vsPowerShellHostTelemetryEmit = new Lazy(() => ServiceLocator.GetInstanceSafe()); + _vsPowerShellHostTelemetryEmit = new Lazy(() => ServiceLocator.GetInstanceSafe()); _name = name; IsCommandEnabled = true; From ac5118340af1d9e0904c3afefe86f36789e8dc62 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Sun, 17 Jan 2021 11:47:51 -0800 Subject: [PATCH 38/59] Address PR comment by Fernando --- .../IDE/VSSolutionManager.cs | 2 +- ...uGet.PackageManagement.VisualStudio.csproj | 3 +- .../Powershell/NuGetPowershellLoadedEvent.cs | 2 +- .../NuGetPowershellVSSolutionCloseEvent.cs | 24 ++++++------- .../PowerShellHostTelemetryProcessor.cs} | 3 +- .../Powershell/PowershellTelemetryConsts.cs | 36 +++++++++++++++++++ .../PowershellTelemetryEmitter.cs} | 9 +++-- .../Telemetry/VSPowershellTelemetryConsts.cs | 36 ------------------- .../NuGetConsole.Host.PowerShell.csproj | 13 ++++--- .../PowerShellHost.cs | 1 + 10 files changed, 66 insertions(+), 63 deletions(-) rename src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/{VsPowerShellHostTelemetryProcessor.cs => Powershell/PowerShellHostTelemetryProcessor.cs} (99%) create mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs rename src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/{VsInstancePowershellTelemetryEmitter.cs => Powershell/PowershellTelemetryEmitter.cs} (92%) delete mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VSPowershellTelemetryConsts.cs diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs index 2ffa244633a..a1a191d11f2 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs @@ -26,7 +26,7 @@ using NuGet.Protocol; using NuGet.Protocol.Core.Types; using NuGet.VisualStudio; -using NuGet.VisualStudio.Telemetry; +using NuGet.VisualStudio.Telemetry.Powershell; using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; using Task = System.Threading.Tasks.Task; diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/NuGet.PackageManagement.VisualStudio.csproj b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/NuGet.PackageManagement.VisualStudio.csproj index 3eb58d80716..c10ea336c63 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/NuGet.PackageManagement.VisualStudio.csproj +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/NuGet.PackageManagement.VisualStudio.csproj @@ -320,8 +320,7 @@ - - \ No newline at end of file + diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellLoadedEvent.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellLoadedEvent.cs index 90d52c58a46..5fcb2deaee9 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellLoadedEvent.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellLoadedEvent.cs @@ -8,7 +8,7 @@ namespace NuGet.VisualStudio.Telemetry.Powershell { internal class NuGetPowershellLoadedEvent : TelemetryEvent { - // Emitted first time powershell gets loaded, so we can detect if VS crash while powershell in use. + // Emitted first time powershell gets loaded, so we can detect if VS crashed after powershell loaded. public NuGetPowershellLoadedEvent(bool loadedfrompmc) : base(TelemetryConst.NuGetPowerShellLoaded) { base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.LoadedFromPMC] = TelemetryConst.LoadedFromPMC; diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs index 6c82baf348a..21289cdeddb 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs @@ -10,19 +10,19 @@ internal class NuGetPowershellVSSolutionCloseEvent : TelemetryEvent { // Emitted first time powershell gets loaded, so we can detect if VS crash while powershell in use. public NuGetPowershellVSSolutionCloseEvent() : this( - FirstTimeLoadedFromPMC : false, - FirstTimeLoadedFromPMUI : false, - InitPs1LoadedFromPMCFirst : false, - InitPs1LoadPMC : false, - InitPs1LoadPMUI : false, - LoadedFromPMC : false, + FirstTimeLoadedFromPMC: false, + FirstTimeLoadedFromPMUI: false, + InitPs1LoadedFromPMCFirst: false, + InitPs1LoadPMC: false, + InitPs1LoadPMUI: false, + LoadedFromPMC: false, LoadedFromPMUI: false, - NuGetCommandUsed : false, - NuGetPMCExecuteCommandCount : 0, - NuGetPMCWindowLoadCount : 0, - NuGetPMUIExecuteCommandCount : 0, - SolutionLoaded : false) - {} + NuGetCommandUsed: false, + NuGetPMCExecuteCommandCount: 0, + NuGetPMCWindowLoadCount: 0, + NuGetPMUIExecuteCommandCount: 0, + SolutionLoaded: false) + { } public NuGetPowershellVSSolutionCloseEvent( bool FirstTimeLoadedFromPMC, diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryProcessor.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs similarity index 99% rename from src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryProcessor.cs rename to src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs index 13b882efc6b..e1c5b28886e 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsPowerShellHostTelemetryProcessor.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs @@ -5,10 +5,9 @@ using System.ComponentModel.Composition; using System.Linq; using NuGet.Common; -using NuGet.VisualStudio.Telemetry.Powershell; using TelemetryConst = NuGet.VisualStudio.Telemetry.VSPowershellTelemetryConsts; -namespace NuGet.VisualStudio.Telemetry +namespace NuGet.VisualStudio.Telemetry.Powershell { [Export(typeof(VsPowerShellHostTelemetryProcessor))] [PartCreationPolicy(CreationPolicy.Shared)] diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs new file mode 100644 index 00000000000..f20da754400 --- /dev/null +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs @@ -0,0 +1,36 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace NuGet.VisualStudio.Telemetry +{ + public sealed class VSPowershellTelemetryConsts + { + // PMC, PMUI powershell telemetry consts + public const string NuGetPMCExecuteCommandCount = nameof(NuGetPMCExecuteCommandCount); + public const string NuGetPMUIExecuteCommandCount = nameof(NuGetPMUIExecuteCommandCount); + public const string NuGetCommandUsed = nameof(NuGetCommandUsed); + public const string InitPs1LoadPMUI = nameof(InitPs1LoadPMUI); + public const string InitPs1LoadPMC = nameof(InitPs1LoadPMC); + public const string InitPs1LoadedFromPMCFirst = nameof(InitPs1LoadedFromPMCFirst); + public const string LoadedFromPMUI = nameof(LoadedFromPMUI); + public const string FirstTimeLoadedFromPMUI = nameof(FirstTimeLoadedFromPMUI); + public const string LoadedFromPMC = nameof(LoadedFromPMC); + public const string FirstTimeLoadedFromPMC = nameof(FirstTimeLoadedFromPMC); + public const string SolutionLoaded = nameof(SolutionLoaded); + public const string PowerShellExecuteCommand = nameof(PowerShellExecuteCommand); + public const string NuGetPowerShellLoaded = nameof(NuGetPowerShellLoaded); + + // PMC UI Console Container telemetry consts + public const string PackageManagerConsoleWindowsLoad = nameof(PackageManagerConsoleWindowsLoad); + public const string NuGetPMCWindowLoadCount = nameof(NuGetPMCWindowLoadCount); + public const string ReOpenAtStart = nameof(ReOpenAtStart); + + // Const name for emitting when VS solution close or VS instance close. + public const string NuGetPowershellPrefix = nameof(NuGetPowershellPrefix); // Using prefix prevent accidental same name property collission from different type telemetry. + public const string NuGetVSSolutionClose = nameof(NuGetVSSolutionClose); + public const string NuGetVSInstanceClose = nameof(NuGetVSInstanceClose); + public const string SolutionCount = nameof(SolutionCount); + public const string PMCPowerShellLoadedSolutionCount = nameof(PMCPowerShellLoadedSolutionCount); + public const string PMUIPowerShellLoadedSolutionCount = nameof(PMUIPowerShellLoadedSolutionCount); + } +} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs similarity index 92% rename from src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs rename to src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs index e2a8c332a14..176b67e50f1 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VsInstancePowershellTelemetryEmitter.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs @@ -7,10 +7,9 @@ using System.ComponentModel.Composition; using System.Linq; using NuGet.Common; -using NuGet.VisualStudio.Telemetry.Powershell; using TelemetryConst = NuGet.VisualStudio.Telemetry.VSPowershellTelemetryConsts; -namespace NuGet.VisualStudio.Telemetry +namespace NuGet.VisualStudio.Telemetry.Powershell { [Export(typeof(VsInstancePowershellTelemetryEmitter))] [PartCreationPolicy(CreationPolicy.Shared)] @@ -42,7 +41,7 @@ public void SolutionOpenedTelemetryEmit() { try { - + // Handle edge cases. EmitPMCUsedWithoutSolution(); _nuGetTelemetryCollector.ClearSolutionTelemetryEvents(); @@ -130,8 +129,8 @@ private void VSSolutionPowershellTelemetryEmit() private void VSInstancePowershellTelemetryEmit() { NuGetPowershellVSInstanceCloseEvent nuGetPowershellVSInstanceCloseEvent = new NuGetPowershellVSInstanceCloseEvent( - nugetpmcexecutecommandcount : _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCExecuteCommandCount]), - nugetpmcwindowloadcount : _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPMCWindowLoadCount]), + nugetpmcexecutecommandcount: _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCExecuteCommandCount]), + nugetpmcwindowloadcount: _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPMCWindowLoadCount]), nugetpmuiexecutecommandcount: _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMUIExecuteCommandCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMUIExecuteCommandCount]), pmcpowershellloadedsolutioncount: _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.SolutionLoaded] is bool && (bool)e[TelemetryConst.SolutionLoaded] && e[TelemetryConst.LoadedFromPMC] is bool).Count(e => (bool)e[TelemetryConst.LoadedFromPMC] == true), pmuipowershellloadedsolutioncount: _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.LoadedFromPMUI] is bool).Count(e => (bool)e[TelemetryConst.LoadedFromPMUI] == true), diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VSPowershellTelemetryConsts.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VSPowershellTelemetryConsts.cs deleted file mode 100644 index 9b7604acea8..00000000000 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/VSPowershellTelemetryConsts.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace NuGet.VisualStudio.Telemetry -{ - public sealed class VSPowershellTelemetryConsts - { - // PMC, PMUI powershell telemetry consts - public const string NuGetPMCExecuteCommandCount = "NuGetPMCExecuteCommandCount"; - public const string NuGetPMUIExecuteCommandCount = "NuGetPMUIExecuteCommandCount"; - public const string NuGetCommandUsed = "NuGetCommandUsed"; - public const string InitPs1LoadPMUI = "InitPs1LoadPMUI"; - public const string InitPs1LoadPMC = "InitPs1LoadPMC"; - public const string InitPs1LoadedFromPMCFirst = "InitPs1LoadedFromPMCFirst"; - public const string LoadedFromPMUI = "LoadedFromPMUI"; - public const string FirstTimeLoadedFromPMUI = "FirstTimeLoadedFromPMUI"; - public const string LoadedFromPMC = "LoadedFromPMC"; - public const string FirstTimeLoadedFromPMC = "FirstTimeLoadedFromPMC"; - public const string SolutionLoaded = "SolutionLoaded"; - public const string PowerShellExecuteCommand = "PowerShellExecuteCommand"; - public const string NuGetPowerShellLoaded = "NuGetPowerShellLoaded"; - - // PMC UI Console Container telemetry consts - public const string PackageManagerConsoleWindowsLoad = "PackageManagerConsoleWindowsLoad"; - public const string NuGetPMCWindowLoadCount = "NuGetPMCWindowLoadCount"; - public const string ReOpenAtStart = "ReOpenAtStart"; - - // Const name for emitting when VS solution close or VS instance close. - public const string NuGetPowershellPrefix = "NuGetPowershellPrefix."; // Using prefix prevent accidental same name property collission from different type telemetry. - public const string NuGetVSSolutionClose = "NuGetVSSolutionClose"; - public const string NuGetVSInstanceClose = "NuGetVSInstanceClose"; - public const string SolutionCount = "SolutionCount"; - public const string PMCPowerShellLoadedSolutionCount = "PMCPowerShellLoadedSolutionCount"; - public const string PMUIPowerShellLoadedSolutionCount = "PMUIPowerShellLoadedSolutionCount"; - } -} diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj index a50ff024e4c..f1bc5aeccb3 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/NuGetConsole.Host.PowerShell.csproj @@ -1,4 +1,4 @@ - + @@ -22,7 +22,6 @@ - @@ -155,11 +154,13 @@ $(LocTargets);GetPowerShellCmdletsHelpFile GetLocalizedPowerShellCmdletHelpFile + - + @@ -180,6 +181,7 @@ + @@ -197,14 +199,17 @@ + <_AllLocalizedXmlFiles Include="$(OutputPath)Scripts\**\NuGet.PackageManagement.PowerShellCmdlets.dll-Help.xml" /> <_XmlLocFiles Include="@(_AllLocalizedXmlFiles)"> Modules\NuGet\$([MSBuild]::MakeRelative($(OutputPath)Scripts, %(_AllLocalizedXmlFiles.Identity))) + <_TxtLocFiles Include="$(LocalizationRootDirectory)\%(VSLanguage.Identity)\15\about_NuGet.Cmdlets.help.txt"> Modules\NuGet\%(VSLanguage.Locale)\about_NuGet.Cmdlets.help.txt + - \ No newline at end of file + diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 03f266a94bd..570491a5acf 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -29,6 +29,7 @@ using NuGet.Protocol.Core.Types; using NuGet.VisualStudio; using NuGet.VisualStudio.Telemetry; +using NuGet.VisualStudio.Telemetry.Powershell; using Task = System.Threading.Tasks.Task; namespace NuGetConsole.Host.PowerShell.Implementation From 85fa8ff4efc4cff51e4eebb1b457d9c734075c48 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Sun, 17 Jan 2021 11:54:19 -0800 Subject: [PATCH 39/59] Fix typo. --- .../Telemetry/NuGetTelemetryCollector.cs | 2 +- .../NuGetConsole.Host.PowerShell/Scripts/nuget.psm1 | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs index 09fbf417eac..029656c0430 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs @@ -37,7 +37,7 @@ public void AddSolutionTelemetryEvent(TelemetryEvent telemetryData) public IReadOnlyList GetSolutionTelemetryEvents() => _vsSolutionTelemetryEvents.ToList().AsReadOnly(); public IReadOnlyList GetVSIntanceTelemetryEvents() => _vsInstanceTelemetryEvents.ToList().AsReadOnly(); - // If open new solution then need ability to reset existing solution events. + // If open new solution then need ability to reset previous solution events. public void ClearSolutionTelemetryEvents() { var newBag = new ConcurrentBag(); diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/nuget.psm1 b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/nuget.psm1 index 15d414f1018..71ab8120563 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/nuget.psm1 +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/Scripts/nuget.psm1 @@ -1,8 +1,6 @@ # make sure we stop on exceptions $ErrorActionPreference = "Stop" -# Please note below command names are hard coded in emitting telemetry in VsPowerShellHostTelemetryEmit.cs. If you add/remove command here then please update it there too. - # This object reprents the result value for tab expansion functions when no result is returned. # This is so that we can distinguish it from $null, which has different semantics $NoResultValue = New-Object PSObject -Property @{ NoResult = $true } @@ -393,4 +391,4 @@ function Format-ProjectName { ) return $project.name -} +} \ No newline at end of file From 43a902775d41bc998b2e0e9bdf99dfd9a6c3c525 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Sun, 17 Jan 2021 13:57:16 -0800 Subject: [PATCH 40/59] Correct event class names --- .../NuGet.Console/Xamls/ConsoleContainer.xaml.cs | 10 +++++----- .../IDE/VSSolutionManager.cs | 8 ++++---- .../Powershell/NuGetPowershellLoadedEvent.cs | 2 +- .../NuGetPowershellVSInstanceCloseEvent.cs | 2 +- .../NuGetPowershellVSSolutionCloseEvent.cs | 2 +- .../Powershell/PowerShellHostTelemetryProcessor.cs | 8 ++++---- .../Powershell/PowershellTelemetryConsts.cs | 2 +- .../Powershell/PowershellTelemetryEmitter.cs | 12 ++++++------ .../NuGetConsole.Host.PowerShell/PowerShellHost.cs | 4 ++-- 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index f9d25846a23..e314bb544e5 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -99,10 +99,10 @@ public void Dispose() { // Work around to detect if PMC loaded automatically because it was last focused window. var reopenAtStart = IsLoaded; - var telemetryEvent = new TelemetryEvent(VSPowershellTelemetryConsts.PackageManagerConsoleWindowsLoad, new Dictionary + var telemetryEvent = new TelemetryEvent(PowershellTelemetryConsts.PackageManagerConsoleWindowsLoad, new Dictionary { - { VSPowershellTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, - { VSPowershellTelemetryConsts.ReOpenAtStart, reopenAtStart} + { PowershellTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, + { PowershellTelemetryConsts.ReOpenAtStart, reopenAtStart} }); _nugetSolutionTelemetry?.AddSolutionTelemetryEvent(telemetryEvent); @@ -128,9 +128,9 @@ void ConsoleContainer_Loaded(object sender, RoutedEventArgs e) private void EmitPowershellUsageTelemetry() { - var telemetryEvent = new TelemetryEvent(VSPowershellTelemetryConsts.PackageManagerConsoleWindowsLoad, new Dictionary + var telemetryEvent = new TelemetryEvent(PowershellTelemetryConsts.PackageManagerConsoleWindowsLoad, new Dictionary { - { VSPowershellTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, + { PowershellTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, }); _nugetSolutionTelemetry?.AddSolutionTelemetryEvent(telemetryEvent); diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs index a1a191d11f2..bbfe2c87f64 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs @@ -56,7 +56,7 @@ public sealed class VSSolutionManager : IVsSolutionManager, IVsSelectionEvents, private readonly IVsProjectAdapterProvider _vsProjectAdapterProvider; private readonly Common.ILogger _logger; private readonly Lazy _settings; - private VsInstancePowershellTelemetryEmitter _vsIntanceTelemetryEmitter; + private PowershellTelemetryEmitter _vsIntanceTelemetryEmitter; private bool _initialized; private bool _cacheInitialized; @@ -128,7 +128,7 @@ internal VSSolutionManager( [Import("VisualStudioActivityLogger")] Common.ILogger logger, Lazy settings, - VsInstancePowershellTelemetryEmitter vsIntanceTelemetryEmitter, + PowershellTelemetryEmitter vsIntanceTelemetryEmitter, JoinableTaskContext joinableTaskContext) : this(AsyncServiceProvider.GlobalProvider, projectSystemCache, @@ -150,7 +150,7 @@ internal VSSolutionManager( IVsProjectAdapterProvider vsProjectAdapterProvider, ILogger logger, Lazy settings, - VsInstancePowershellTelemetryEmitter vsIntanceTelemetryEmitter, + PowershellTelemetryEmitter vsIntanceTelemetryEmitter, JoinableTaskContext joinableTaskContext) { Assumes.Present(asyncServiceProvider); @@ -1043,7 +1043,7 @@ private void OnSolutionOpened(object sender, EventArgs e) private void OnSolutionClosed(object sender, EventArgs e) { - _vsIntanceTelemetryEmitter.SolutionClosedEmit(); + _vsIntanceTelemetryEmitter.SolutionClosedTelemetryEmit(); } public void Dispose() diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellLoadedEvent.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellLoadedEvent.cs index 5fcb2deaee9..3692a4d644e 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellLoadedEvent.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellLoadedEvent.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using NuGet.Common; -using TelemetryConst = NuGet.VisualStudio.Telemetry.VSPowershellTelemetryConsts; +using TelemetryConst = NuGet.VisualStudio.Telemetry.PowershellTelemetryConsts; namespace NuGet.VisualStudio.Telemetry.Powershell { diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSInstanceCloseEvent.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSInstanceCloseEvent.cs index fa9495eaa8e..47e937bcaf9 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSInstanceCloseEvent.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSInstanceCloseEvent.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using NuGet.Common; -using TelemetryConst = NuGet.VisualStudio.Telemetry.VSPowershellTelemetryConsts; +using TelemetryConst = NuGet.VisualStudio.Telemetry.PowershellTelemetryConsts; namespace NuGet.VisualStudio.Telemetry.Powershell { diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs index 21289cdeddb..cf4e19c5807 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using NuGet.Common; -using TelemetryConst = NuGet.VisualStudio.Telemetry.VSPowershellTelemetryConsts; +using TelemetryConst = NuGet.VisualStudio.Telemetry.PowershellTelemetryConsts; namespace NuGet.VisualStudio.Telemetry.Powershell { diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs index e1c5b28886e..dade16a8ab7 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs @@ -5,13 +5,13 @@ using System.ComponentModel.Composition; using System.Linq; using NuGet.Common; -using TelemetryConst = NuGet.VisualStudio.Telemetry.VSPowershellTelemetryConsts; +using TelemetryConst = NuGet.VisualStudio.Telemetry.PowershellTelemetryConsts; namespace NuGet.VisualStudio.Telemetry.Powershell { - [Export(typeof(VsPowerShellHostTelemetryProcessor))] + [Export(typeof(PowerShellHostTelemetryProcessor))] [PartCreationPolicy(CreationPolicy.Shared)] - public class VsPowerShellHostTelemetryProcessor + public class PowerShellHostTelemetryProcessor { private readonly object _telemetryLock = new object(); private bool _isTelemetryEmitted; @@ -30,7 +30,7 @@ public class VsPowerShellHostTelemetryProcessor // 7 - Did PowerShellHost for PMC created during current VS instance session? private static byte PowerShellHostInstances; - public VsPowerShellHostTelemetryProcessor() + public PowerShellHostTelemetryProcessor() { _nuGetTelemetryCollector = ServiceLocator.GetInstance(); } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs index f20da754400..60cb43b5fad 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs @@ -3,7 +3,7 @@ namespace NuGet.VisualStudio.Telemetry { - public sealed class VSPowershellTelemetryConsts + public sealed class PowershellTelemetryConsts { // PMC, PMUI powershell telemetry consts public const string NuGetPMCExecuteCommandCount = nameof(NuGetPMCExecuteCommandCount); diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs index 176b67e50f1..dad44d7641e 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs @@ -7,13 +7,13 @@ using System.ComponentModel.Composition; using System.Linq; using NuGet.Common; -using TelemetryConst = NuGet.VisualStudio.Telemetry.VSPowershellTelemetryConsts; +using TelemetryConst = NuGet.VisualStudio.Telemetry.PowershellTelemetryConsts; namespace NuGet.VisualStudio.Telemetry.Powershell { - [Export(typeof(VsInstancePowershellTelemetryEmitter))] + [Export(typeof(PowershellTelemetryEmitter))] [PartCreationPolicy(CreationPolicy.Shared)] - public sealed class VsInstancePowershellTelemetryEmitter + public sealed class PowershellTelemetryEmitter { private int _solutionCount; //private Lazy> _vsSolutionTelemetryEmitQueue; @@ -24,7 +24,7 @@ public sealed class VsInstancePowershellTelemetryEmitter private Lazy> _vsInstanceTelemetryEvents; [ImportingConstructor] - internal VsInstancePowershellTelemetryEmitter(INuGetTelemetryCollector nuGetTelemetryCollector) + internal PowershellTelemetryEmitter(INuGetTelemetryCollector nuGetTelemetryCollector) { //_vsSolutionTelemetryEmitQueue = new Lazy>(() => new Dictionary()); //_vsInstanceTelemetryEmitQueue = new Lazy>(() => new Dictionary()); @@ -54,7 +54,7 @@ public void SolutionOpenedTelemetryEmit() } // Emit VS solution session telemetry when solution is closed. - public void SolutionClosedEmit() + public void SolutionClosedTelemetryEmit() { try { @@ -99,7 +99,7 @@ private void EmitPMCUsedWithoutSolution() // If there is not emitted PowerShellExecuteCommand telemetry. if (_nuGetTelemetryCollector.GetSolutionTelemetryEvents().Any(e => e is NuGetPowershellVSSolutionCloseEvent)) { - SolutionClosedEmit(); + SolutionClosedTelemetryEmit(); } } diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 570491a5acf..48408eb18af 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -52,7 +52,7 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private readonly Lazy _settings; private readonly Lazy _sourceControlManagerProvider; private readonly Lazy _commonOperations; - private readonly Lazy _vsPowerShellHostTelemetryEmit; + private readonly Lazy _vsPowerShellHostTelemetryEmit; private readonly Lazy _deleteOnRestartManager; private readonly Lazy _scriptExecutor; private const string ActivePackageSourceKey = "activePackageSource"; @@ -116,7 +116,7 @@ protected PowerShellHost(string name, IRestoreEvents restoreEvents, IRunspaceMan _sourceControlManagerProvider = new Lazy( () => ServiceLocator.GetInstanceSafe()); _commonOperations = new Lazy(() => ServiceLocator.GetInstanceSafe()); - _vsPowerShellHostTelemetryEmit = new Lazy(() => ServiceLocator.GetInstanceSafe()); + _vsPowerShellHostTelemetryEmit = new Lazy(() => ServiceLocator.GetInstanceSafe()); _name = name; IsCommandEnabled = true; From c80194698b5858543c2f44a11e96f37187f3dd09 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Sun, 17 Jan 2021 20:57:39 -0800 Subject: [PATCH 41/59] Clean up and improve Powershell/PowershellTelemetryEmitter. --- .../Telemetry/INuGetTelemetryCollector.cs | 2 +- .../Telemetry/NuGetTelemetryCollector.cs | 22 +++++------ .../Powershell/PowershellTelemetryEmitter.cs | 39 +++++++++---------- .../PowerShellHost.cs | 20 +++++----- 4 files changed, 40 insertions(+), 43 deletions(-) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs index 6ab1bfdeabe..1e6a856d6f8 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs @@ -10,7 +10,7 @@ namespace NuGet.VisualStudio.Telemetry public interface INuGetTelemetryCollector { void AddSolutionTelemetryEvent(TelemetryEvent telemetryData); - IReadOnlyList GetSolutionTelemetryEvents(); + IReadOnlyList GetVSSolutionTelemetryEvents(); IReadOnlyList GetVSIntanceTelemetryEvents(); void ClearSolutionTelemetryEvents(); } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs index 029656c0430..dd155921d75 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs @@ -16,32 +16,32 @@ namespace NuGet.VisualStudio.Telemetry internal sealed class NuGetTelemetryCollector : INuGetTelemetryCollector { // _solutionTelemetryEvents hold telemetry for current VS solution session. - private ConcurrentBag _vsSolutionTelemetryEvents; + private Lazy> _vsSolutionTelemetryEvents; // _vsInstanceTelemetryEvents hold telemetry for current VS instance session. - private readonly ConcurrentBag _vsInstanceTelemetryEvents; + private readonly Lazy> _vsInstanceTelemetryEvents; public NuGetTelemetryCollector() { - _vsSolutionTelemetryEvents = new ConcurrentBag(); - _vsInstanceTelemetryEvents = new ConcurrentBag(); + _vsSolutionTelemetryEvents = new Lazy>(); + _vsInstanceTelemetryEvents = new Lazy>(); } /// Add a to telemetry list which will be aggregated and emitted later. /// Telemetry event to add into aggregation. public void AddSolutionTelemetryEvent(TelemetryEvent telemetryData) { - _vsSolutionTelemetryEvents.Add(telemetryData); - _vsInstanceTelemetryEvents.Add(telemetryData); + _vsSolutionTelemetryEvents.Value.Add(telemetryData); + _vsInstanceTelemetryEvents.Value.Add(telemetryData); } - public IReadOnlyList GetSolutionTelemetryEvents() => _vsSolutionTelemetryEvents.ToList().AsReadOnly(); - public IReadOnlyList GetVSIntanceTelemetryEvents() => _vsInstanceTelemetryEvents.ToList().AsReadOnly(); + public IReadOnlyList GetVSSolutionTelemetryEvents() => _vsSolutionTelemetryEvents.Value.ToList(); + public IReadOnlyList GetVSIntanceTelemetryEvents() => _vsInstanceTelemetryEvents.Value.ToList(); - // If open new solution then need ability to reset previous solution events. + // If open new solution then need to clear previous solution events. public void ClearSolutionTelemetryEvents() { - var newBag = new ConcurrentBag(); - Interlocked.Exchange>(ref _vsSolutionTelemetryEvents, newBag); + var newBag = new Lazy>(); + Interlocked.Exchange(ref _vsSolutionTelemetryEvents, newBag); } } } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs index dad44d7641e..57c0d2aa4b1 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Linq; @@ -16,24 +15,19 @@ namespace NuGet.VisualStudio.Telemetry.Powershell public sealed class PowershellTelemetryEmitter { private int _solutionCount; - //private Lazy> _vsSolutionTelemetryEmitQueue; - //private Lazy> _vsInstanceTelemetryEmitQueue; - private Lazy> _vsSolutionTelemetryEmitQueue; - private Lazy> _vsInstanceTelemetryEmitQueue; + private bool _powershellLoadEventEmitted = false; private INuGetTelemetryCollector _nuGetTelemetryCollector; - private Lazy> _vsInstanceTelemetryEvents; + private Func> _vsSolutionTelemetryEvents; + private Lazy> _vsInstanceTelemetryEvents; [ImportingConstructor] internal PowershellTelemetryEmitter(INuGetTelemetryCollector nuGetTelemetryCollector) { - //_vsSolutionTelemetryEmitQueue = new Lazy>(() => new Dictionary()); - //_vsInstanceTelemetryEmitQueue = new Lazy>(() => new Dictionary()); - _vsSolutionTelemetryEmitQueue = new Lazy>(() => new ConcurrentBag()); - _vsInstanceTelemetryEmitQueue = new Lazy>(() => new ConcurrentBag()); _nuGetTelemetryCollector = nuGetTelemetryCollector; - _vsInstanceTelemetryEvents = new Lazy>(() => + _vsSolutionTelemetryEvents = () => _nuGetTelemetryCollector?.GetVSSolutionTelemetryEvents().ToList(); + _vsInstanceTelemetryEvents = new Lazy>(() => { - return _nuGetTelemetryCollector?.GetVSIntanceTelemetryEvents(); + return _nuGetTelemetryCollector?.GetVSIntanceTelemetryEvents().ToList(); }); } @@ -41,7 +35,6 @@ public void SolutionOpenedTelemetryEmit() { try { - // Handle edge cases. EmitPMCUsedWithoutSolution(); _nuGetTelemetryCollector.ClearSolutionTelemetryEvents(); @@ -74,8 +67,8 @@ public void VSInstanceClosedTelemetryEmit() try { // Handle edge cases. - EmitPMCUsedWithoutSolution(); + EmitPMCUsedWithoutSolution(); VSInstancePowershellTelemetryEmit(); } catch (Exception) @@ -88,16 +81,20 @@ private void EmitPMCUsedWithoutSolution() { // Edge case: PMC window can be opened without any solution at all, but sometimes TelemetryActivity.NuGetTelemetryService is not ready yet when PMC open. // In general we want to emit this telemetry right away, but not possible then emit later. - var nuGetPowerShellLoadedEvent = _nuGetTelemetryCollector.GetSolutionTelemetryEvents().FirstOrDefault(e => e.Name == TelemetryConst.NuGetPowerShellLoaded); - if (nuGetPowerShellLoadedEvent != null) + if (!_powershellLoadEventEmitted) { - TelemetryActivity.EmitTelemetryEvent(nuGetPowerShellLoadedEvent); - //vsSolutionTelemetryEvents.Remove(nuGetPowerShellLoadedEvent); + var nuGetPowerShellLoadedEvent = _vsSolutionTelemetryEvents().FirstOrDefault(e => e.Name == TelemetryConst.NuGetPowerShellLoaded); + + if (nuGetPowerShellLoadedEvent != null) + { + TelemetryActivity.EmitTelemetryEvent(nuGetPowerShellLoadedEvent); + _powershellLoadEventEmitted = true; + } } // If there is not emitted PowerShellExecuteCommand telemetry. - if (_nuGetTelemetryCollector.GetSolutionTelemetryEvents().Any(e => e is NuGetPowershellVSSolutionCloseEvent)) + if (_vsSolutionTelemetryEvents().Any(e => e is NuGetPowershellVSSolutionCloseEvent)) { SolutionClosedTelemetryEmit(); } @@ -105,7 +102,7 @@ private void EmitPMCUsedWithoutSolution() private void VSSolutionPowershellTelemetryEmit() { - NuGetPowershellVSSolutionCloseEvent vsSolutionPowershellTelemetry = _nuGetTelemetryCollector.GetSolutionTelemetryEvents().FirstOrDefault(e => e is NuGetPowershellVSSolutionCloseEvent) as NuGetPowershellVSSolutionCloseEvent; + NuGetPowershellVSSolutionCloseEvent vsSolutionPowershellTelemetry = _vsSolutionTelemetryEvents().FirstOrDefault(e => e is NuGetPowershellVSSolutionCloseEvent) as NuGetPowershellVSSolutionCloseEvent; // If powershell(PMC/PMUI) is not loaded at all then we need to create default telemetry event which will be emitted. if (vsSolutionPowershellTelemetry == null) @@ -120,7 +117,7 @@ private void VSSolutionPowershellTelemetryEmit() return; } - vsSolutionPowershellTelemetry[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCWindowLoadCount] = _nuGetTelemetryCollector.GetSolutionTelemetryEvents().Where(e => e[TelemetryConst.NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPMCWindowLoadCount]); + vsSolutionPowershellTelemetry[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCWindowLoadCount] = _vsSolutionTelemetryEvents().Where(e => e[TelemetryConst.NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPMCWindowLoadCount]); } TelemetryActivity.EmitTelemetryEvent(vsSolutionPowershellTelemetry); diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 48408eb18af..48c3c311128 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -52,7 +52,7 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private readonly Lazy _settings; private readonly Lazy _sourceControlManagerProvider; private readonly Lazy _commonOperations; - private readonly Lazy _vsPowerShellHostTelemetryEmit; + private readonly Lazy _powerShellHostTelemetryProcessor; private readonly Lazy _deleteOnRestartManager; private readonly Lazy _scriptExecutor; private const string ActivePackageSourceKey = "activePackageSource"; @@ -116,7 +116,7 @@ protected PowerShellHost(string name, IRestoreEvents restoreEvents, IRunspaceMan _sourceControlManagerProvider = new Lazy( () => ServiceLocator.GetInstanceSafe()); _commonOperations = new Lazy(() => ServiceLocator.GetInstanceSafe()); - _vsPowerShellHostTelemetryEmit = new Lazy(() => ServiceLocator.GetInstanceSafe()); + _powerShellHostTelemetryProcessor = new Lazy(() => ServiceLocator.GetInstanceSafe()); _name = name; IsCommandEnabled = true; @@ -316,16 +316,16 @@ public void Initialize(IConsole console) UpdateWorkingDirectory(); // Emit first time Powershell load event to find out later how many VS instance crash after loading powershell. - _vsPowerShellHostTelemetryEmit.Value.EmitPowerShellLoadedTelemetry(console is IWpfConsole); + _powerShellHostTelemetryProcessor.Value.EmitPowerShellLoadedTelemetry(console is IWpfConsole); // Record if PowerShellHost initiated from PMC or PMUI - _vsPowerShellHostTelemetryEmit.Value.RecordPSHostInitializeOrigin(console is IWpfConsole); + _powerShellHostTelemetryProcessor.Value.RecordPSHostInitializeOrigin(console is IWpfConsole); await ExecuteInitScriptsAsync(); _solutionManager.Value.SolutionOpening += (o, e) => { // Hook up solution events, check if PMC is used before without any solution. - _vsPowerShellHostTelemetryEmit.Value.HandleSolutionOpenedEmit(); + _powerShellHostTelemetryProcessor.Value.HandleSolutionOpenedEmit(); }; // check if PMC console is actually opened, then only hook to solution load events. @@ -346,7 +346,7 @@ public void Initialize(IConsole console) _solutionManager.Value.SolutionClosing += (o, e) => { // Hook up solution events, we emit telemetry data from current VS solution session. - _vsPowerShellHostTelemetryEmit.Value.HandleSolutionClosingEmit(); + _powerShellHostTelemetryProcessor.Value.HandleSolutionClosingEmit(); }; _solutionManager.Value.NuGetProjectAdded += (o, e) => UpdateWorkingDirectoryAndAvailableProjects(); @@ -495,7 +495,7 @@ private async Task ExecuteInitPs1Async(string installPath, PackageIdentity ident if (File.Exists(scriptPath)) { // Record if init.ps1 is loaded. - _vsPowerShellHostTelemetryEmit.Value.RecordInitPs1loaded(_activeConsole is IWpfConsole); + _powerShellHostTelemetryProcessor.Value.RecordInitPs1loaded(_activeConsole is IWpfConsole); if (_scriptExecutor.Value.TryMarkVisited(identity, PackageInitPS1State.FoundAndExecuted)) { @@ -555,10 +555,10 @@ public bool Execute(IConsole console, string command, params object[] inputs) } // Increase command execution counters for PMC/PMUI - _vsPowerShellHostTelemetryEmit.Value.IncreaseCommandCounter(console is IWpfConsole); + _powerShellHostTelemetryProcessor.Value.IncreaseCommandCounter(console is IWpfConsole); // Check and add to telemetery if a command is NugetCommand like 'install-package' etc. - _vsPowerShellHostTelemetryEmit.Value.IsNuGetCommand(command); + _powerShellHostTelemetryProcessor.Value.IsNuGetCommand(command); // since install.ps1/uninstall.ps1 could depend on init scripts, so we need to make sure // to run it once for each solution @@ -947,7 +947,7 @@ public void Dispose() // Below emits telemetry in case there was no solution was loaded at all. // But in case there were any solution then this one will ignored because actual data already emitted with solution SolutionClosing event previously. // If no solution loaded nor PMC is engaged at then this will be ignored internally. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore then. - _vsPowerShellHostTelemetryEmit.Value.EmitPowershellUsageTelemetry(false); + _powerShellHostTelemetryProcessor.Value.EmitPowershellUsageTelemetry(false); } #endregion From dea971b995085dfc8c96146c9b1693dfeb039778 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Tue, 19 Jan 2021 12:51:52 -0800 Subject: [PATCH 42/59] Fix typos. --- .../Telemetry/INuGetTelemetryCollector.cs | 2 +- .../NuGetPowershellVSInstanceCloseEvent.cs | 15 +++++++-------- .../NuGetPowershellVSSolutionCloseEvent.cs | 1 - .../PowerShellHostTelemetryProcessor.cs | 2 +- .../Powershell/PowershellTelemetryEmitter.cs | 1 - 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs index 1e6a856d6f8..cbe8bd6acfa 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs @@ -6,7 +6,7 @@ namespace NuGet.VisualStudio.Telemetry { - // Emit aggregated telemetry at VS solution close or VS instance close. + // Collect telemetry for aggregated telemetry emitting later at VS solution/instance close. public interface INuGetTelemetryCollector { void AddSolutionTelemetryEvent(TelemetryEvent telemetryData); diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSInstanceCloseEvent.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSInstanceCloseEvent.cs index 47e937bcaf9..0ee98fe3fc3 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSInstanceCloseEvent.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSInstanceCloseEvent.cs @@ -8,7 +8,6 @@ namespace NuGet.VisualStudio.Telemetry.Powershell { internal class NuGetPowershellVSInstanceCloseEvent : TelemetryEvent { - // Emitted first time powershell gets loaded, so we can detect if VS crash while powershell in use. public NuGetPowershellVSInstanceCloseEvent( int nugetpmcexecutecommandcount, int nugetpmcwindowloadcount, @@ -18,13 +17,13 @@ public NuGetPowershellVSInstanceCloseEvent( bool reopenatstart, int solutioncount) : base(TelemetryConst.NuGetVSInstanceClose) { - base[TelemetryConst.NuGetPowershellPrefix + "nugetpmcexecutecommandcount"] = nugetpmcexecutecommandcount; - base[TelemetryConst.NuGetPowershellPrefix + "nugetpmcwindowloadcount"] = nugetpmcwindowloadcount; - base[TelemetryConst.NuGetPowershellPrefix + "nugetpmuiexecutecommandcount"] = nugetpmuiexecutecommandcount; - base[TelemetryConst.NuGetPowershellPrefix + "pmcpowershellloadedsolutioncount"] = pmcpowershellloadedsolutioncount; - base[TelemetryConst.NuGetPowershellPrefix + "pmuipowershellloadedsolutioncount"] = pmuipowershellloadedsolutioncount; - base[TelemetryConst.NuGetPowershellPrefix + "reopenatstart"] = reopenatstart; - base[TelemetryConst.NuGetPowershellPrefix + "solutioncount"] = solutioncount; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCExecuteCommandCount] = nugetpmcexecutecommandcount; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCWindowLoadCount] = nugetpmcwindowloadcount; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMUIExecuteCommandCount] = nugetpmuiexecutecommandcount; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.PMCPowerShellLoadedSolutionCount] = pmcpowershellloadedsolutioncount; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.PMUIPowerShellLoadedSolutionCount] = pmuipowershellloadedsolutioncount; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.ReOpenAtStart] = reopenatstart; + base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.SolutionCount] = solutioncount; } } } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs index cf4e19c5807..f748702b82e 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs @@ -8,7 +8,6 @@ namespace NuGet.VisualStudio.Telemetry.Powershell { internal class NuGetPowershellVSSolutionCloseEvent : TelemetryEvent { - // Emitted first time powershell gets loaded, so we can detect if VS crash while powershell in use. public NuGetPowershellVSSolutionCloseEvent() : this( FirstTimeLoadedFromPMC: false, FirstTimeLoadedFromPMUI: false, diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs index dade16a8ab7..c38b3e80312 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs @@ -108,7 +108,7 @@ public void EmitPowerShellLoadedTelemetry(bool isPMC) { lock (_telemetryLock) { - // This is PowerShellHost load first time, let's emit this to find out later how many VS instance crash after loading powershell. + // PowerShellHost loaded first time, let's emit this to find out later how many VS instance crash after loading powershell. if (TestAnyBitNotSet(PowerShellHostInstances, 0b00000011)) { var telemetryEvent = new TelemetryEvent(TelemetryConst.NuGetPowerShellLoaded, new Dictionary diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs index 57c0d2aa4b1..660bad59956 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs @@ -67,7 +67,6 @@ public void VSInstanceClosedTelemetryEmit() try { // Handle edge cases. - EmitPMCUsedWithoutSolution(); VSInstancePowershellTelemetryEmit(); } From 2d780152e6e2303889a487c71e0adcd8a43ce287 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Tue, 19 Jan 2021 15:57:56 -0800 Subject: [PATCH 43/59] Stop passing telemetry event between different methods. --- .../Xamls/ConsoleContainer.xaml.cs | 10 ++- .../Telemetry/INuGetTelemetryCollector.cs | 7 +- .../Telemetry/NuGetTelemetryCollector.cs | 18 ++--- .../PowerShellHostTelemetryProcessor.cs | 47 ++++++----- .../Powershell/PowershellTelemetryConsts.cs | 1 + .../Powershell/PowershellTelemetryEmitter.cs | 78 +++++++++++-------- 6 files changed, 92 insertions(+), 69 deletions(-) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index e314bb544e5..31b84b848f0 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -99,11 +99,12 @@ public void Dispose() { // Work around to detect if PMC loaded automatically because it was last focused window. var reopenAtStart = IsLoaded; - var telemetryEvent = new TelemetryEvent(PowershellTelemetryConsts.PackageManagerConsoleWindowsLoad, new Dictionary + var telemetryEvent = new Dictionary { + { PowershellTelemetryConsts.Name, PowershellTelemetryConsts.PackageManagerConsoleWindowsLoad}, { PowershellTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, { PowershellTelemetryConsts.ReOpenAtStart, reopenAtStart} - }); + }; _nugetSolutionTelemetry?.AddSolutionTelemetryEvent(telemetryEvent); @@ -128,10 +129,11 @@ void ConsoleContainer_Loaded(object sender, RoutedEventArgs e) private void EmitPowershellUsageTelemetry() { - var telemetryEvent = new TelemetryEvent(PowershellTelemetryConsts.PackageManagerConsoleWindowsLoad, new Dictionary + var telemetryEvent = new Dictionary { + { PowershellTelemetryConsts.Name, PowershellTelemetryConsts.PackageManagerConsoleWindowsLoad}, { PowershellTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, - }); + }; _nugetSolutionTelemetry?.AddSolutionTelemetryEvent(telemetryEvent); _windowLoadCount = 0; diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs index cbe8bd6acfa..b004fca8095 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs @@ -2,16 +2,15 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; -using NuGet.Common; namespace NuGet.VisualStudio.Telemetry { // Collect telemetry for aggregated telemetry emitting later at VS solution/instance close. public interface INuGetTelemetryCollector { - void AddSolutionTelemetryEvent(TelemetryEvent telemetryData); - IReadOnlyList GetVSSolutionTelemetryEvents(); - IReadOnlyList GetVSIntanceTelemetryEvents(); + void AddSolutionTelemetryEvent(Dictionary telemetryData); + IReadOnlyList> GetVSSolutionTelemetryEvents(); + IReadOnlyList> GetVSIntanceTelemetryEvents(); void ClearSolutionTelemetryEvents(); } } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs index dd155921d75..7785b93d125 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs @@ -15,32 +15,32 @@ namespace NuGet.VisualStudio.Telemetry [PartCreationPolicy(CreationPolicy.Shared)] internal sealed class NuGetTelemetryCollector : INuGetTelemetryCollector { - // _solutionTelemetryEvents hold telemetry for current VS solution session. - private Lazy> _vsSolutionTelemetryEvents; + // _solutionTelemetryEvents hold telemetry data for current VS solution session. + private Lazy>> _vsSolutionTelemetryEvents; // _vsInstanceTelemetryEvents hold telemetry for current VS instance session. - private readonly Lazy> _vsInstanceTelemetryEvents; + private readonly Lazy>> _vsInstanceTelemetryEvents; public NuGetTelemetryCollector() { - _vsSolutionTelemetryEvents = new Lazy>(); - _vsInstanceTelemetryEvents = new Lazy>(); + _vsSolutionTelemetryEvents = new Lazy>>(); + _vsInstanceTelemetryEvents = new Lazy>>(); } /// Add a to telemetry list which will be aggregated and emitted later. /// Telemetry event to add into aggregation. - public void AddSolutionTelemetryEvent(TelemetryEvent telemetryData) + public void AddSolutionTelemetryEvent(Dictionary telemetryData) { _vsSolutionTelemetryEvents.Value.Add(telemetryData); _vsInstanceTelemetryEvents.Value.Add(telemetryData); } - public IReadOnlyList GetVSSolutionTelemetryEvents() => _vsSolutionTelemetryEvents.Value.ToList(); - public IReadOnlyList GetVSIntanceTelemetryEvents() => _vsInstanceTelemetryEvents.Value.ToList(); + public IReadOnlyList> GetVSSolutionTelemetryEvents() => _vsSolutionTelemetryEvents.Value.ToList(); + public IReadOnlyList> GetVSIntanceTelemetryEvents() => _vsInstanceTelemetryEvents.Value.ToList(); // If open new solution then need to clear previous solution events. public void ClearSolutionTelemetryEvents() { - var newBag = new Lazy>(); + var newBag = new Lazy>>(); Interlocked.Exchange(ref _vsSolutionTelemetryEvents, newBag); } } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs index c38b3e80312..7c5c8246306 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs @@ -111,21 +111,24 @@ public void EmitPowerShellLoadedTelemetry(bool isPMC) // PowerShellHost loaded first time, let's emit this to find out later how many VS instance crash after loading powershell. if (TestAnyBitNotSet(PowerShellHostInstances, 0b00000011)) { - var telemetryEvent = new TelemetryEvent(TelemetryConst.NuGetPowerShellLoaded, new Dictionary - { - { TelemetryConst.NuGetPowershellPrefix + TelemetryConst.LoadedFromPMC, isPMC} - }); - - // Edge case: PMC window can be opened without any solution at all, but sometimes TelemetryActivity.NuGetTelemetryService is not ready yet. // In general we want to emit this telemetry right away. if (TelemetryActivity.NuGetTelemetryService != null) { + var telemetryEvent = new TelemetryEvent(TelemetryConst.NuGetPowerShellLoaded, new Dictionary + { + { TelemetryConst.NuGetPowershellPrefix + TelemetryConst.LoadedFromPMC, isPMC } + }); + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); } else { - _nuGetTelemetryCollector?.AddSolutionTelemetryEvent(telemetryEvent); + _nuGetTelemetryCollector?.AddSolutionTelemetryEvent(new Dictionary + { + { TelemetryConst.Name , TelemetryConst.NuGetPowerShellLoaded }, + { TelemetryConst.NuGetPowershellPrefix + TelemetryConst.LoadedFromPMC, isPMC } + }); } } } @@ -137,20 +140,22 @@ public void EmitPowershellUsageTelemetry(bool withSolution) { if (!_isTelemetryEmitted) { - var nugetVSSolutionCloseEvent = new NuGetPowershellVSSolutionCloseEvent( - FirstTimeLoadedFromPMC: TestAllBitsSet(PowerShellHostInstances, 0b00000100), - FirstTimeLoadedFromPMUI: TestAllBitsSet(PowerShellHostInstances, 0b00001000), - InitPs1LoadedFromPMCFirst: TestAllBitsSet(PowerShellHostInstances, 0b00010000), - InitPs1LoadPMC: TestAllBitsSet(PowerShellHostInstances, 0b00100000), - InitPs1LoadPMUI: TestAllBitsSet(PowerShellHostInstances, 0b01000000), - LoadedFromPMC: TestAllBitsSet(PowerShellHostInstances, 0b00000001), - LoadedFromPMUI: TestAllBitsSet(PowerShellHostInstances, 0b00000010), - NuGetCommandUsed: TestAllBitsSet(PowerShellHostInstances, 0b10000000), - NuGetPMCExecuteCommandCount: _pmcExecutedCount, - NuGetPMCWindowLoadCount: 0, - NuGetPMUIExecuteCommandCount: _nonPmcExecutedCount, - SolutionLoaded: withSolution - ); + var nugetVSSolutionCloseEvent = new Dictionary + { + { TelemetryConst.Name , TelemetryConst.NuGetVSSolutionClose }, + { TelemetryConst.FirstTimeLoadedFromPMC , TestAllBitsSet(PowerShellHostInstances, 0b00000100) }, + { TelemetryConst.FirstTimeLoadedFromPMUI, TestAllBitsSet(PowerShellHostInstances, 0b00001000) }, + { TelemetryConst.InitPs1LoadedFromPMCFirst, TestAllBitsSet(PowerShellHostInstances, 0b00010000) }, + { TelemetryConst.InitPs1LoadPMC, TestAllBitsSet(PowerShellHostInstances, 0b00100000) }, + { TelemetryConst.InitPs1LoadPMUI, TestAllBitsSet(PowerShellHostInstances, 0b01000000) }, + { TelemetryConst.LoadedFromPMC, TestAllBitsSet(PowerShellHostInstances, 0b00000001) }, + { TelemetryConst.LoadedFromPMUI, TestAllBitsSet(PowerShellHostInstances, 0b00000010) }, + { TelemetryConst.NuGetCommandUsed, TestAllBitsSet(PowerShellHostInstances, 0b10000000) }, + { TelemetryConst.NuGetPMCExecuteCommandCount, _pmcExecutedCount }, + { TelemetryConst.NuGetPMCWindowLoadCount, 0 }, + { TelemetryConst.NuGetPMUIExecuteCommandCount, _nonPmcExecutedCount }, + { TelemetryConst.SolutionLoaded, withSolution } + }; _nuGetTelemetryCollector?.AddSolutionTelemetryEvent(nugetVSSolutionCloseEvent); diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs index 60cb43b5fad..f741224116a 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs @@ -26,6 +26,7 @@ public sealed class PowershellTelemetryConsts public const string ReOpenAtStart = nameof(ReOpenAtStart); // Const name for emitting when VS solution close or VS instance close. + public const string Name = nameof(Name); public const string NuGetPowershellPrefix = nameof(NuGetPowershellPrefix); // Using prefix prevent accidental same name property collission from different type telemetry. public const string NuGetVSSolutionClose = nameof(NuGetVSSolutionClose); public const string NuGetVSInstanceClose = nameof(NuGetVSInstanceClose); diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs index 660bad59956..4eea916a94c 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs @@ -17,18 +17,17 @@ public sealed class PowershellTelemetryEmitter private int _solutionCount; private bool _powershellLoadEventEmitted = false; private INuGetTelemetryCollector _nuGetTelemetryCollector; - private Func> _vsSolutionTelemetryEvents; - private Lazy> _vsInstanceTelemetryEvents; + private Func>> _vsSolutionTelemetryEvents; + private Func>> _vsInstanceTelemetryEvents; + private readonly INuGetTelemetryProvider _telemetryProvider; [ImportingConstructor] - internal PowershellTelemetryEmitter(INuGetTelemetryCollector nuGetTelemetryCollector) + internal PowershellTelemetryEmitter(INuGetTelemetryCollector nuGetTelemetryCollector, INuGetTelemetryProvider telemetryProvider) { _nuGetTelemetryCollector = nuGetTelemetryCollector; + _telemetryProvider = telemetryProvider ?? throw new ArgumentNullException(nameof(telemetryProvider)); _vsSolutionTelemetryEvents = () => _nuGetTelemetryCollector?.GetVSSolutionTelemetryEvents().ToList(); - _vsInstanceTelemetryEvents = new Lazy>(() => - { - return _nuGetTelemetryCollector?.GetVSIntanceTelemetryEvents().ToList(); - }); + _vsInstanceTelemetryEvents = () => _nuGetTelemetryCollector?.GetVSIntanceTelemetryEvents().ToList(); } public void SolutionOpenedTelemetryEmit() @@ -40,9 +39,9 @@ public void SolutionOpenedTelemetryEmit() _nuGetTelemetryCollector.ClearSolutionTelemetryEvents(); _solutionCount++; } - catch (Exception) + catch (Exception exception) { - // Currently do nothing. + _telemetryProvider.PostFault(exception, typeof(PowershellTelemetryEmitter).FullName); } } @@ -55,9 +54,9 @@ public void SolutionClosedTelemetryEmit() VSSolutionPowershellTelemetryEmit(); _nuGetTelemetryCollector.ClearSolutionTelemetryEvents(); } - catch (Exception) + catch (Exception exception) { - // Currently do nothing. + _telemetryProvider.PostFault(exception, typeof(PowershellTelemetryEmitter).FullName); } } @@ -70,9 +69,9 @@ public void VSInstanceClosedTelemetryEmit() EmitPMCUsedWithoutSolution(); VSInstancePowershellTelemetryEmit(); } - catch (Exception) + catch (Exception exception) { - // Currently do nothing. + _telemetryProvider.PostFault(exception, typeof(PowershellTelemetryEmitter).FullName); } } @@ -83,17 +82,20 @@ private void EmitPMCUsedWithoutSolution() if (!_powershellLoadEventEmitted) { - var nuGetPowerShellLoadedEvent = _vsSolutionTelemetryEvents().FirstOrDefault(e => e.Name == TelemetryConst.NuGetPowerShellLoaded); + Dictionary nuGetPowerShellLoadedEvent = _vsSolutionTelemetryEvents().FirstOrDefault(e => (string)e[TelemetryConst.Name] == TelemetryConst.NuGetPowerShellLoaded); if (nuGetPowerShellLoadedEvent != null) { - TelemetryActivity.EmitTelemetryEvent(nuGetPowerShellLoadedEvent); + TelemetryActivity.EmitTelemetryEvent( + new NuGetPowershellLoadedEvent( + loadedfrompmc: (bool)nuGetPowerShellLoadedEvent[TelemetryConst.LoadedFromPMC]) + ); _powershellLoadEventEmitted = true; } } // If there is not emitted PowerShellExecuteCommand telemetry. - if (_vsSolutionTelemetryEvents().Any(e => e is NuGetPowershellVSSolutionCloseEvent)) + if (_vsSolutionTelemetryEvents().Where(e => (string)e[TelemetryConst.Name] == TelemetryConst.NuGetVSSolutionClose).Any()) { SolutionClosedTelemetryEmit(); } @@ -101,36 +103,50 @@ private void EmitPMCUsedWithoutSolution() private void VSSolutionPowershellTelemetryEmit() { - NuGetPowershellVSSolutionCloseEvent vsSolutionPowershellTelemetry = _vsSolutionTelemetryEvents().FirstOrDefault(e => e is NuGetPowershellVSSolutionCloseEvent) as NuGetPowershellVSSolutionCloseEvent; + Dictionary vsSolutionPowershellTelemetry = _vsSolutionTelemetryEvents().Where(e => (string)e[TelemetryConst.Name] == TelemetryConst.NuGetVSSolutionClose).FirstOrDefault(); // If powershell(PMC/PMUI) is not loaded at all then we need to create default telemetry event which will be emitted. - if (vsSolutionPowershellTelemetry == null) - { - vsSolutionPowershellTelemetry = new NuGetPowershellVSSolutionCloseEvent(); - } - else + var nugetVSSolutionCloseEvent = new NuGetPowershellVSSolutionCloseEvent(); + + if (vsSolutionPowershellTelemetry != null) { // PMC opened, but no command executed nor any solution was loaded. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore. - if (!(bool)vsSolutionPowershellTelemetry[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.SolutionLoaded] && (int)vsSolutionPowershellTelemetry[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCExecuteCommandCount] == 0) + if (!(bool)vsSolutionPowershellTelemetry[TelemetryConst.SolutionLoaded] && (int)vsSolutionPowershellTelemetry[TelemetryConst.NuGetPMCExecuteCommandCount] == 0) { return; } - vsSolutionPowershellTelemetry[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCWindowLoadCount] = _vsSolutionTelemetryEvents().Where(e => e[TelemetryConst.NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPMCWindowLoadCount]); + nugetVSSolutionCloseEvent = new NuGetPowershellVSSolutionCloseEvent( + FirstTimeLoadedFromPMC: (bool)vsSolutionPowershellTelemetry[TelemetryConst.FirstTimeLoadedFromPMC], + FirstTimeLoadedFromPMUI: (bool)vsSolutionPowershellTelemetry[TelemetryConst.FirstTimeLoadedFromPMUI], + InitPs1LoadedFromPMCFirst: (bool)vsSolutionPowershellTelemetry[TelemetryConst.InitPs1LoadedFromPMCFirst], + InitPs1LoadPMC: (bool)vsSolutionPowershellTelemetry[TelemetryConst.InitPs1LoadPMC], + InitPs1LoadPMUI: (bool)vsSolutionPowershellTelemetry[TelemetryConst.InitPs1LoadPMUI], + LoadedFromPMC: (bool)vsSolutionPowershellTelemetry[TelemetryConst.LoadedFromPMC], + LoadedFromPMUI: (bool)vsSolutionPowershellTelemetry[TelemetryConst.LoadedFromPMUI], + NuGetCommandUsed: (bool)vsSolutionPowershellTelemetry[TelemetryConst.NuGetCommandUsed], + NuGetPMCExecuteCommandCount: (int)vsSolutionPowershellTelemetry[TelemetryConst.NuGetPMCExecuteCommandCount], + NuGetPMCWindowLoadCount: _vsSolutionTelemetryEvents().Where(e => e[TelemetryConst.NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPMCWindowLoadCount]), + NuGetPMUIExecuteCommandCount: (int)vsSolutionPowershellTelemetry[TelemetryConst.NuGetPMUIExecuteCommandCount], + SolutionLoaded: (bool)vsSolutionPowershellTelemetry[TelemetryConst.SolutionLoaded] + ); } - TelemetryActivity.EmitTelemetryEvent(vsSolutionPowershellTelemetry); + TelemetryActivity.EmitTelemetryEvent(nugetVSSolutionCloseEvent); } private void VSInstancePowershellTelemetryEmit() { + List> nuGetVSSolutionCloseEvents = _vsInstanceTelemetryEvents().Where(e => (string)e[TelemetryConst.Name] == TelemetryConst.NuGetVSSolutionClose).ToList(); + List> packageManagerConsoleWindowsLoadEvents = _vsInstanceTelemetryEvents().Where(e => (string)e[TelemetryConst.Name] == TelemetryConst.PackageManagerConsoleWindowsLoad).ToList(); + NuGetPowershellVSInstanceCloseEvent nuGetPowershellVSInstanceCloseEvent = new NuGetPowershellVSInstanceCloseEvent( - nugetpmcexecutecommandcount: _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCExecuteCommandCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCExecuteCommandCount]), - nugetpmcwindowloadcount: _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPMCWindowLoadCount]), - nugetpmuiexecutecommandcount: _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMUIExecuteCommandCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMUIExecuteCommandCount]), - pmcpowershellloadedsolutioncount: _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.SolutionLoaded] is bool && (bool)e[TelemetryConst.SolutionLoaded] && e[TelemetryConst.LoadedFromPMC] is bool).Count(e => (bool)e[TelemetryConst.LoadedFromPMC] == true), - pmuipowershellloadedsolutioncount: _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.LoadedFromPMUI] is bool).Count(e => (bool)e[TelemetryConst.LoadedFromPMUI] == true), - reopenatstart: _vsInstanceTelemetryEvents.Value.Where(e => e[TelemetryConst.ReOpenAtStart] != null).Any(), + nugetpmcexecutecommandcount: nuGetVSSolutionCloseEvents.Sum(e => (int)e[TelemetryConst.NuGetPMCExecuteCommandCount]), + nugetpmcwindowloadcount: packageManagerConsoleWindowsLoadEvents.Sum(e => (int)e[TelemetryConst.NuGetPMCWindowLoadCount]), + nugetpmuiexecutecommandcount: nuGetVSSolutionCloseEvents.Sum(e => (int)e[TelemetryConst.NuGetPMUIExecuteCommandCount]), + pmcpowershellloadedsolutioncount: nuGetVSSolutionCloseEvents.Count(e => (bool)e[TelemetryConst.LoadedFromPMC] == true), + pmuipowershellloadedsolutioncount: nuGetVSSolutionCloseEvents.Count(e => (bool)e[TelemetryConst.LoadedFromPMUI] == true), + reopenatstart: packageManagerConsoleWindowsLoadEvents.Any(e => e[TelemetryConst.ReOpenAtStart] != null), solutioncount: _solutionCount ); From 87b7d9a51fe615d1a0e795c8112911bbf247e2a0 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Tue, 19 Jan 2021 17:12:37 -0800 Subject: [PATCH 44/59] Improve reaadability --- .../Xamls/ConsoleContainer.xaml.cs | 8 +-- .../Powershell/PowershellTelemetryEmitter.cs | 61 +++++++++++++------ 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index 31b84b848f0..2724ed8e2df 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -28,7 +28,7 @@ public sealed partial class ConsoleContainer : UserControl, IDisposable { private INuGetSolutionManagerService _solutionManager; private IVsSolutionManager _iVsSolutionManager; - private INuGetTelemetryCollector _nugetSolutionTelemetry; + private INuGetTelemetryCollector _nugeTelemetryCollector; private int _windowLoadCount; private bool _isTelemetryEmitted; @@ -65,7 +65,7 @@ await System.Threading.Tasks.Task.Run( var packageRestoreManager = ServiceLocator.GetInstance(); var deleteOnRestartManager = ServiceLocator.GetInstance(); var shell = ServiceLocator.GetGlobalService(); - _nugetSolutionTelemetry = ServiceLocator.GetInstance(); + _nugeTelemetryCollector = ServiceLocator.GetInstance(); await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -106,7 +106,7 @@ public void Dispose() { PowershellTelemetryConsts.ReOpenAtStart, reopenAtStart} }; - _nugetSolutionTelemetry?.AddSolutionTelemetryEvent(telemetryEvent); + _nugeTelemetryCollector?.AddSolutionTelemetryEvent(telemetryEvent); _isTelemetryEmitted = true; } @@ -134,7 +134,7 @@ private void EmitPowershellUsageTelemetry() { PowershellTelemetryConsts.Name, PowershellTelemetryConsts.PackageManagerConsoleWindowsLoad}, { PowershellTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, }; - _nugetSolutionTelemetry?.AddSolutionTelemetryEvent(telemetryEvent); + _nugeTelemetryCollector?.AddSolutionTelemetryEvent(telemetryEvent); _windowLoadCount = 0; } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs index 4eea916a94c..6f42b6046ce 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs @@ -82,7 +82,8 @@ private void EmitPMCUsedWithoutSolution() if (!_powershellLoadEventEmitted) { - Dictionary nuGetPowerShellLoadedEvent = _vsSolutionTelemetryEvents().FirstOrDefault(e => (string)e[TelemetryConst.Name] == TelemetryConst.NuGetPowerShellLoaded); + Dictionary nuGetPowerShellLoadedEvent = _vsSolutionTelemetryEvents() + .FirstOrDefault(e => (string)e[TelemetryConst.Name] == TelemetryConst.NuGetPowerShellLoaded); if (nuGetPowerShellLoadedEvent != null) { @@ -94,8 +95,12 @@ private void EmitPMCUsedWithoutSolution() } } - // If there is not emitted PowerShellExecuteCommand telemetry. - if (_vsSolutionTelemetryEvents().Where(e => (string)e[TelemetryConst.Name] == TelemetryConst.NuGetVSSolutionClose).Any()) + bool anyNotEmittedVSSolutionCloseTelemetry = _vsSolutionTelemetryEvents() + .Any(e => + e.ContainsKey(TelemetryConst.Name) + && (string)e[TelemetryConst.Name] == TelemetryConst.NuGetVSSolutionClose); + + if (anyNotEmittedVSSolutionCloseTelemetry) { SolutionClosedTelemetryEmit(); } @@ -103,32 +108,40 @@ private void EmitPMCUsedWithoutSolution() private void VSSolutionPowershellTelemetryEmit() { - Dictionary vsSolutionPowershellTelemetry = _vsSolutionTelemetryEvents().Where(e => (string)e[TelemetryConst.Name] == TelemetryConst.NuGetVSSolutionClose).FirstOrDefault(); + Dictionary vsSolutionCloseTelemetry = _vsSolutionTelemetryEvents() + .Where(e => + e.ContainsKey(TelemetryConst.Name) + && (string)e[TelemetryConst.Name] == TelemetryConst.NuGetVSSolutionClose) + .FirstOrDefault(); // If powershell(PMC/PMUI) is not loaded at all then we need to create default telemetry event which will be emitted. var nugetVSSolutionCloseEvent = new NuGetPowershellVSSolutionCloseEvent(); - if (vsSolutionPowershellTelemetry != null) + if (vsSolutionCloseTelemetry != null) { // PMC opened, but no command executed nor any solution was loaded. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore. - if (!(bool)vsSolutionPowershellTelemetry[TelemetryConst.SolutionLoaded] && (int)vsSolutionPowershellTelemetry[TelemetryConst.NuGetPMCExecuteCommandCount] == 0) + if (!(bool)vsSolutionCloseTelemetry[TelemetryConst.SolutionLoaded] && (int)vsSolutionCloseTelemetry[TelemetryConst.NuGetPMCExecuteCommandCount] == 0) { return; } nugetVSSolutionCloseEvent = new NuGetPowershellVSSolutionCloseEvent( - FirstTimeLoadedFromPMC: (bool)vsSolutionPowershellTelemetry[TelemetryConst.FirstTimeLoadedFromPMC], - FirstTimeLoadedFromPMUI: (bool)vsSolutionPowershellTelemetry[TelemetryConst.FirstTimeLoadedFromPMUI], - InitPs1LoadedFromPMCFirst: (bool)vsSolutionPowershellTelemetry[TelemetryConst.InitPs1LoadedFromPMCFirst], - InitPs1LoadPMC: (bool)vsSolutionPowershellTelemetry[TelemetryConst.InitPs1LoadPMC], - InitPs1LoadPMUI: (bool)vsSolutionPowershellTelemetry[TelemetryConst.InitPs1LoadPMUI], - LoadedFromPMC: (bool)vsSolutionPowershellTelemetry[TelemetryConst.LoadedFromPMC], - LoadedFromPMUI: (bool)vsSolutionPowershellTelemetry[TelemetryConst.LoadedFromPMUI], - NuGetCommandUsed: (bool)vsSolutionPowershellTelemetry[TelemetryConst.NuGetCommandUsed], - NuGetPMCExecuteCommandCount: (int)vsSolutionPowershellTelemetry[TelemetryConst.NuGetPMCExecuteCommandCount], - NuGetPMCWindowLoadCount: _vsSolutionTelemetryEvents().Where(e => e[TelemetryConst.NuGetPMCWindowLoadCount] is int).Sum(e => (int)e[TelemetryConst.NuGetPMCWindowLoadCount]), - NuGetPMUIExecuteCommandCount: (int)vsSolutionPowershellTelemetry[TelemetryConst.NuGetPMUIExecuteCommandCount], - SolutionLoaded: (bool)vsSolutionPowershellTelemetry[TelemetryConst.SolutionLoaded] + FirstTimeLoadedFromPMC: (bool)vsSolutionCloseTelemetry[TelemetryConst.FirstTimeLoadedFromPMC], + FirstTimeLoadedFromPMUI: (bool)vsSolutionCloseTelemetry[TelemetryConst.FirstTimeLoadedFromPMUI], + InitPs1LoadedFromPMCFirst: (bool)vsSolutionCloseTelemetry[TelemetryConst.InitPs1LoadedFromPMCFirst], + InitPs1LoadPMC: (bool)vsSolutionCloseTelemetry[TelemetryConst.InitPs1LoadPMC], + InitPs1LoadPMUI: (bool)vsSolutionCloseTelemetry[TelemetryConst.InitPs1LoadPMUI], + LoadedFromPMC: (bool)vsSolutionCloseTelemetry[TelemetryConst.LoadedFromPMC], + LoadedFromPMUI: (bool)vsSolutionCloseTelemetry[TelemetryConst.LoadedFromPMUI], + NuGetCommandUsed: (bool)vsSolutionCloseTelemetry[TelemetryConst.NuGetCommandUsed], + NuGetPMCExecuteCommandCount: (int)vsSolutionCloseTelemetry[TelemetryConst.NuGetPMCExecuteCommandCount], + NuGetPMCWindowLoadCount: _vsSolutionTelemetryEvents() + .Where(e => + e.ContainsKey(TelemetryConst.Name) + && (string)e[TelemetryConst.Name] == TelemetryConst.PackageManagerConsoleWindowsLoad) + .Sum(e => (int)e[TelemetryConst.NuGetPMCWindowLoadCount]), + NuGetPMUIExecuteCommandCount: (int)vsSolutionCloseTelemetry[TelemetryConst.NuGetPMUIExecuteCommandCount], + SolutionLoaded: (bool)vsSolutionCloseTelemetry[TelemetryConst.SolutionLoaded] ); } @@ -137,8 +150,16 @@ private void VSSolutionPowershellTelemetryEmit() private void VSInstancePowershellTelemetryEmit() { - List> nuGetVSSolutionCloseEvents = _vsInstanceTelemetryEvents().Where(e => (string)e[TelemetryConst.Name] == TelemetryConst.NuGetVSSolutionClose).ToList(); - List> packageManagerConsoleWindowsLoadEvents = _vsInstanceTelemetryEvents().Where(e => (string)e[TelemetryConst.Name] == TelemetryConst.PackageManagerConsoleWindowsLoad).ToList(); + List> nuGetVSSolutionCloseEvents = _vsInstanceTelemetryEvents() + .Where(e => + e.ContainsKey(TelemetryConst.Name) + && (string)e[TelemetryConst.Name] == TelemetryConst.NuGetVSSolutionClose) + .ToList(); + List> packageManagerConsoleWindowsLoadEvents = _vsInstanceTelemetryEvents() + .Where(e => + e.ContainsKey(TelemetryConst.Name) + && (string)e[TelemetryConst.Name] == TelemetryConst.PackageManagerConsoleWindowsLoad) + .ToList(); NuGetPowershellVSInstanceCloseEvent nuGetPowershellVSInstanceCloseEvent = new NuGetPowershellVSInstanceCloseEvent( nugetpmcexecutecommandcount: nuGetVSSolutionCloseEvents.Sum(e => (int)e[TelemetryConst.NuGetPMCExecuteCommandCount]), From 0c4f737b8ec04615e1f2b5f68a2b1dd55a1562fc Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Tue, 19 Jan 2021 20:27:22 -0800 Subject: [PATCH 45/59] Move no any solution is loaded and not executed any command logic to PowerShellHostTelemetryProcessor.cs --- .../PowerShellHostTelemetryProcessor.cs | 38 ++++++++++--------- .../Powershell/PowershellTelemetryEmitter.cs | 10 +---- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs index 7c5c8246306..5ba3ee4231e 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs @@ -140,24 +140,28 @@ public void EmitPowershellUsageTelemetry(bool withSolution) { if (!_isTelemetryEmitted) { - var nugetVSSolutionCloseEvent = new Dictionary + // PMC opened, but no command executed nor any solution was loaded. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore. + if (withSolution || _pmcExecutedCount > 0) { - { TelemetryConst.Name , TelemetryConst.NuGetVSSolutionClose }, - { TelemetryConst.FirstTimeLoadedFromPMC , TestAllBitsSet(PowerShellHostInstances, 0b00000100) }, - { TelemetryConst.FirstTimeLoadedFromPMUI, TestAllBitsSet(PowerShellHostInstances, 0b00001000) }, - { TelemetryConst.InitPs1LoadedFromPMCFirst, TestAllBitsSet(PowerShellHostInstances, 0b00010000) }, - { TelemetryConst.InitPs1LoadPMC, TestAllBitsSet(PowerShellHostInstances, 0b00100000) }, - { TelemetryConst.InitPs1LoadPMUI, TestAllBitsSet(PowerShellHostInstances, 0b01000000) }, - { TelemetryConst.LoadedFromPMC, TestAllBitsSet(PowerShellHostInstances, 0b00000001) }, - { TelemetryConst.LoadedFromPMUI, TestAllBitsSet(PowerShellHostInstances, 0b00000010) }, - { TelemetryConst.NuGetCommandUsed, TestAllBitsSet(PowerShellHostInstances, 0b10000000) }, - { TelemetryConst.NuGetPMCExecuteCommandCount, _pmcExecutedCount }, - { TelemetryConst.NuGetPMCWindowLoadCount, 0 }, - { TelemetryConst.NuGetPMUIExecuteCommandCount, _nonPmcExecutedCount }, - { TelemetryConst.SolutionLoaded, withSolution } - }; - - _nuGetTelemetryCollector?.AddSolutionTelemetryEvent(nugetVSSolutionCloseEvent); + var nugetVSSolutionCloseEvent = new Dictionary + { + { TelemetryConst.Name , TelemetryConst.NuGetVSSolutionClose }, + { TelemetryConst.FirstTimeLoadedFromPMC , TestAllBitsSet(PowerShellHostInstances, 0b00000100) }, + { TelemetryConst.FirstTimeLoadedFromPMUI, TestAllBitsSet(PowerShellHostInstances, 0b00001000) }, + { TelemetryConst.InitPs1LoadedFromPMCFirst, TestAllBitsSet(PowerShellHostInstances, 0b00010000) }, + { TelemetryConst.InitPs1LoadPMC, TestAllBitsSet(PowerShellHostInstances, 0b00100000) }, + { TelemetryConst.InitPs1LoadPMUI, TestAllBitsSet(PowerShellHostInstances, 0b01000000) }, + { TelemetryConst.LoadedFromPMC, TestAllBitsSet(PowerShellHostInstances, 0b00000001) }, + { TelemetryConst.LoadedFromPMUI, TestAllBitsSet(PowerShellHostInstances, 0b00000010) }, + { TelemetryConst.NuGetCommandUsed, TestAllBitsSet(PowerShellHostInstances, 0b10000000) }, + { TelemetryConst.NuGetPMCExecuteCommandCount, _pmcExecutedCount }, + { TelemetryConst.NuGetPMCWindowLoadCount, 0 }, + { TelemetryConst.NuGetPMUIExecuteCommandCount, _nonPmcExecutedCount }, + { TelemetryConst.SolutionLoaded, withSolution } + }; + + _nuGetTelemetryCollector?.AddSolutionTelemetryEvent(nugetVSSolutionCloseEvent); + } _pmcExecutedCount = 0; _nonPmcExecutedCount = 0; diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs index 6f42b6046ce..e38b5543d07 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs @@ -119,12 +119,6 @@ private void VSSolutionPowershellTelemetryEmit() if (vsSolutionCloseTelemetry != null) { - // PMC opened, but no command executed nor any solution was loaded. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore. - if (!(bool)vsSolutionCloseTelemetry[TelemetryConst.SolutionLoaded] && (int)vsSolutionCloseTelemetry[TelemetryConst.NuGetPMCExecuteCommandCount] == 0) - { - return; - } - nugetVSSolutionCloseEvent = new NuGetPowershellVSSolutionCloseEvent( FirstTimeLoadedFromPMC: (bool)vsSolutionCloseTelemetry[TelemetryConst.FirstTimeLoadedFromPMC], FirstTimeLoadedFromPMUI: (bool)vsSolutionCloseTelemetry[TelemetryConst.FirstTimeLoadedFromPMUI], @@ -165,8 +159,8 @@ private void VSInstancePowershellTelemetryEmit() nugetpmcexecutecommandcount: nuGetVSSolutionCloseEvents.Sum(e => (int)e[TelemetryConst.NuGetPMCExecuteCommandCount]), nugetpmcwindowloadcount: packageManagerConsoleWindowsLoadEvents.Sum(e => (int)e[TelemetryConst.NuGetPMCWindowLoadCount]), nugetpmuiexecutecommandcount: nuGetVSSolutionCloseEvents.Sum(e => (int)e[TelemetryConst.NuGetPMUIExecuteCommandCount]), - pmcpowershellloadedsolutioncount: nuGetVSSolutionCloseEvents.Count(e => (bool)e[TelemetryConst.LoadedFromPMC] == true), - pmuipowershellloadedsolutioncount: nuGetVSSolutionCloseEvents.Count(e => (bool)e[TelemetryConst.LoadedFromPMUI] == true), + pmcpowershellloadedsolutioncount: nuGetVSSolutionCloseEvents.Count(e => (bool)e[TelemetryConst.SolutionLoaded] && (bool)e[TelemetryConst.LoadedFromPMC]), + pmuipowershellloadedsolutioncount: nuGetVSSolutionCloseEvents.Count(e => (bool)e[TelemetryConst.SolutionLoaded] && (bool)e[TelemetryConst.LoadedFromPMUI] == true), reopenatstart: packageManagerConsoleWindowsLoadEvents.Any(e => e[TelemetryConst.ReOpenAtStart] != null), solutioncount: _solutionCount ); From 4f20b1888da6afdb51a273b15b7e762846c714f5 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Tue, 19 Jan 2021 20:45:40 -0800 Subject: [PATCH 46/59] Correct prefix . --- .../Telemetry/Powershell/PowershellTelemetryConsts.cs | 2 +- .../Telemetry/Powershell/PowershellTelemetryEmitter.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs index f741224116a..a3090d3a932 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs @@ -27,7 +27,7 @@ public sealed class PowershellTelemetryConsts // Const name for emitting when VS solution close or VS instance close. public const string Name = nameof(Name); - public const string NuGetPowershellPrefix = nameof(NuGetPowershellPrefix); // Using prefix prevent accidental same name property collission from different type telemetry. + public const string NuGetPowershellPrefix = "NuGetPowershellPrefix."; // Using prefix prevent accidental same name property collission from different type telemetry. public const string NuGetVSSolutionClose = nameof(NuGetVSSolutionClose); public const string NuGetVSInstanceClose = nameof(NuGetVSInstanceClose); public const string SolutionCount = nameof(SolutionCount); diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs index e38b5543d07..66d5811f8bc 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs @@ -160,7 +160,7 @@ private void VSInstancePowershellTelemetryEmit() nugetpmcwindowloadcount: packageManagerConsoleWindowsLoadEvents.Sum(e => (int)e[TelemetryConst.NuGetPMCWindowLoadCount]), nugetpmuiexecutecommandcount: nuGetVSSolutionCloseEvents.Sum(e => (int)e[TelemetryConst.NuGetPMUIExecuteCommandCount]), pmcpowershellloadedsolutioncount: nuGetVSSolutionCloseEvents.Count(e => (bool)e[TelemetryConst.SolutionLoaded] && (bool)e[TelemetryConst.LoadedFromPMC]), - pmuipowershellloadedsolutioncount: nuGetVSSolutionCloseEvents.Count(e => (bool)e[TelemetryConst.SolutionLoaded] && (bool)e[TelemetryConst.LoadedFromPMUI] == true), + pmuipowershellloadedsolutioncount: nuGetVSSolutionCloseEvents.Count(e => (bool)e[TelemetryConst.SolutionLoaded] && (bool)e[TelemetryConst.LoadedFromPMUI]), reopenatstart: packageManagerConsoleWindowsLoadEvents.Any(e => e[TelemetryConst.ReOpenAtStart] != null), solutioncount: _solutionCount ); From f4a8a122113800ba83861ccdbbe9ca548679a27a Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Wed, 20 Jan 2021 16:10:33 -0800 Subject: [PATCH 47/59] Refactor whole design after consulting with Andy. --- .../NuGet.Console/WpfConsole/WpfConsole.cs | 6 - .../Xamls/ConsoleContainer.xaml.cs | 46 +- .../IDE/VSSolutionManager.cs | 28 +- src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs | 16 + .../Telemetry/INuGetTelemetryCollector.cs | 16 - .../NuGetPowerShellUsageCollector.cs | 419 ++++++++++++++++++ .../Telemetry/NuGetTelemetryCollector.cs | 47 -- .../Powershell/NuGetPowerShellUsage.cs | 64 +++ .../Powershell/NuGetPowershellLoadedEvent.cs | 17 - .../NuGetPowershellVSInstanceCloseEvent.cs | 29 -- .../NuGetPowershellVSSolutionCloseEvent.cs | 54 --- .../PowerShellHostTelemetryProcessor.cs | 243 ---------- .../Powershell/PowershellTelemetryConsts.cs | 37 -- .../Powershell/PowershellTelemetryEmitter.cs | 171 ------- .../PowerShellHost.cs | 29 +- 15 files changed, 515 insertions(+), 707 deletions(-) delete mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs create mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs delete mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs create mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs delete mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellLoadedEvent.cs delete mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSInstanceCloseEvent.cs delete mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs delete mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs delete mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs delete mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs diff --git a/src/NuGet.Clients/NuGet.Console/WpfConsole/WpfConsole.cs b/src/NuGet.Clients/NuGet.Console/WpfConsole/WpfConsole.cs index 054475a32ee..2761628bc2c 100644 --- a/src/NuGet.Clients/NuGet.Console/WpfConsole/WpfConsole.cs +++ b/src/NuGet.Clients/NuGet.Console/WpfConsole/WpfConsole.cs @@ -751,12 +751,6 @@ protected virtual void Dispose(bool disposing) { disposable.Dispose(); } - - var host = Host as IDisposable; - if (host != null) - { - host.Dispose(); - } } } diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index 2724ed8e2df..a558b6fc9d4 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.Threading; using System.Windows; using System.Windows.Controls; @@ -10,14 +9,13 @@ using Microsoft.ServiceHub.Framework; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -using NuGet.Common; using NuGet.PackageManagement; using NuGet.PackageManagement.UI; -using NuGet.PackageManagement.VisualStudio; using NuGet.VisualStudio; using NuGet.VisualStudio.Common; using NuGet.VisualStudio.Internal.Contracts; using NuGet.VisualStudio.Telemetry; +using NuGet.VisualStudio.Telemetry.Powershell; namespace NuGetConsole { @@ -27,14 +25,15 @@ namespace NuGetConsole public sealed partial class ConsoleContainer : UserControl, IDisposable { private INuGetSolutionManagerService _solutionManager; - private IVsSolutionManager _iVsSolutionManager; - private INuGetTelemetryCollector _nugeTelemetryCollector; - private int _windowLoadCount; private bool _isTelemetryEmitted; public ConsoleContainer() { InitializeComponent(); + + NuGetPowerShellUsageCollector _nuGetPowerShellUsageCollector = ServiceLocator.GetInstance(); + Assumes.NotNull(_nuGetPowerShellUsageCollector); + Loaded += ConsoleContainer_Loaded; ThreadHelper.JoinableTaskFactory.StartOnIdle( @@ -52,20 +51,10 @@ await System.Threading.Tasks.Task.Run( Assumes.NotNull(_solutionManager); - _iVsSolutionManager = await ServiceLocator.GetInstanceAsync(); - Assumes.NotNull(_iVsSolutionManager); - - // Hook up solution events - _iVsSolutionManager.SolutionClosing += (o, e) => - { - EmitPowershellUsageTelemetry(); - }; - var productUpdateService = ServiceLocator.GetInstance(); var packageRestoreManager = ServiceLocator.GetInstance(); var deleteOnRestartManager = ServiceLocator.GetInstance(); var shell = ServiceLocator.GetGlobalService(); - _nugeTelemetryCollector = ServiceLocator.GetInstance(); await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -98,15 +87,8 @@ public void Dispose() if (!_isTelemetryEmitted) { // Work around to detect if PMC loaded automatically because it was last focused window. - var reopenAtStart = IsLoaded; - var telemetryEvent = new Dictionary - { - { PowershellTelemetryConsts.Name, PowershellTelemetryConsts.PackageManagerConsoleWindowsLoad}, - { PowershellTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, - { PowershellTelemetryConsts.ReOpenAtStart, reopenAtStart} - }; - - _nugeTelemetryCollector?.AddSolutionTelemetryEvent(telemetryEvent); + bool? reopenAtStart = IsLoaded; + NuGetPowerShellUsage.RaisePMCWindowsEvent(reopenAtStart); _isTelemetryEmitted = true; } @@ -124,19 +106,7 @@ public void Dispose() void ConsoleContainer_Loaded(object sender, RoutedEventArgs e) { - _windowLoadCount++; - } - - private void EmitPowershellUsageTelemetry() - { - var telemetryEvent = new Dictionary - { - { PowershellTelemetryConsts.Name, PowershellTelemetryConsts.PackageManagerConsoleWindowsLoad}, - { PowershellTelemetryConsts.NuGetPMCWindowLoadCount, _windowLoadCount}, - }; - _nugeTelemetryCollector?.AddSolutionTelemetryEvent(telemetryEvent); - - _windowLoadCount = 0; + NuGetPowerShellUsage.RaisePMCWindowsEvent(null); } } } diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs index bbfe2c87f64..b9d94e4d944 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs @@ -26,7 +26,6 @@ using NuGet.Protocol; using NuGet.Protocol.Core.Types; using NuGet.VisualStudio; -using NuGet.VisualStudio.Telemetry.Powershell; using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; using Task = System.Threading.Tasks.Task; @@ -35,7 +34,7 @@ namespace NuGet.PackageManagement.VisualStudio [Export(typeof(ISolutionManager))] [Export(typeof(IVsSolutionManager))] [PartCreationPolicy(CreationPolicy.Shared)] - public sealed class VSSolutionManager : IVsSolutionManager, IVsSelectionEvents, IDisposable + public sealed class VSSolutionManager : IVsSolutionManager, IVsSelectionEvents { private static readonly INuGetProjectContext EmptyNuGetProjectContext = new EmptyNuGetProjectContext(); private static readonly string VSNuGetClientName = "NuGet VS VSIX"; @@ -56,7 +55,6 @@ public sealed class VSSolutionManager : IVsSolutionManager, IVsSelectionEvents, private readonly IVsProjectAdapterProvider _vsProjectAdapterProvider; private readonly Common.ILogger _logger; private readonly Lazy _settings; - private PowershellTelemetryEmitter _vsIntanceTelemetryEmitter; private bool _initialized; private bool _cacheInitialized; @@ -128,7 +126,6 @@ internal VSSolutionManager( [Import("VisualStudioActivityLogger")] Common.ILogger logger, Lazy settings, - PowershellTelemetryEmitter vsIntanceTelemetryEmitter, JoinableTaskContext joinableTaskContext) : this(AsyncServiceProvider.GlobalProvider, projectSystemCache, @@ -137,7 +134,6 @@ internal VSSolutionManager( vsProjectAdapterProvider, logger, settings, - vsIntanceTelemetryEmitter, joinableTaskContext) { } @@ -150,7 +146,6 @@ internal VSSolutionManager( IVsProjectAdapterProvider vsProjectAdapterProvider, ILogger logger, Lazy settings, - PowershellTelemetryEmitter vsIntanceTelemetryEmitter, JoinableTaskContext joinableTaskContext) { Assumes.Present(asyncServiceProvider); @@ -169,11 +164,7 @@ internal VSSolutionManager( _vsProjectAdapterProvider = vsProjectAdapterProvider; _logger = logger; _settings = settings; - _vsIntanceTelemetryEmitter = vsIntanceTelemetryEmitter; _initLock = new NuGetLockService(joinableTaskContext); - - SolutionOpened += OnSolutionOpened; - SolutionClosed += OnSolutionClosed; } private async Task InitializeAsync() @@ -1036,23 +1027,6 @@ public async Task UpgradeProjectToPackageReferenceAsync(NuGetProje return nuGetProject; } - private void OnSolutionOpened(object sender, EventArgs e) - { - _vsIntanceTelemetryEmitter.SolutionOpenedTelemetryEmit(); - } - - private void OnSolutionClosed(object sender, EventArgs e) - { - _vsIntanceTelemetryEmitter.SolutionClosedTelemetryEmit(); - } - - public void Dispose() - { - SolutionOpened -= OnSolutionOpened; - SolutionClosed -= OnSolutionClosed; - _vsIntanceTelemetryEmitter.VSInstanceClosedTelemetryEmit(); - } - #endregion } } diff --git a/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs b/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs index c85519b6d86..6be7cb2ef01 100644 --- a/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs +++ b/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs @@ -28,6 +28,7 @@ using NuGet.VisualStudio.Common; using NuGet.VisualStudio.Internal.Contracts; using NuGet.VisualStudio.Telemetry; +using NuGet.VisualStudio.Telemetry.Powershell; using NuGetConsole; using NuGetConsole.Implementation; using ContractsNuGetServices = NuGet.VisualStudio.Contracts.NuGetServices; @@ -139,6 +140,9 @@ public NuGetPackage() [Import] private Lazy ServiceBrokerProvider { get; set; } + [Import] + private Lazy NuGetPowerShellUsageCollector { get; set; } + /// /// Initialization of the package; this method is called right after the package is sited, so this is the place /// where you can put all the initialization code that rely on services provided by VisualStudio. @@ -179,6 +183,8 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke ThreadHelper.JoinableTaskFactory); await NuGetBrokeredServiceFactory.ProfferServicesAsync(this); + + VsShellUtilities.ShutdownToken.Register(RegisterEmitVSInstancePowerShellTelemetry); } /// @@ -209,12 +215,17 @@ private async Task InitializeMEFAsync() SolutionManager.Value.NuGetProjectContext = ProjectContext.Value; } + NuGetPowerShellUsageCollector nuGetPowerShellUsageCollectorInstance = NuGetPowerShellUsageCollector.Value; + // when NuGet loads, if the current solution has some package // folders marked for deletion (because a previous uninstalltion didn't succeed), // delete them now. if (await SolutionManager.Value.IsSolutionOpenAsync()) { await DeleteOnRestartManager.Value.DeleteMarkedPackageDirectoriesAsync(ProjectContext.Value); + + // Hook up solution events, check if PMC is used before without any solution. + NuGetPowerShellUsage.RaiseSolutionOpenEvent(); } IVsTrackProjectRetargeting vsTrackProjectRetargeting = await this.GetServiceAsync(); @@ -1247,6 +1258,11 @@ private void OnBeginShutDown() _dteEvents = null; } + private void RegisterEmitVSInstancePowerShellTelemetry() + { + NuGetPowerShellUsage.RaiseVSInstanceCloseEvent(); + } + #region IVsPersistSolutionOpts // Called by the shell when a solution is opened and the SUO file is read. diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs deleted file mode 100644 index b004fca8095..00000000000 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/INuGetTelemetryCollector.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; - -namespace NuGet.VisualStudio.Telemetry -{ - // Collect telemetry for aggregated telemetry emitting later at VS solution/instance close. - public interface INuGetTelemetryCollector - { - void AddSolutionTelemetryEvent(Dictionary telemetryData); - IReadOnlyList> GetVSSolutionTelemetryEvents(); - IReadOnlyList> GetVSIntanceTelemetryEvents(); - void ClearSolutionTelemetryEvents(); - } -} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs new file mode 100644 index 00000000000..3a4978834dd --- /dev/null +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs @@ -0,0 +1,419 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using NuGet.Common; +using NuGet.VisualStudio.Telemetry.Powershell; + +namespace NuGet.VisualStudio.Telemetry +{ + [Export(typeof(NuGetPowerShellUsageCollector))] + [PartCreationPolicy(CreationPolicy.Shared)] + public sealed class NuGetPowerShellUsageCollector : IDisposable + { + // PMC, PMUI powershell telemetry consts + public const string NuGetPMCExecuteCommandCount = nameof(NuGetPMCExecuteCommandCount); + public const string NuGetPMUIExecuteCommandCount = nameof(NuGetPMUIExecuteCommandCount); + public const string NuGetCommandUsed = nameof(NuGetCommandUsed); + public const string InitPs1LoadPMUI = nameof(InitPs1LoadPMUI); + public const string InitPs1LoadPMC = nameof(InitPs1LoadPMC); + public const string InitPs1LoadedFromPMCFirst = nameof(InitPs1LoadedFromPMCFirst); + public const string LoadedFromPMUI = nameof(LoadedFromPMUI); + public const string FirstTimeLoadedFromPMUI = nameof(FirstTimeLoadedFromPMUI); + public const string LoadedFromPMC = nameof(LoadedFromPMC); + public const string FirstTimeLoadedFromPMC = nameof(FirstTimeLoadedFromPMC); + public const string SolutionLoaded = nameof(SolutionLoaded); + public const string PowerShellExecuteCommand = nameof(PowerShellExecuteCommand); + public const string NuGetPowerShellLoaded = nameof(NuGetPowerShellLoaded); + + // PMC UI Console Container telemetry consts + public const string PackageManagerConsoleWindowsLoad = nameof(PackageManagerConsoleWindowsLoad); + public const string NuGetPMCWindowLoadCount = nameof(NuGetPMCWindowLoadCount); + public const string ReOpenAtStart = nameof(ReOpenAtStart); + + // Const name for emitting when VS solution close or VS instance close. + public const string Name = nameof(Name); + public const string NuGetPowershellPrefix = "NuGetPowershellPrefix."; // Using prefix prevent accidental same name property collission from different type telemetry. + public const string NuGetVSSolutionClose = nameof(NuGetVSSolutionClose); + public const string NuGetVSInstanceClose = nameof(NuGetVSInstanceClose); + public const string SolutionCount = nameof(SolutionCount); + public const string PMCPowerShellLoadedSolutionCount = nameof(PMCPowerShellLoadedSolutionCount); + public const string PMUIPowerShellLoadedSolutionCount = nameof(PMUIPowerShellLoadedSolutionCount); + + private int _solutionCount; + // _vsSolutionData hold telemetry data for current VS solution session. + private NuGetPowershellVSSolutionCloseEvent _vsSolutionData; + // _vsInstanceData hold telemetry for current VS instance session. + private readonly NuGetPowershellVSInstanceCloseEvent _vsInstanceData; + private TelemetryEvent _powerShellLoadEvent; + private object _lock = new object(); + + public NuGetPowerShellUsageCollector() + { + _vsSolutionData = new NuGetPowershellVSSolutionCloseEvent(); + _vsInstanceData = new NuGetPowershellVSInstanceCloseEvent(); + + NuGetPowerShellUsage.PowerShellLoadEvent += NuGetPowerShellUsage_PMCLoadEventHandler; + NuGetPowerShellUsage.PowerShellCommandExecuteEvent += NuGetPowerShellUsage_PowerShellCommandExecuteEvent; + NuGetPowerShellUsage.InitPs1LoadEvent += NuGetPowerShellUsage_InitPs1LoadEvent; + NuGetPowerShellUsage.PMCWindowsEvent += NuGetPowerShellUsage_PMCWindowsEventHandler; + NuGetPowerShellUsage.SolutionOpenEvent += NuGetPowerShellUsage_SolutionOpenHandler; + NuGetPowerShellUsage.SolutionCloseEvent += NuGetPowerShellUsage_SolutionCloseHandler; + NuGetPowerShellUsage.VSInstanceCloseEvent += NuGetPowerShellUsage_VSInstanseCloseHandler; + } + + private void NuGetPowerShellUsage_PMCLoadEventHandler(bool isPMC) + { + AddPowerShellLoadedData(isPMC, _vsSolutionData); + } + + internal void AddPowerShellLoadedData(bool isPMC, NuGetPowershellVSSolutionCloseEvent vsSolutionData) + { + lock (_lock) + { + // PowerShellHost loaded first time, let's emit this to find out later how many VS instance crash after loading powershell. + if (!vsSolutionData._loadedFromPMC && !vsSolutionData._loadedFromPMUI) + { + var telemetryEvent = new TelemetryEvent(NuGetPowerShellLoaded, new Dictionary + { + { NuGetPowershellPrefix + LoadedFromPMC, isPMC } + }); + + if (TelemetryActivity.NuGetTelemetryService == null) + { + _powerShellLoadEvent = telemetryEvent; + } + else + { + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + } + } + + if (isPMC) + { + if (!vsSolutionData._loadedFromPMC) + { + vsSolutionData._firstTimeLoadedFromPMC = true; + } + + vsSolutionData._loadedFromPMC = true; + } + else + { + if (!vsSolutionData._loadedFromPMUI) + { + vsSolutionData._loadedFromPMUI = true; + } + + vsSolutionData._loadedFromPMUI = true; + } + } + } + + private void NuGetPowerShellUsage_PowerShellCommandExecuteEvent(bool isPMC, string commandStr) + { + AddPowerShellCommandExecuteData(isPMC, commandStr, _vsSolutionData); + } + + internal void AddPowerShellCommandExecuteData(bool isPMC, string commandStr, NuGetPowershellVSSolutionCloseEvent vsSolutionData) + { + lock (_lock) + { + if (isPMC) + { + { + // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files + // For PMC all installation done in one pass so no double counting. + vsSolutionData._nuGetPMCExecuteCommandCount++; + } + } + else + { + // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files + // This one is called for both init.ps1 and install.ps1 seperately. + // For MSBuildNuGetProject projects install.ps1 can even further increase duplicate counting: See MSBuildNuGetProject.cs#L377 - L396 + // Also this concern valid for dependent packages with *.ps1 files. + vsSolutionData._nuGetPMUIExecuteCommandCount++; + } + + // Check and add to telemetery if a command is NugetCommand like 'install-package' etc. + if (vsSolutionData._nuGetCommandUsed) + { + return; + } + + if (!string.IsNullOrWhiteSpace(commandStr)) + { + string command = commandStr.Trim().ToUpperInvariant(); + string[] commandParts = command.Split(' '); + + if (commandParts.Count() > 1) + { + command = commandParts[0]; + } + + switch (command) + { + case "GET-HELP": + case "FIND-PACKAGE": + case "GET-PACKAGE": + case "INSTALL-PACKAGE": + case "UNINSTALL-PACKAGE": + case "UPDATE-PACKAGE": + case "SYNC-PACKAGE": + case "ADD-BINDINGREDIRECT": + case "GET-PROJECT": + case "REGISTER-TABEXPANSION": + // Nuget Command executed + vsSolutionData._nuGetCommandUsed = true; + break; + } + } + } + } + + private void NuGetPowerShellUsage_InitPs1LoadEvent(bool isPMC) + { + AddInitPs1LoadData(isPMC, _vsSolutionData); + } + + internal void AddInitPs1LoadData(bool isPMC, NuGetPowershellVSSolutionCloseEvent vsSolutionData) + { + lock (_lock) + { + // Test bit flag for if init.ps1 already loaded from PMC or PMUI + if (isPMC && (!vsSolutionData._firstTimeLoadedFromPMC && !vsSolutionData._firstTimeLoadedFromPMUI)) + { + // if not then set initialization origin bit + vsSolutionData._initPs1LoadedFromPMCFirst = true; + } + + if (isPMC) + { + vsSolutionData._initPs1LoadPMC = true; + } + else + { + vsSolutionData._initPs1LoadPMUI = true; + } + } + } + + private void NuGetPowerShellUsage_PMCWindowsEventHandler(bool? reOpen) + { + AddPMCWindowsEventData(reOpen); + } + + internal void AddPMCWindowsEventData(bool? reOpen) + { + lock (_lock) + { + if (reOpen.HasValue) + { + _vsInstanceData._reOpenAtStart = reOpen.Value; + } + else + { + _vsSolutionData._nuGetPMCWindowLoadCount++; + } + } + } + + private void NuGetPowerShellUsage_SolutionOpenHandler() + { + lock (_lock) + { + if (_powerShellLoadEvent != null) + { + TelemetryActivity.EmitTelemetryEvent(_powerShellLoadEvent); + _powerShellLoadEvent = null; + } + + _solutionCount++; + + // Edge case: PMC used without solution load + if (!_vsSolutionData._solutionLoaded && _vsSolutionData._nuGetPMCExecuteCommandCount > 0) + { + // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event aggregation before loading a solution. + TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); + UpdateVSInstanceData(); + } + + ClearSolutionData(); + _vsSolutionData._solutionLoaded = true; + } + } + + private void NuGetPowerShellUsage_SolutionCloseHandler() + { + lock (_lock) + { + // Emit solution telemetry + TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); + UpdateVSInstanceData(); + ClearSolutionData(); + } + } + + private void NuGetPowerShellUsage_VSInstanseCloseHandler() + { + lock (_lock) + { + if (_powerShellLoadEvent != null) + { + TelemetryActivity.EmitTelemetryEvent(_powerShellLoadEvent); + _powerShellLoadEvent = null; + } + + // Edge case: PMC used without solution load + if (!_vsSolutionData._solutionLoaded && _vsSolutionData._nuGetPMCExecuteCommandCount > 0) + { + // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event aggregation before loading a solution. + TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); + UpdateVSInstanceData(); + } + + // Emit VS Instance telemetry + TelemetryActivity.EmitTelemetryEvent(_vsInstanceData.ToTelemetryEvent()); + } + } + + //If open new solution then need to clear previous solution events. But powershell remain loaded in memory. + private void ClearSolutionData() + { + bool pmcPowershellLoad = _vsSolutionData._loadedFromPMC; + bool pmuiPowershellLoad = _vsSolutionData._loadedFromPMUI; + + _vsSolutionData = new NuGetPowershellVSSolutionCloseEvent(); + + _vsSolutionData._loadedFromPMC = pmcPowershellLoad; + _vsSolutionData._loadedFromPMUI = pmuiPowershellLoad; + } + + private void UpdateVSInstanceData() + { + _vsInstanceData._nugetPMCExecuteCommandCount += _vsSolutionData._nuGetPMCExecuteCommandCount; + _vsInstanceData._nugetPMCWindowLoadCount += _vsSolutionData._nuGetPMCWindowLoadCount; + _vsInstanceData._nugetPMUIExecuteCommandCount += _vsSolutionData._nuGetPMUIExecuteCommandCount; + + if (_vsSolutionData._loadedFromPMC) + { + _vsInstanceData._pmcLoadedSolutionCount++; + } + + if (_vsSolutionData._loadedFromPMUI) + { + _vsInstanceData._pmuiLoadedSolutionCount++; + } + + _vsInstanceData._solutionCount = _solutionCount; + } + + public void Dispose() + { + NuGetPowerShellUsage.PowerShellLoadEvent -= NuGetPowerShellUsage_PMCLoadEventHandler; + NuGetPowerShellUsage.PowerShellCommandExecuteEvent -= NuGetPowerShellUsage_PowerShellCommandExecuteEvent; + NuGetPowerShellUsage.InitPs1LoadEvent -= NuGetPowerShellUsage_InitPs1LoadEvent; + NuGetPowerShellUsage.SolutionOpenEvent -= NuGetPowerShellUsage_SolutionOpenHandler; + NuGetPowerShellUsage.SolutionCloseEvent -= NuGetPowerShellUsage_SolutionCloseHandler; + NuGetPowerShellUsage.VSInstanceCloseEvent -= NuGetPowerShellUsage_VSInstanseCloseHandler; + } + + internal class NuGetPowershellVSSolutionCloseEvent + { + internal bool _firstTimeLoadedFromPMC; + internal bool _firstTimeLoadedFromPMUI; + internal bool _initPs1LoadedFromPMCFirst; + internal bool _initPs1LoadPMC; + internal bool _initPs1LoadPMUI; + internal bool _loadedFromPMC; + internal bool _loadedFromPMUI; + internal bool _nuGetCommandUsed; + internal int _nuGetPMCExecuteCommandCount; + internal int _nuGetPMCWindowLoadCount; + internal int _nuGetPMUIExecuteCommandCount; + internal bool _solutionLoaded; + + internal NuGetPowershellVSSolutionCloseEvent() + { + _firstTimeLoadedFromPMC = false; + _firstTimeLoadedFromPMUI = false; + _initPs1LoadedFromPMCFirst = false; + _initPs1LoadPMC = false; + _initPs1LoadPMUI = false; + _loadedFromPMC = false; + _loadedFromPMUI = false; + _nuGetCommandUsed = false; + _nuGetPMCExecuteCommandCount = 0; + _nuGetPMCWindowLoadCount = 0; + _nuGetPMUIExecuteCommandCount = 0; + _solutionLoaded = false; + + } + + internal TelemetryEvent ToTelemetryEvent() + { + var telemetry = new TelemetryEvent(nameof(NuGetPowershellVSSolutionCloseEvent), + new Dictionary() + { + { NuGetPowershellPrefix + FirstTimeLoadedFromPMC , _firstTimeLoadedFromPMC }, + { NuGetPowershellPrefix + FirstTimeLoadedFromPMUI , _firstTimeLoadedFromPMUI }, + { NuGetPowershellPrefix + InitPs1LoadedFromPMCFirst , _initPs1LoadedFromPMCFirst }, + { NuGetPowershellPrefix + InitPs1LoadPMC , _initPs1LoadPMC }, + { NuGetPowershellPrefix + InitPs1LoadPMUI , _initPs1LoadPMUI }, + { NuGetPowershellPrefix + LoadedFromPMC , _loadedFromPMC }, + { NuGetPowershellPrefix + LoadedFromPMUI , _loadedFromPMUI }, + { NuGetPowershellPrefix + NuGetCommandUsed , _nuGetCommandUsed }, + { NuGetPowershellPrefix + NuGetPMCExecuteCommandCount , _nuGetPMCExecuteCommandCount }, + { NuGetPowershellPrefix + NuGetPMCWindowLoadCount , _nuGetPMCWindowLoadCount }, + { NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount , _nuGetPMUIExecuteCommandCount }, + { NuGetPowershellPrefix + SolutionLoaded , _solutionLoaded } + }); + + return telemetry; + } + } + + internal class NuGetPowershellVSInstanceCloseEvent + { + internal int _nugetPMCExecuteCommandCount; + internal int _nugetPMCWindowLoadCount; + internal int _nugetPMUIExecuteCommandCount; + internal int _pmcLoadedSolutionCount; + internal int _pmuiLoadedSolutionCount; + internal bool _reOpenAtStart; + internal int _solutionCount; + + internal NuGetPowershellVSInstanceCloseEvent() + { + _nugetPMCExecuteCommandCount = 0; + _nugetPMCWindowLoadCount = 0; + _nugetPMUIExecuteCommandCount = 0; + _pmcLoadedSolutionCount = 0; + _pmuiLoadedSolutionCount = 0; + _reOpenAtStart = false; + _solutionCount = 0; + } + + internal TelemetryEvent ToTelemetryEvent() + { + var telemetry = new TelemetryEvent(nameof(NuGetPowershellVSInstanceCloseEvent), + new Dictionary() + { + { NuGetPowershellPrefix + NuGetPMCExecuteCommandCount , _nugetPMCExecuteCommandCount }, + { NuGetPowershellPrefix + NuGetPMCWindowLoadCount , _nugetPMCWindowLoadCount }, + { NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount , _nugetPMUIExecuteCommandCount }, + { NuGetPowershellPrefix + PMCPowerShellLoadedSolutionCount , _pmcLoadedSolutionCount }, + { NuGetPowershellPrefix + PMUIPowerShellLoadedSolutionCount , _pmuiLoadedSolutionCount }, + { NuGetPowershellPrefix + ReOpenAtStart , _reOpenAtStart }, + { NuGetPowershellPrefix + SolutionCount , _solutionCount }, + }); + + return telemetry; + } + } + } +} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs deleted file mode 100644 index 7785b93d125..00000000000 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetTelemetryCollector.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Linq; -using System.Threading; -using NuGet.Common; - -namespace NuGet.VisualStudio.Telemetry -{ - [Export(typeof(INuGetTelemetryCollector))] - [PartCreationPolicy(CreationPolicy.Shared)] - internal sealed class NuGetTelemetryCollector : INuGetTelemetryCollector - { - // _solutionTelemetryEvents hold telemetry data for current VS solution session. - private Lazy>> _vsSolutionTelemetryEvents; - // _vsInstanceTelemetryEvents hold telemetry for current VS instance session. - private readonly Lazy>> _vsInstanceTelemetryEvents; - - public NuGetTelemetryCollector() - { - _vsSolutionTelemetryEvents = new Lazy>>(); - _vsInstanceTelemetryEvents = new Lazy>>(); - } - - /// Add a to telemetry list which will be aggregated and emitted later. - /// Telemetry event to add into aggregation. - public void AddSolutionTelemetryEvent(Dictionary telemetryData) - { - _vsSolutionTelemetryEvents.Value.Add(telemetryData); - _vsInstanceTelemetryEvents.Value.Add(telemetryData); - } - - public IReadOnlyList> GetVSSolutionTelemetryEvents() => _vsSolutionTelemetryEvents.Value.ToList(); - public IReadOnlyList> GetVSIntanceTelemetryEvents() => _vsInstanceTelemetryEvents.Value.ToList(); - - // If open new solution then need to clear previous solution events. - public void ClearSolutionTelemetryEvents() - { - var newBag = new Lazy>>(); - Interlocked.Exchange(ref _vsSolutionTelemetryEvents, newBag); - } - } -} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs new file mode 100644 index 00000000000..07e10a3e1e0 --- /dev/null +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs @@ -0,0 +1,64 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace NuGet.VisualStudio.Telemetry.Powershell +{ + public static class NuGetPowerShellUsage + { + public delegate void SolutionOpenHandler(); + public static event SolutionOpenHandler SolutionOpenEvent; + + public delegate void SolutionCloseHandler(); + public static event SolutionCloseHandler SolutionCloseEvent; + + public delegate void VSInstanceCloseHandler(); + public static event VSInstanceCloseHandler VSInstanceCloseEvent; + + public delegate void PowerShellLoadEventHandler(bool isPMC); + public static event PowerShellLoadEventHandler PowerShellLoadEvent; + + public delegate void PowerShellCommandExecuteEventHandler(bool isPMC, string commandStr); + public static event PowerShellCommandExecuteEventHandler PowerShellCommandExecuteEvent; + + public delegate void InitPs1LoadEventHandler(bool isPMC); + public static event InitPs1LoadEventHandler InitPs1LoadEvent; + + public delegate void PMCWindowEventHandler(bool? reOpen); + public static event PMCWindowEventHandler PMCWindowsEvent; + + public static void RaisePowerShellLoadEvent(bool isPMC) + { + PowerShellLoadEvent?.Invoke(isPMC); + } + + public static void RaiseCommandExecuteEvent(bool isPMC, string commandStr) + { + PowerShellCommandExecuteEvent?.Invoke(isPMC, commandStr); + } + + public static void RaisInitPs1LoadEvent(bool isPMC) + { + InitPs1LoadEvent?.Invoke(isPMC); + } + + public static void RaisePMCWindowsEvent(bool? reOpen) + { + PMCWindowsEvent?.Invoke(reOpen); + } + + public static void RaiseSolutionOpenEvent() + { + SolutionOpenEvent?.Invoke(); + } + + public static void RaiseSolutionCloseEvent() + { + SolutionCloseEvent?.Invoke(); + } + + public static void RaiseVSInstanceCloseEvent() + { + VSInstanceCloseEvent?.Invoke(); + } + } +} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellLoadedEvent.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellLoadedEvent.cs deleted file mode 100644 index 3692a4d644e..00000000000 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellLoadedEvent.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using NuGet.Common; -using TelemetryConst = NuGet.VisualStudio.Telemetry.PowershellTelemetryConsts; - -namespace NuGet.VisualStudio.Telemetry.Powershell -{ - internal class NuGetPowershellLoadedEvent : TelemetryEvent - { - // Emitted first time powershell gets loaded, so we can detect if VS crashed after powershell loaded. - public NuGetPowershellLoadedEvent(bool loadedfrompmc) : base(TelemetryConst.NuGetPowerShellLoaded) - { - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.LoadedFromPMC] = TelemetryConst.LoadedFromPMC; - } - } -} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSInstanceCloseEvent.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSInstanceCloseEvent.cs deleted file mode 100644 index 0ee98fe3fc3..00000000000 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSInstanceCloseEvent.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using NuGet.Common; -using TelemetryConst = NuGet.VisualStudio.Telemetry.PowershellTelemetryConsts; - -namespace NuGet.VisualStudio.Telemetry.Powershell -{ - internal class NuGetPowershellVSInstanceCloseEvent : TelemetryEvent - { - public NuGetPowershellVSInstanceCloseEvent( - int nugetpmcexecutecommandcount, - int nugetpmcwindowloadcount, - int nugetpmuiexecutecommandcount, - int pmcpowershellloadedsolutioncount, - int pmuipowershellloadedsolutioncount, - bool reopenatstart, - int solutioncount) : base(TelemetryConst.NuGetVSInstanceClose) - { - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCExecuteCommandCount] = nugetpmcexecutecommandcount; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCWindowLoadCount] = nugetpmcwindowloadcount; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMUIExecuteCommandCount] = nugetpmuiexecutecommandcount; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.PMCPowerShellLoadedSolutionCount] = pmcpowershellloadedsolutioncount; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.PMUIPowerShellLoadedSolutionCount] = pmuipowershellloadedsolutioncount; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.ReOpenAtStart] = reopenatstart; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.SolutionCount] = solutioncount; - } - } -} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs deleted file mode 100644 index f748702b82e..00000000000 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowershellVSSolutionCloseEvent.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using NuGet.Common; -using TelemetryConst = NuGet.VisualStudio.Telemetry.PowershellTelemetryConsts; - -namespace NuGet.VisualStudio.Telemetry.Powershell -{ - internal class NuGetPowershellVSSolutionCloseEvent : TelemetryEvent - { - public NuGetPowershellVSSolutionCloseEvent() : this( - FirstTimeLoadedFromPMC: false, - FirstTimeLoadedFromPMUI: false, - InitPs1LoadedFromPMCFirst: false, - InitPs1LoadPMC: false, - InitPs1LoadPMUI: false, - LoadedFromPMC: false, - LoadedFromPMUI: false, - NuGetCommandUsed: false, - NuGetPMCExecuteCommandCount: 0, - NuGetPMCWindowLoadCount: 0, - NuGetPMUIExecuteCommandCount: 0, - SolutionLoaded: false) - { } - - public NuGetPowershellVSSolutionCloseEvent( - bool FirstTimeLoadedFromPMC, - bool FirstTimeLoadedFromPMUI, - bool InitPs1LoadedFromPMCFirst, - bool InitPs1LoadPMC, - bool InitPs1LoadPMUI, - bool LoadedFromPMC, - bool LoadedFromPMUI, - bool NuGetCommandUsed, - int NuGetPMCExecuteCommandCount, - int NuGetPMCWindowLoadCount, - int NuGetPMUIExecuteCommandCount, - bool SolutionLoaded) : base(TelemetryConst.NuGetVSSolutionClose) - { - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.FirstTimeLoadedFromPMC] = FirstTimeLoadedFromPMC; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.FirstTimeLoadedFromPMUI] = FirstTimeLoadedFromPMUI; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.InitPs1LoadedFromPMCFirst] = InitPs1LoadedFromPMCFirst; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.InitPs1LoadPMC] = InitPs1LoadPMC; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.InitPs1LoadPMUI] = InitPs1LoadPMUI; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.LoadedFromPMC] = LoadedFromPMC; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.LoadedFromPMUI] = LoadedFromPMUI; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetCommandUsed] = NuGetCommandUsed; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCExecuteCommandCount] = NuGetPMCExecuteCommandCount; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMCWindowLoadCount] = NuGetPMCWindowLoadCount; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.NuGetPMUIExecuteCommandCount] = NuGetPMUIExecuteCommandCount; - base[TelemetryConst.NuGetPowershellPrefix + TelemetryConst.SolutionLoaded] = SolutionLoaded; - } - } -} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs deleted file mode 100644 index 5ba3ee4231e..00000000000 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowerShellHostTelemetryProcessor.cs +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Linq; -using NuGet.Common; -using TelemetryConst = NuGet.VisualStudio.Telemetry.PowershellTelemetryConsts; - -namespace NuGet.VisualStudio.Telemetry.Powershell -{ - [Export(typeof(PowerShellHostTelemetryProcessor))] - [PartCreationPolicy(CreationPolicy.Shared)] - public class PowerShellHostTelemetryProcessor - { - private readonly object _telemetryLock = new object(); - private bool _isTelemetryEmitted; - private int _pmcExecutedCount; - private int _nonPmcExecutedCount; - private readonly INuGetTelemetryCollector _nuGetTelemetryCollector; - - // There are 8 bits in byte which used as boolean flags. - // 0 - Did any nuget command execute during current VS solution session? - // 1 - Did init.ps1 is load during current VS solution session from PMUI? - // 2 - Did init.ps1 is load during current VS solution session from PMC? - // 3 - Did init.ps1 load first from PMC or PMUI for above 2 cases? - // 4 - Did PowerShellHost for PMUI created during current VS solution session first time? - // 5 - Did PowerShellHost for PMC created during current VS solution session first time? - // 6 - Did PowerShellHost for PMUI created during current VS instance session? - // 7 - Did PowerShellHost for PMC created during current VS instance session? - private static byte PowerShellHostInstances; - - public PowerShellHostTelemetryProcessor() - { - _nuGetTelemetryCollector = ServiceLocator.GetInstance(); - } - - public void RecordPSHostInitializeOrigin(bool isPMC) - { - lock (_telemetryLock) - { - if (isPMC) - { - if (TestAnyBitNotSet(PowerShellHostInstances, 0b00000001)) - { - // First time load PMC - PowerShellHostInstances |= 0b00000100; - } - - // LoadedFromPMC - PowerShellHostInstances |= 0b00000001; - } - else - { - if (TestAnyBitNotSet(PowerShellHostInstances, 0b00000010)) - { - // First time load from PMUI - PowerShellHostInstances |= 0b00001000; - } - - // LoadedFromPMUI - PowerShellHostInstances |= 0b00000010; - } - } - } - - public void IncreaseCommandCounter(bool isPMC) - { - lock (_telemetryLock) - { - if (isPMC) - { - // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files - // For PMC all installation done in one pass so no double counting. - _pmcExecutedCount++; - } - else - { - // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files - // This one is called for both init.ps1 and install.ps1 seperately. - // For MSBuildNuGetProject projects install.ps1 can even further increase duplicate counting: See MSBuildNuGetProject.cs#L377 - L396 - // Also this concern valid for dependent packages with *.ps1 files. - _nonPmcExecutedCount++; - } - } - } - - public void HandleSolutionOpenedEmit() - { - if (_pmcExecutedCount > 0) - { - // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event aggregation before loading a solution. - EmitPowershellUsageTelemetry(false); - } - - _isTelemetryEmitted = false; - } - - public void HandleSolutionClosingEmit() - { - EmitPowershellUsageTelemetry(true); - - // PMC can still used after solution is closed, so reset _isTelemetryEmitted make it possible to remaining telemetry. - _isTelemetryEmitted = false; - } - - public void EmitPowerShellLoadedTelemetry(bool isPMC) - { - lock (_telemetryLock) - { - // PowerShellHost loaded first time, let's emit this to find out later how many VS instance crash after loading powershell. - if (TestAnyBitNotSet(PowerShellHostInstances, 0b00000011)) - { - // Edge case: PMC window can be opened without any solution at all, but sometimes TelemetryActivity.NuGetTelemetryService is not ready yet. - // In general we want to emit this telemetry right away. - if (TelemetryActivity.NuGetTelemetryService != null) - { - var telemetryEvent = new TelemetryEvent(TelemetryConst.NuGetPowerShellLoaded, new Dictionary - { - { TelemetryConst.NuGetPowershellPrefix + TelemetryConst.LoadedFromPMC, isPMC } - }); - - TelemetryActivity.EmitTelemetryEvent(telemetryEvent); - } - else - { - _nuGetTelemetryCollector?.AddSolutionTelemetryEvent(new Dictionary - { - { TelemetryConst.Name , TelemetryConst.NuGetPowerShellLoaded }, - { TelemetryConst.NuGetPowershellPrefix + TelemetryConst.LoadedFromPMC, isPMC } - }); - } - } - } - } - - public void EmitPowershellUsageTelemetry(bool withSolution) - { - lock (_telemetryLock) - { - if (!_isTelemetryEmitted) - { - // PMC opened, but no command executed nor any solution was loaded. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore. - if (withSolution || _pmcExecutedCount > 0) - { - var nugetVSSolutionCloseEvent = new Dictionary - { - { TelemetryConst.Name , TelemetryConst.NuGetVSSolutionClose }, - { TelemetryConst.FirstTimeLoadedFromPMC , TestAllBitsSet(PowerShellHostInstances, 0b00000100) }, - { TelemetryConst.FirstTimeLoadedFromPMUI, TestAllBitsSet(PowerShellHostInstances, 0b00001000) }, - { TelemetryConst.InitPs1LoadedFromPMCFirst, TestAllBitsSet(PowerShellHostInstances, 0b00010000) }, - { TelemetryConst.InitPs1LoadPMC, TestAllBitsSet(PowerShellHostInstances, 0b00100000) }, - { TelemetryConst.InitPs1LoadPMUI, TestAllBitsSet(PowerShellHostInstances, 0b01000000) }, - { TelemetryConst.LoadedFromPMC, TestAllBitsSet(PowerShellHostInstances, 0b00000001) }, - { TelemetryConst.LoadedFromPMUI, TestAllBitsSet(PowerShellHostInstances, 0b00000010) }, - { TelemetryConst.NuGetCommandUsed, TestAllBitsSet(PowerShellHostInstances, 0b10000000) }, - { TelemetryConst.NuGetPMCExecuteCommandCount, _pmcExecutedCount }, - { TelemetryConst.NuGetPMCWindowLoadCount, 0 }, - { TelemetryConst.NuGetPMUIExecuteCommandCount, _nonPmcExecutedCount }, - { TelemetryConst.SolutionLoaded, withSolution } - }; - - _nuGetTelemetryCollector?.AddSolutionTelemetryEvent(nugetVSSolutionCloseEvent); - } - - _pmcExecutedCount = 0; - _nonPmcExecutedCount = 0; - - // Keep 2 flags for current VS instance,but reset all others because they're for current VS session. - PowerShellHostInstances &= 0b00000011; - } - - _isTelemetryEmitted = true; - } - } - - public void RecordInitPs1loaded(bool isPMC) - { - // Test bit flag for if init.ps1 already loaded from PMC or PMUI - if (TestAnyBitNotSet(PowerShellHostInstances, 0b01100000) && isPMC) - { - // if not then set initialization origin bit - PowerShellHostInstances |= 0b00010000; - } - - if (isPMC) - { - PowerShellHostInstances |= 0b00100000; - } - else - { - PowerShellHostInstances |= 0b01000000; - } - } - - public void IsNuGetCommand(string commandStr) - { - if (TestAllBitsSet(PowerShellHostInstances, 0b10000000)) - { - // NuGetCommand used and flag is already set - return; - } - - if (!string.IsNullOrWhiteSpace(commandStr)) - { - string command = commandStr.Trim().ToUpperInvariant(); - string[] commandParts = command.Split(' '); - - if (commandParts.Count() > 1) - { - command = commandParts[0]; - } - - switch (command) - { - case "GET-HELP": - case "FIND-PACKAGE": - case "GET-PACKAGE": - case "INSTALL-PACKAGE": - case "UNINSTALL-PACKAGE": - case "UPDATE-PACKAGE": - case "SYNC-PACKAGE": - case "ADD-BINDINGREDIRECT": - case "GET-PROJECT": - case "REGISTER-TABEXPANSION": - // NugetCommand executed - PowerShellHostInstances |= 0b10000000; - break; - } - } - } - - private bool TestAllBitsSet(byte input, byte mask) - { - return (input & mask) == mask; - } - - private bool TestAnyBitNotSet(byte input, byte mask) - { - return (input & mask) == 0; - } - } -} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs deleted file mode 100644 index a3090d3a932..00000000000 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryConsts.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace NuGet.VisualStudio.Telemetry -{ - public sealed class PowershellTelemetryConsts - { - // PMC, PMUI powershell telemetry consts - public const string NuGetPMCExecuteCommandCount = nameof(NuGetPMCExecuteCommandCount); - public const string NuGetPMUIExecuteCommandCount = nameof(NuGetPMUIExecuteCommandCount); - public const string NuGetCommandUsed = nameof(NuGetCommandUsed); - public const string InitPs1LoadPMUI = nameof(InitPs1LoadPMUI); - public const string InitPs1LoadPMC = nameof(InitPs1LoadPMC); - public const string InitPs1LoadedFromPMCFirst = nameof(InitPs1LoadedFromPMCFirst); - public const string LoadedFromPMUI = nameof(LoadedFromPMUI); - public const string FirstTimeLoadedFromPMUI = nameof(FirstTimeLoadedFromPMUI); - public const string LoadedFromPMC = nameof(LoadedFromPMC); - public const string FirstTimeLoadedFromPMC = nameof(FirstTimeLoadedFromPMC); - public const string SolutionLoaded = nameof(SolutionLoaded); - public const string PowerShellExecuteCommand = nameof(PowerShellExecuteCommand); - public const string NuGetPowerShellLoaded = nameof(NuGetPowerShellLoaded); - - // PMC UI Console Container telemetry consts - public const string PackageManagerConsoleWindowsLoad = nameof(PackageManagerConsoleWindowsLoad); - public const string NuGetPMCWindowLoadCount = nameof(NuGetPMCWindowLoadCount); - public const string ReOpenAtStart = nameof(ReOpenAtStart); - - // Const name for emitting when VS solution close or VS instance close. - public const string Name = nameof(Name); - public const string NuGetPowershellPrefix = "NuGetPowershellPrefix."; // Using prefix prevent accidental same name property collission from different type telemetry. - public const string NuGetVSSolutionClose = nameof(NuGetVSSolutionClose); - public const string NuGetVSInstanceClose = nameof(NuGetVSInstanceClose); - public const string SolutionCount = nameof(SolutionCount); - public const string PMCPowerShellLoadedSolutionCount = nameof(PMCPowerShellLoadedSolutionCount); - public const string PMUIPowerShellLoadedSolutionCount = nameof(PMUIPowerShellLoadedSolutionCount); - } -} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs deleted file mode 100644 index 66d5811f8bc..00000000000 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/PowershellTelemetryEmitter.cs +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Linq; -using NuGet.Common; -using TelemetryConst = NuGet.VisualStudio.Telemetry.PowershellTelemetryConsts; - -namespace NuGet.VisualStudio.Telemetry.Powershell -{ - [Export(typeof(PowershellTelemetryEmitter))] - [PartCreationPolicy(CreationPolicy.Shared)] - public sealed class PowershellTelemetryEmitter - { - private int _solutionCount; - private bool _powershellLoadEventEmitted = false; - private INuGetTelemetryCollector _nuGetTelemetryCollector; - private Func>> _vsSolutionTelemetryEvents; - private Func>> _vsInstanceTelemetryEvents; - private readonly INuGetTelemetryProvider _telemetryProvider; - - [ImportingConstructor] - internal PowershellTelemetryEmitter(INuGetTelemetryCollector nuGetTelemetryCollector, INuGetTelemetryProvider telemetryProvider) - { - _nuGetTelemetryCollector = nuGetTelemetryCollector; - _telemetryProvider = telemetryProvider ?? throw new ArgumentNullException(nameof(telemetryProvider)); - _vsSolutionTelemetryEvents = () => _nuGetTelemetryCollector?.GetVSSolutionTelemetryEvents().ToList(); - _vsInstanceTelemetryEvents = () => _nuGetTelemetryCollector?.GetVSIntanceTelemetryEvents().ToList(); - } - - public void SolutionOpenedTelemetryEmit() - { - try - { - // Handle edge cases. - EmitPMCUsedWithoutSolution(); - _nuGetTelemetryCollector.ClearSolutionTelemetryEvents(); - _solutionCount++; - } - catch (Exception exception) - { - _telemetryProvider.PostFault(exception, typeof(PowershellTelemetryEmitter).FullName); - } - } - - // Emit VS solution session telemetry when solution is closed. - public void SolutionClosedTelemetryEmit() - { - try - { - // Queue all different types of telemetries and do some processing prior to emit. - VSSolutionPowershellTelemetryEmit(); - _nuGetTelemetryCollector.ClearSolutionTelemetryEvents(); - } - catch (Exception exception) - { - _telemetryProvider.PostFault(exception, typeof(PowershellTelemetryEmitter).FullName); - } - } - - // Emit VS solution session telemetry when VS instance is closed. - public void VSInstanceClosedTelemetryEmit() - { - try - { - // Handle edge cases. - EmitPMCUsedWithoutSolution(); - VSInstancePowershellTelemetryEmit(); - } - catch (Exception exception) - { - _telemetryProvider.PostFault(exception, typeof(PowershellTelemetryEmitter).FullName); - } - } - - private void EmitPMCUsedWithoutSolution() - { - // Edge case: PMC window can be opened without any solution at all, but sometimes TelemetryActivity.NuGetTelemetryService is not ready yet when PMC open. - // In general we want to emit this telemetry right away, but not possible then emit later. - - if (!_powershellLoadEventEmitted) - { - Dictionary nuGetPowerShellLoadedEvent = _vsSolutionTelemetryEvents() - .FirstOrDefault(e => (string)e[TelemetryConst.Name] == TelemetryConst.NuGetPowerShellLoaded); - - if (nuGetPowerShellLoadedEvent != null) - { - TelemetryActivity.EmitTelemetryEvent( - new NuGetPowershellLoadedEvent( - loadedfrompmc: (bool)nuGetPowerShellLoadedEvent[TelemetryConst.LoadedFromPMC]) - ); - _powershellLoadEventEmitted = true; - } - } - - bool anyNotEmittedVSSolutionCloseTelemetry = _vsSolutionTelemetryEvents() - .Any(e => - e.ContainsKey(TelemetryConst.Name) - && (string)e[TelemetryConst.Name] == TelemetryConst.NuGetVSSolutionClose); - - if (anyNotEmittedVSSolutionCloseTelemetry) - { - SolutionClosedTelemetryEmit(); - } - } - - private void VSSolutionPowershellTelemetryEmit() - { - Dictionary vsSolutionCloseTelemetry = _vsSolutionTelemetryEvents() - .Where(e => - e.ContainsKey(TelemetryConst.Name) - && (string)e[TelemetryConst.Name] == TelemetryConst.NuGetVSSolutionClose) - .FirstOrDefault(); - - // If powershell(PMC/PMUI) is not loaded at all then we need to create default telemetry event which will be emitted. - var nugetVSSolutionCloseEvent = new NuGetPowershellVSSolutionCloseEvent(); - - if (vsSolutionCloseTelemetry != null) - { - nugetVSSolutionCloseEvent = new NuGetPowershellVSSolutionCloseEvent( - FirstTimeLoadedFromPMC: (bool)vsSolutionCloseTelemetry[TelemetryConst.FirstTimeLoadedFromPMC], - FirstTimeLoadedFromPMUI: (bool)vsSolutionCloseTelemetry[TelemetryConst.FirstTimeLoadedFromPMUI], - InitPs1LoadedFromPMCFirst: (bool)vsSolutionCloseTelemetry[TelemetryConst.InitPs1LoadedFromPMCFirst], - InitPs1LoadPMC: (bool)vsSolutionCloseTelemetry[TelemetryConst.InitPs1LoadPMC], - InitPs1LoadPMUI: (bool)vsSolutionCloseTelemetry[TelemetryConst.InitPs1LoadPMUI], - LoadedFromPMC: (bool)vsSolutionCloseTelemetry[TelemetryConst.LoadedFromPMC], - LoadedFromPMUI: (bool)vsSolutionCloseTelemetry[TelemetryConst.LoadedFromPMUI], - NuGetCommandUsed: (bool)vsSolutionCloseTelemetry[TelemetryConst.NuGetCommandUsed], - NuGetPMCExecuteCommandCount: (int)vsSolutionCloseTelemetry[TelemetryConst.NuGetPMCExecuteCommandCount], - NuGetPMCWindowLoadCount: _vsSolutionTelemetryEvents() - .Where(e => - e.ContainsKey(TelemetryConst.Name) - && (string)e[TelemetryConst.Name] == TelemetryConst.PackageManagerConsoleWindowsLoad) - .Sum(e => (int)e[TelemetryConst.NuGetPMCWindowLoadCount]), - NuGetPMUIExecuteCommandCount: (int)vsSolutionCloseTelemetry[TelemetryConst.NuGetPMUIExecuteCommandCount], - SolutionLoaded: (bool)vsSolutionCloseTelemetry[TelemetryConst.SolutionLoaded] - ); - } - - TelemetryActivity.EmitTelemetryEvent(nugetVSSolutionCloseEvent); - } - - private void VSInstancePowershellTelemetryEmit() - { - List> nuGetVSSolutionCloseEvents = _vsInstanceTelemetryEvents() - .Where(e => - e.ContainsKey(TelemetryConst.Name) - && (string)e[TelemetryConst.Name] == TelemetryConst.NuGetVSSolutionClose) - .ToList(); - List> packageManagerConsoleWindowsLoadEvents = _vsInstanceTelemetryEvents() - .Where(e => - e.ContainsKey(TelemetryConst.Name) - && (string)e[TelemetryConst.Name] == TelemetryConst.PackageManagerConsoleWindowsLoad) - .ToList(); - - NuGetPowershellVSInstanceCloseEvent nuGetPowershellVSInstanceCloseEvent = new NuGetPowershellVSInstanceCloseEvent( - nugetpmcexecutecommandcount: nuGetVSSolutionCloseEvents.Sum(e => (int)e[TelemetryConst.NuGetPMCExecuteCommandCount]), - nugetpmcwindowloadcount: packageManagerConsoleWindowsLoadEvents.Sum(e => (int)e[TelemetryConst.NuGetPMCWindowLoadCount]), - nugetpmuiexecutecommandcount: nuGetVSSolutionCloseEvents.Sum(e => (int)e[TelemetryConst.NuGetPMUIExecuteCommandCount]), - pmcpowershellloadedsolutioncount: nuGetVSSolutionCloseEvents.Count(e => (bool)e[TelemetryConst.SolutionLoaded] && (bool)e[TelemetryConst.LoadedFromPMC]), - pmuipowershellloadedsolutioncount: nuGetVSSolutionCloseEvents.Count(e => (bool)e[TelemetryConst.SolutionLoaded] && (bool)e[TelemetryConst.LoadedFromPMUI]), - reopenatstart: packageManagerConsoleWindowsLoadEvents.Any(e => e[TelemetryConst.ReOpenAtStart] != null), - solutioncount: _solutionCount - ); - - TelemetryActivity.EmitTelemetryEvent(nuGetPowershellVSInstanceCloseEvent); - } - } -} diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 48c3c311128..0488092b9f5 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -52,7 +52,6 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private readonly Lazy _settings; private readonly Lazy _sourceControlManagerProvider; private readonly Lazy _commonOperations; - private readonly Lazy _powerShellHostTelemetryProcessor; private readonly Lazy _deleteOnRestartManager; private readonly Lazy _scriptExecutor; private const string ActivePackageSourceKey = "activePackageSource"; @@ -116,7 +115,6 @@ protected PowerShellHost(string name, IRestoreEvents restoreEvents, IRunspaceMan _sourceControlManagerProvider = new Lazy( () => ServiceLocator.GetInstanceSafe()); _commonOperations = new Lazy(() => ServiceLocator.GetInstanceSafe()); - _powerShellHostTelemetryProcessor = new Lazy(() => ServiceLocator.GetInstanceSafe()); _name = name; IsCommandEnabled = true; @@ -316,17 +314,12 @@ public void Initialize(IConsole console) UpdateWorkingDirectory(); // Emit first time Powershell load event to find out later how many VS instance crash after loading powershell. - _powerShellHostTelemetryProcessor.Value.EmitPowerShellLoadedTelemetry(console is IWpfConsole); - // Record if PowerShellHost initiated from PMC or PMUI - _powerShellHostTelemetryProcessor.Value.RecordPSHostInitializeOrigin(console is IWpfConsole); + NuGetPowerShellUsageCollector nuGetPowerShellUsageCollectorInstance = ServiceLocator.GetInstance(); + Assumes.NotNull(nuGetPowerShellUsageCollectorInstance); - await ExecuteInitScriptsAsync(); + NuGetPowerShellUsage.RaisePowerShellLoadEvent(console is IWpfConsole); - _solutionManager.Value.SolutionOpening += (o, e) => - { - // Hook up solution events, check if PMC is used before without any solution. - _powerShellHostTelemetryProcessor.Value.HandleSolutionOpenedEmit(); - }; + await ExecuteInitScriptsAsync(); // check if PMC console is actually opened, then only hook to solution load events. if (console is IWpfConsole) @@ -346,7 +339,7 @@ public void Initialize(IConsole console) _solutionManager.Value.SolutionClosing += (o, e) => { // Hook up solution events, we emit telemetry data from current VS solution session. - _powerShellHostTelemetryProcessor.Value.HandleSolutionClosingEmit(); + NuGetPowerShellUsage.RaiseSolutionCloseEvent(); }; _solutionManager.Value.NuGetProjectAdded += (o, e) => UpdateWorkingDirectoryAndAvailableProjects(); @@ -495,7 +488,7 @@ private async Task ExecuteInitPs1Async(string installPath, PackageIdentity ident if (File.Exists(scriptPath)) { // Record if init.ps1 is loaded. - _powerShellHostTelemetryProcessor.Value.RecordInitPs1loaded(_activeConsole is IWpfConsole); + NuGetPowerShellUsage.RaisInitPs1LoadEvent(_activeConsole is IWpfConsole); if (_scriptExecutor.Value.TryMarkVisited(identity, PackageInitPS1State.FoundAndExecuted)) { @@ -555,10 +548,7 @@ public bool Execute(IConsole console, string command, params object[] inputs) } // Increase command execution counters for PMC/PMUI - _powerShellHostTelemetryProcessor.Value.IncreaseCommandCounter(console is IWpfConsole); - - // Check and add to telemetery if a command is NugetCommand like 'install-package' etc. - _powerShellHostTelemetryProcessor.Value.IsNuGetCommand(command); + NuGetPowerShellUsage.RaiseCommandExecuteEvent(console is IWpfConsole, command); // since install.ps1/uninstall.ps1 could depend on init scripts, so we need to make sure // to run it once for each solution @@ -943,11 +933,6 @@ public void Dispose() _restoreEvents.SolutionRestoreCompleted -= RestoreEvents_SolutionRestoreCompleted; _initScriptsLock.Dispose(); Runspace?.Dispose(); - - // Below emits telemetry in case there was no solution was loaded at all. - // But in case there were any solution then this one will ignored because actual data already emitted with solution SolutionClosing event previously. - // If no solution loaded nor PMC is engaged at then this will be ignored internally. Rather than sending separate nugetvssolutionclose telemetry with no data just ignore then. - _powerShellHostTelemetryProcessor.Value.EmitPowershellUsageTelemetry(false); } #endregion From 32f08f334a0827ab820edbb6843a94286d68b79e Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Wed, 20 Jan 2021 17:59:47 -0800 Subject: [PATCH 48/59] Fix reopen flag might not work from dispose on PMC window. --- .../Xamls/ConsoleContainer.xaml.cs | 19 +++++++---------- .../NuGetPowerShellUsageCollector.cs | 21 +++++++------------ .../Powershell/NuGetPowerShellUsage.cs | 6 +++--- 3 files changed, 19 insertions(+), 27 deletions(-) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index a558b6fc9d4..23de6803bcf 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -25,7 +25,6 @@ namespace NuGetConsole public sealed partial class ConsoleContainer : UserControl, IDisposable { private INuGetSolutionManagerService _solutionManager; - private bool _isTelemetryEmitted; public ConsoleContainer() { @@ -35,6 +34,7 @@ public ConsoleContainer() Assumes.NotNull(_nuGetPowerShellUsageCollector); Loaded += ConsoleContainer_Loaded; + Unloaded += ConsoleContainer_UnLoaded; ThreadHelper.JoinableTaskFactory.StartOnIdle( async () => @@ -84,16 +84,8 @@ public void NotifyInitializationCompleted() public void Dispose() { - if (!_isTelemetryEmitted) - { - // Work around to detect if PMC loaded automatically because it was last focused window. - bool? reopenAtStart = IsLoaded; - NuGetPowerShellUsage.RaisePMCWindowsEvent(reopenAtStart); - - _isTelemetryEmitted = true; - } - Loaded -= ConsoleContainer_Loaded; + Unloaded -= ConsoleContainer_UnLoaded; // Use more verbose null-checking syntax to avoid ISB001 misfiring. if (_solutionManager != null) @@ -106,7 +98,12 @@ public void Dispose() void ConsoleContainer_Loaded(object sender, RoutedEventArgs e) { - NuGetPowerShellUsage.RaisePMCWindowsEvent(null); + NuGetPowerShellUsage.RaisePMCWindowsLoadEvent(true); + } + + void ConsoleContainer_UnLoaded(object sender, RoutedEventArgs e) + { + NuGetPowerShellUsage.RaisePMCWindowsLoadEvent(false); } } } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs index 3a4978834dd..50fb78b1425 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs @@ -202,23 +202,17 @@ internal void AddInitPs1LoadData(bool isPMC, NuGetPowershellVSSolutionCloseEvent } } - private void NuGetPowerShellUsage_PMCWindowsEventHandler(bool? reOpen) + private void NuGetPowerShellUsage_PMCWindowsEventHandler(bool isLoad) { - AddPMCWindowsEventData(reOpen); + AddPMCWindowsEventData(isLoad); } - internal void AddPMCWindowsEventData(bool? reOpen) + internal void AddPMCWindowsEventData(bool isLoad) { lock (_lock) { - if (reOpen.HasValue) - { - _vsInstanceData._reOpenAtStart = reOpen.Value; - } - else - { - _vsSolutionData._nuGetPMCWindowLoadCount++; - } + _vsSolutionData._nuGetPMCWindowLoadCount++; + _vsInstanceData._reOpenAtStart = isLoad; } } @@ -317,6 +311,7 @@ public void Dispose() NuGetPowerShellUsage.PowerShellLoadEvent -= NuGetPowerShellUsage_PMCLoadEventHandler; NuGetPowerShellUsage.PowerShellCommandExecuteEvent -= NuGetPowerShellUsage_PowerShellCommandExecuteEvent; NuGetPowerShellUsage.InitPs1LoadEvent -= NuGetPowerShellUsage_InitPs1LoadEvent; + NuGetPowerShellUsage.PMCWindowsEvent -= NuGetPowerShellUsage_PMCWindowsEventHandler; NuGetPowerShellUsage.SolutionOpenEvent -= NuGetPowerShellUsage_SolutionOpenHandler; NuGetPowerShellUsage.SolutionCloseEvent -= NuGetPowerShellUsage_SolutionCloseHandler; NuGetPowerShellUsage.VSInstanceCloseEvent -= NuGetPowerShellUsage_VSInstanseCloseHandler; @@ -356,7 +351,7 @@ internal NuGetPowershellVSSolutionCloseEvent() internal TelemetryEvent ToTelemetryEvent() { - var telemetry = new TelemetryEvent(nameof(NuGetPowershellVSSolutionCloseEvent), + var telemetry = new TelemetryEvent(NuGetVSSolutionClose, new Dictionary() { { NuGetPowershellPrefix + FirstTimeLoadedFromPMC , _firstTimeLoadedFromPMC }, @@ -400,7 +395,7 @@ internal NuGetPowershellVSInstanceCloseEvent() internal TelemetryEvent ToTelemetryEvent() { - var telemetry = new TelemetryEvent(nameof(NuGetPowershellVSInstanceCloseEvent), + var telemetry = new TelemetryEvent(NuGetVSInstanceClose, new Dictionary() { { NuGetPowershellPrefix + NuGetPMCExecuteCommandCount , _nugetPMCExecuteCommandCount }, diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs index 07e10a3e1e0..70c83d4a9ff 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs @@ -23,7 +23,7 @@ public static class NuGetPowerShellUsage public delegate void InitPs1LoadEventHandler(bool isPMC); public static event InitPs1LoadEventHandler InitPs1LoadEvent; - public delegate void PMCWindowEventHandler(bool? reOpen); + public delegate void PMCWindowEventHandler(bool isLoad); public static event PMCWindowEventHandler PMCWindowsEvent; public static void RaisePowerShellLoadEvent(bool isPMC) @@ -41,9 +41,9 @@ public static void RaisInitPs1LoadEvent(bool isPMC) InitPs1LoadEvent?.Invoke(isPMC); } - public static void RaisePMCWindowsEvent(bool? reOpen) + public static void RaisePMCWindowsLoadEvent(bool isLoad) { - PMCWindowsEvent?.Invoke(reOpen); + PMCWindowsEvent?.Invoke(isLoad); } public static void RaiseSolutionOpenEvent() From d0574537ad69de14848565172e4e98c342834496 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Wed, 20 Jan 2021 18:24:26 -0800 Subject: [PATCH 49/59] Correct window count logic. --- .../Telemetry/NuGetPowerShellUsageCollector.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs index 50fb78b1425..04f8940a8f9 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs @@ -82,6 +82,7 @@ internal void AddPowerShellLoadedData(bool isPMC, NuGetPowershellVSSolutionClose { NuGetPowershellPrefix + LoadedFromPMC, isPMC } }); + // Telemetry service is not ready then delay for while. if (TelemetryActivity.NuGetTelemetryService == null) { _powerShellLoadEvent = telemetryEvent; @@ -211,7 +212,12 @@ internal void AddPMCWindowsEventData(bool isLoad) { lock (_lock) { - _vsSolutionData._nuGetPMCWindowLoadCount++; + if (isLoad) + { + _vsSolutionData._nuGetPMCWindowLoadCount++; + } + + // Shutdown call happen before Unload event for PMCWindow so VSInstanceClose event going to have current up to date status. _vsInstanceData._reOpenAtStart = isLoad; } } From f25af0ed2504916d4884a8babf7626d10fa4e80c Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Wed, 20 Jan 2021 19:05:00 -0800 Subject: [PATCH 50/59] Improve formatting and fix PMC, PMUI loaded not set from after load from init.ps1 --- .../NuGetPowerShellUsageCollector.cs | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs index 04f8940a8f9..a8e4b347f17 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs @@ -195,10 +195,12 @@ internal void AddInitPs1LoadData(bool isPMC, NuGetPowershellVSSolutionCloseEvent if (isPMC) { vsSolutionData._initPs1LoadPMC = true; + vsSolutionData._loadedFromPMC = true; } else { vsSolutionData._initPs1LoadPMUI = true; + vsSolutionData._loadedFromPMUI = true; } } } @@ -358,21 +360,21 @@ internal NuGetPowershellVSSolutionCloseEvent() internal TelemetryEvent ToTelemetryEvent() { var telemetry = new TelemetryEvent(NuGetVSSolutionClose, - new Dictionary() - { - { NuGetPowershellPrefix + FirstTimeLoadedFromPMC , _firstTimeLoadedFromPMC }, - { NuGetPowershellPrefix + FirstTimeLoadedFromPMUI , _firstTimeLoadedFromPMUI }, - { NuGetPowershellPrefix + InitPs1LoadedFromPMCFirst , _initPs1LoadedFromPMCFirst }, - { NuGetPowershellPrefix + InitPs1LoadPMC , _initPs1LoadPMC }, - { NuGetPowershellPrefix + InitPs1LoadPMUI , _initPs1LoadPMUI }, - { NuGetPowershellPrefix + LoadedFromPMC , _loadedFromPMC }, - { NuGetPowershellPrefix + LoadedFromPMUI , _loadedFromPMUI }, - { NuGetPowershellPrefix + NuGetCommandUsed , _nuGetCommandUsed }, - { NuGetPowershellPrefix + NuGetPMCExecuteCommandCount , _nuGetPMCExecuteCommandCount }, - { NuGetPowershellPrefix + NuGetPMCWindowLoadCount , _nuGetPMCWindowLoadCount }, - { NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount , _nuGetPMUIExecuteCommandCount }, - { NuGetPowershellPrefix + SolutionLoaded , _solutionLoaded } - }); + new Dictionary() + { + { NuGetPowershellPrefix + FirstTimeLoadedFromPMC , _firstTimeLoadedFromPMC }, + { NuGetPowershellPrefix + FirstTimeLoadedFromPMUI , _firstTimeLoadedFromPMUI }, + { NuGetPowershellPrefix + InitPs1LoadedFromPMCFirst , _initPs1LoadedFromPMCFirst }, + { NuGetPowershellPrefix + InitPs1LoadPMC , _initPs1LoadPMC }, + { NuGetPowershellPrefix + InitPs1LoadPMUI , _initPs1LoadPMUI }, + { NuGetPowershellPrefix + LoadedFromPMC , _loadedFromPMC }, + { NuGetPowershellPrefix + LoadedFromPMUI , _loadedFromPMUI }, + { NuGetPowershellPrefix + NuGetCommandUsed , _nuGetCommandUsed }, + { NuGetPowershellPrefix + NuGetPMCExecuteCommandCount , _nuGetPMCExecuteCommandCount }, + { NuGetPowershellPrefix + NuGetPMCWindowLoadCount , _nuGetPMCWindowLoadCount }, + { NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount , _nuGetPMUIExecuteCommandCount }, + { NuGetPowershellPrefix + SolutionLoaded , _solutionLoaded } + }); return telemetry; } @@ -402,16 +404,16 @@ internal NuGetPowershellVSInstanceCloseEvent() internal TelemetryEvent ToTelemetryEvent() { var telemetry = new TelemetryEvent(NuGetVSInstanceClose, - new Dictionary() - { - { NuGetPowershellPrefix + NuGetPMCExecuteCommandCount , _nugetPMCExecuteCommandCount }, - { NuGetPowershellPrefix + NuGetPMCWindowLoadCount , _nugetPMCWindowLoadCount }, - { NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount , _nugetPMUIExecuteCommandCount }, - { NuGetPowershellPrefix + PMCPowerShellLoadedSolutionCount , _pmcLoadedSolutionCount }, - { NuGetPowershellPrefix + PMUIPowerShellLoadedSolutionCount , _pmuiLoadedSolutionCount }, - { NuGetPowershellPrefix + ReOpenAtStart , _reOpenAtStart }, - { NuGetPowershellPrefix + SolutionCount , _solutionCount }, - }); + new Dictionary() + { + { NuGetPowershellPrefix + NuGetPMCExecuteCommandCount , _nugetPMCExecuteCommandCount }, + { NuGetPowershellPrefix + NuGetPMCWindowLoadCount , _nugetPMCWindowLoadCount }, + { NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount , _nugetPMUIExecuteCommandCount }, + { NuGetPowershellPrefix + PMCPowerShellLoadedSolutionCount , _pmcLoadedSolutionCount }, + { NuGetPowershellPrefix + PMUIPowerShellLoadedSolutionCount , _pmuiLoadedSolutionCount }, + { NuGetPowershellPrefix + ReOpenAtStart , _reOpenAtStart }, + { NuGetPowershellPrefix + SolutionCount , _solutionCount }, + }); return telemetry; } From eaa9b9faec3781203a415b3018ff20a0a51bd380 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Wed, 20 Jan 2021 20:49:17 -0800 Subject: [PATCH 51/59] Correct solution count. --- .../IDE/VSSolutionManager.cs | 25 ++++++++++++++++++- src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs | 3 --- .../NuGetPowerShellUsageCollector.cs | 11 ++++---- .../PowerShellHost.cs | 6 ----- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs index b9d94e4d944..d71127ae18b 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs @@ -26,6 +26,8 @@ using NuGet.Protocol; using NuGet.Protocol.Core.Types; using NuGet.VisualStudio; +using NuGet.VisualStudio.Telemetry; +using NuGet.VisualStudio.Telemetry.Powershell; using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; using Task = System.Threading.Tasks.Task; @@ -34,7 +36,7 @@ namespace NuGet.PackageManagement.VisualStudio [Export(typeof(ISolutionManager))] [Export(typeof(IVsSolutionManager))] [PartCreationPolicy(CreationPolicy.Shared)] - public sealed class VSSolutionManager : IVsSolutionManager, IVsSelectionEvents + public sealed class VSSolutionManager : IVsSolutionManager, IVsSelectionEvents, IDisposable { private static readonly INuGetProjectContext EmptyNuGetProjectContext = new EmptyNuGetProjectContext(); private static readonly string VSNuGetClientName = "NuGet VS VSIX"; @@ -165,6 +167,9 @@ internal VSSolutionManager( _logger = logger; _settings = settings; _initLock = new NuGetLockService(joinableTaskContext); + + SolutionOpened += OnSolutionOpened; + SolutionClosing += OnSolutionClosing; } private async Task InitializeAsync() @@ -216,6 +221,8 @@ private async Task InitializeAsync() _solutionSaveAsEvent.AfterExecute += SolutionSaveAs_AfterExecute; _projectSystemCache.CacheUpdated += NuGetCacheUpdate_After; + + NuGetPowerShellUsageCollector nuGetPowerShellUsageCollectorInstance = await _asyncServiceProvider.GetServiceAsync(); } public async Task GetNuGetProjectAsync(string nuGetProjectSafeName) @@ -1027,6 +1034,22 @@ public async Task UpgradeProjectToPackageReferenceAsync(NuGetProje return nuGetProject; } + private void OnSolutionOpened(object sender, EventArgs e) + { + NuGetPowerShellUsage.RaiseSolutionOpenEvent(); + } + + private void OnSolutionClosing(object sender, EventArgs e) + { + NuGetPowerShellUsage.RaiseSolutionCloseEvent(); + } + + public void Dispose() + { + SolutionOpened -= OnSolutionOpened; + SolutionClosing -= OnSolutionClosing; + } + #endregion } } diff --git a/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs b/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs index 6be7cb2ef01..f7225c666d5 100644 --- a/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs +++ b/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs @@ -223,9 +223,6 @@ private async Task InitializeMEFAsync() if (await SolutionManager.Value.IsSolutionOpenAsync()) { await DeleteOnRestartManager.Value.DeleteMarkedPackageDirectoriesAsync(ProjectContext.Value); - - // Hook up solution events, check if PMC is used before without any solution. - NuGetPowerShellUsage.RaiseSolutionOpenEvent(); } IVsTrackProjectRetargeting vsTrackProjectRetargeting = await this.GetServiceAsync(); diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs index a8e4b347f17..b96d8333c01 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs @@ -235,13 +235,14 @@ private void NuGetPowerShellUsage_SolutionOpenHandler() } _solutionCount++; + _vsInstanceData._solutionCount = _solutionCount; // Edge case: PMC used without solution load if (!_vsSolutionData._solutionLoaded && _vsSolutionData._nuGetPMCExecuteCommandCount > 0) { // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event aggregation before loading a solution. TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); - UpdateVSInstanceData(); + IncrementUpdateVSInstanceData(); } ClearSolutionData(); @@ -255,7 +256,7 @@ private void NuGetPowerShellUsage_SolutionCloseHandler() { // Emit solution telemetry TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); - UpdateVSInstanceData(); + IncrementUpdateVSInstanceData(); ClearSolutionData(); } } @@ -275,7 +276,7 @@ private void NuGetPowerShellUsage_VSInstanseCloseHandler() { // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event aggregation before loading a solution. TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); - UpdateVSInstanceData(); + IncrementUpdateVSInstanceData(); } // Emit VS Instance telemetry @@ -295,7 +296,7 @@ private void ClearSolutionData() _vsSolutionData._loadedFromPMUI = pmuiPowershellLoad; } - private void UpdateVSInstanceData() + private void IncrementUpdateVSInstanceData() { _vsInstanceData._nugetPMCExecuteCommandCount += _vsSolutionData._nuGetPMCExecuteCommandCount; _vsInstanceData._nugetPMCWindowLoadCount += _vsSolutionData._nuGetPMCWindowLoadCount; @@ -310,8 +311,6 @@ private void UpdateVSInstanceData() { _vsInstanceData._pmuiLoadedSolutionCount++; } - - _vsInstanceData._solutionCount = _solutionCount; } public void Dispose() diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 0488092b9f5..d6c40209c25 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -336,12 +336,6 @@ public void Initialize(IConsole console) }; } - _solutionManager.Value.SolutionClosing += (o, e) => - { - // Hook up solution events, we emit telemetry data from current VS solution session. - NuGetPowerShellUsage.RaiseSolutionCloseEvent(); - }; - _solutionManager.Value.NuGetProjectAdded += (o, e) => UpdateWorkingDirectoryAndAvailableProjects(); _solutionManager.Value.NuGetProjectRenamed += (o, e) => UpdateWorkingDirectoryAndAvailableProjects(); _solutionManager.Value.NuGetProjectUpdated += (o, e) => UpdateWorkingDirectoryAndAvailableProjects(); From 9ab746d76105ca441a9ed614c3d82bdfeca2d6ec Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Thu, 21 Jan 2021 09:39:03 -0800 Subject: [PATCH 52/59] Address PR comment Nkolche. --- .../IDE/VSSolutionManager.cs | 26 ++++--------------- .../NuGetPowerShellUsageCollector.cs | 8 +++--- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs index d71127ae18b..7d7c98ecffa 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs @@ -36,7 +36,7 @@ namespace NuGet.PackageManagement.VisualStudio [Export(typeof(ISolutionManager))] [Export(typeof(IVsSolutionManager))] [PartCreationPolicy(CreationPolicy.Shared)] - public sealed class VSSolutionManager : IVsSolutionManager, IVsSelectionEvents, IDisposable + public sealed class VSSolutionManager : IVsSolutionManager, IVsSelectionEvents { private static readonly INuGetProjectContext EmptyNuGetProjectContext = new EmptyNuGetProjectContext(); private static readonly string VSNuGetClientName = "NuGet VS VSIX"; @@ -167,9 +167,6 @@ internal VSSolutionManager( _logger = logger; _settings = settings; _initLock = new NuGetLockService(joinableTaskContext); - - SolutionOpened += OnSolutionOpened; - SolutionClosing += OnSolutionClosing; } private async Task InitializeAsync() @@ -222,7 +219,7 @@ private async Task InitializeAsync() _projectSystemCache.CacheUpdated += NuGetCacheUpdate_After; - NuGetPowerShellUsageCollector nuGetPowerShellUsageCollectorInstance = await _asyncServiceProvider.GetServiceAsync(); + NuGetPowerShellUsageCollector nuGetPowerShellUsageCollectorInstance = ServiceLocator.GetInstance(); } public async Task GetNuGetProjectAsync(string nuGetProjectSafeName) @@ -506,6 +503,8 @@ private async Task OnSolutionExistsAndFullyLoadedAsync() SolutionOpening?.Invoke(this, EventArgs.Empty); + NuGetPowerShellUsage.RaiseSolutionOpenEvent(); + // although the SolutionOpened event fires, the solution may be only in memory (e.g. when // doing File - New File). In that case, we don't want to act on the event. if (!await IsSolutionOpenAsync()) @@ -533,6 +532,7 @@ private void OnAfterClosing() private void OnBeforeClosing() { + NuGetPowerShellUsage.RaiseSolutionCloseEvent(); SolutionClosing?.Invoke(this, EventArgs.Empty); } @@ -1034,22 +1034,6 @@ public async Task UpgradeProjectToPackageReferenceAsync(NuGetProje return nuGetProject; } - private void OnSolutionOpened(object sender, EventArgs e) - { - NuGetPowerShellUsage.RaiseSolutionOpenEvent(); - } - - private void OnSolutionClosing(object sender, EventArgs e) - { - NuGetPowerShellUsage.RaiseSolutionCloseEvent(); - } - - public void Dispose() - { - SolutionOpened -= OnSolutionOpened; - SolutionClosing -= OnSolutionClosing; - } - #endregion } } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs index b96d8333c01..0c604354e4e 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs @@ -228,15 +228,13 @@ private void NuGetPowerShellUsage_SolutionOpenHandler() { lock (_lock) { + // Emit previously not sent telemetry due to Telemetry service was not available yet. if (_powerShellLoadEvent != null) { TelemetryActivity.EmitTelemetryEvent(_powerShellLoadEvent); _powerShellLoadEvent = null; } - _solutionCount++; - _vsInstanceData._solutionCount = _solutionCount; - // Edge case: PMC used without solution load if (!_vsSolutionData._solutionLoaded && _vsSolutionData._nuGetPMCExecuteCommandCount > 0) { @@ -246,7 +244,6 @@ private void NuGetPowerShellUsage_SolutionOpenHandler() } ClearSolutionData(); - _vsSolutionData._solutionLoaded = true; } } @@ -254,6 +251,9 @@ private void NuGetPowerShellUsage_SolutionCloseHandler() { lock (_lock) { + _vsInstanceData._solutionCount = ++_solutionCount; + _vsSolutionData._solutionLoaded = true; + // Emit solution telemetry TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); IncrementUpdateVSInstanceData(); From 73fc122ec5de53a91caea4885bc83f2ca0979ed1 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Thu, 21 Jan 2021 14:10:29 -0800 Subject: [PATCH 53/59] Address remaining review comments from Andy. --- .../Xamls/ConsoleContainer.xaml.cs | 3 - .../NuGetPowerShellBaseCommand.cs | 4 + .../IDE/VSSolutionManager.cs | 4 - src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs | 9 +- .../NuGetPowerShellUsageCollector.cs | 328 ++++++++---------- .../Powershell/NuGetPowerShellUsage.cs | 14 +- .../PowerShellHost.cs | 7 +- 7 files changed, 162 insertions(+), 207 deletions(-) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index 23de6803bcf..91805df17dd 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -30,9 +30,6 @@ public ConsoleContainer() { InitializeComponent(); - NuGetPowerShellUsageCollector _nuGetPowerShellUsageCollector = ServiceLocator.GetInstance(); - Assumes.NotNull(_nuGetPowerShellUsageCollector); - Loaded += ConsoleContainer_Loaded; Unloaded += ConsoleContainer_UnLoaded; diff --git a/src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/NuGetPowerShellBaseCommand.cs b/src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/NuGetPowerShellBaseCommand.cs index 0b5917d81d5..855424c9f2d 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/NuGetPowerShellBaseCommand.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/NuGetPowerShellBaseCommand.cs @@ -28,6 +28,7 @@ using NuGet.Protocol.Core.Types; using NuGet.Versioning; using NuGet.VisualStudio; +using NuGet.VisualStudio.Telemetry.Powershell; using ExecutionContext = NuGet.ProjectManagement.ExecutionContext; namespace NuGet.PackageManagement.PowerShellCmdlets @@ -214,6 +215,9 @@ protected override sealed void ProcessRecord() stopWatch.Start(); try { + // Recond NuGetCmdlet executed + NuGetPowerShellUsage.RaiseNuGetCmdletExecutedEvent(); + ProcessRecordCore(); } catch (Exception ex) diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs index 7d7c98ecffa..32a51eacfba 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs @@ -186,8 +186,6 @@ private async Task InitializeAsync() }); }); - TelemetryActivity.NuGetTelemetryService = new NuGetVSTelemetryService(); - _vsMonitorSelection = await _asyncServiceProvider.GetServiceAsync(); var solutionLoadedGuid = VSConstants.UICONTEXT.SolutionExistsAndFullyLoaded_guid; @@ -218,8 +216,6 @@ private async Task InitializeAsync() _solutionSaveAsEvent.AfterExecute += SolutionSaveAs_AfterExecute; _projectSystemCache.CacheUpdated += NuGetCacheUpdate_After; - - NuGetPowerShellUsageCollector nuGetPowerShellUsageCollectorInstance = ServiceLocator.GetInstance(); } public async Task GetNuGetProjectAsync(string nuGetProjectSafeName) diff --git a/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs b/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs index f7225c666d5..c8882e6e66a 100644 --- a/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs +++ b/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs @@ -97,6 +97,7 @@ public sealed class NuGetPackage : AsyncPackage, IVsPackageExtensionProvider, IV private uint _solutionExistsCookie; private bool _powerConsoleCommandExecuting; private bool _initialized; + private NuGetPowerShellUsageCollector _nuGetPowerShellUsageCollector; public NuGetPackage() { @@ -140,15 +141,15 @@ public NuGetPackage() [Import] private Lazy ServiceBrokerProvider { get; set; } - [Import] - private Lazy NuGetPowerShellUsageCollector { get; set; } - /// /// Initialization of the package; this method is called right after the package is sited, so this is the place /// where you can put all the initialization code that rely on services provided by VisualStudio. /// protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) { + _nuGetPowerShellUsageCollector = new NuGetPowerShellUsageCollector(); + NuGet.Common.TelemetryActivity.NuGetTelemetryService = new NuGetVSTelemetryService(); + await base.InitializeAsync(cancellationToken, progress); // Add our command handlers for menu (commands must exist in the .vsct file) @@ -215,8 +216,6 @@ private async Task InitializeMEFAsync() SolutionManager.Value.NuGetProjectContext = ProjectContext.Value; } - NuGetPowerShellUsageCollector nuGetPowerShellUsageCollectorInstance = NuGetPowerShellUsageCollector.Value; - // when NuGet loads, if the current solution has some package // folders marked for deletion (because a previous uninstalltion didn't succeed), // delete them now. diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs index 0c604354e4e..d5e30682423 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs @@ -3,15 +3,12 @@ using System; using System.Collections.Generic; -using System.ComponentModel.Composition; using System.Linq; using NuGet.Common; using NuGet.VisualStudio.Telemetry.Powershell; namespace NuGet.VisualStudio.Telemetry { - [Export(typeof(NuGetPowerShellUsageCollector))] - [PartCreationPolicy(CreationPolicy.Shared)] public sealed class NuGetPowerShellUsageCollector : IDisposable { // PMC, PMUI powershell telemetry consts @@ -45,20 +42,20 @@ public sealed class NuGetPowerShellUsageCollector : IDisposable private int _solutionCount; // _vsSolutionData hold telemetry data for current VS solution session. - private NuGetPowershellVSSolutionCloseEvent _vsSolutionData; + private SolutionData _vsSolutionData; // _vsInstanceData hold telemetry for current VS instance session. - private readonly NuGetPowershellVSInstanceCloseEvent _vsInstanceData; - private TelemetryEvent _powerShellLoadEvent; + private readonly InstanceData _vsInstanceData; private object _lock = new object(); public NuGetPowerShellUsageCollector() { - _vsSolutionData = new NuGetPowershellVSSolutionCloseEvent(); - _vsInstanceData = new NuGetPowershellVSInstanceCloseEvent(); + _vsSolutionData = new SolutionData(); + _vsInstanceData = new InstanceData(); NuGetPowerShellUsage.PowerShellLoadEvent += NuGetPowerShellUsage_PMCLoadEventHandler; - NuGetPowerShellUsage.PowerShellCommandExecuteEvent += NuGetPowerShellUsage_PowerShellCommandExecuteEvent; - NuGetPowerShellUsage.InitPs1LoadEvent += NuGetPowerShellUsage_InitPs1LoadEvent; + NuGetPowerShellUsage.PowerShellCommandExecuteEvent += NuGetPowerShellUsage_PowerShellCommandExecuteEventHandler; + NuGetPowerShellUsage.NuGetCmdletExecutedEvent += NuGetPowerShellUsage_NuGetCmdletExecutedEventHandler; + NuGetPowerShellUsage.InitPs1LoadEvent += NuGetPowerShellUsage_InitPs1LoadEventHandler; NuGetPowerShellUsage.PMCWindowsEvent += NuGetPowerShellUsage_PMCWindowsEventHandler; NuGetPowerShellUsage.SolutionOpenEvent += NuGetPowerShellUsage_SolutionOpenHandler; NuGetPowerShellUsage.SolutionCloseEvent += NuGetPowerShellUsage_SolutionCloseHandler; @@ -70,137 +67,125 @@ private void NuGetPowerShellUsage_PMCLoadEventHandler(bool isPMC) AddPowerShellLoadedData(isPMC, _vsSolutionData); } - internal void AddPowerShellLoadedData(bool isPMC, NuGetPowershellVSSolutionCloseEvent vsSolutionData) + internal void AddPowerShellLoadedData(bool isPMC, SolutionData vsSolutionData) { lock (_lock) { // PowerShellHost loaded first time, let's emit this to find out later how many VS instance crash after loading powershell. - if (!vsSolutionData._loadedFromPMC && !vsSolutionData._loadedFromPMUI) + if (!vsSolutionData.LoadedFromPMC && !vsSolutionData.LoadedFromPMUI) { var telemetryEvent = new TelemetryEvent(NuGetPowerShellLoaded, new Dictionary { { NuGetPowershellPrefix + LoadedFromPMC, isPMC } }); - // Telemetry service is not ready then delay for while. - if (TelemetryActivity.NuGetTelemetryService == null) - { - _powerShellLoadEvent = telemetryEvent; - } - else - { - TelemetryActivity.EmitTelemetryEvent(telemetryEvent); - } + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); } if (isPMC) { - if (!vsSolutionData._loadedFromPMC) + if (!vsSolutionData.LoadedFromPMC) { - vsSolutionData._firstTimeLoadedFromPMC = true; + vsSolutionData.FirstTimeLoadedFromPMC = true; + _vsInstanceData.PMCLoadedSolutionCount++; } - vsSolutionData._loadedFromPMC = true; + vsSolutionData.LoadedFromPMC = true; } else { - if (!vsSolutionData._loadedFromPMUI) + if (!vsSolutionData.LoadedFromPMUI) { - vsSolutionData._loadedFromPMUI = true; + vsSolutionData.FirstTimeLoadedFromPMUI = true; + _vsInstanceData.PMUILoadedSolutionCount++; } - vsSolutionData._loadedFromPMUI = true; + vsSolutionData.LoadedFromPMUI = true; } } } - private void NuGetPowerShellUsage_PowerShellCommandExecuteEvent(bool isPMC, string commandStr) + private void NuGetPowerShellUsage_PowerShellCommandExecuteEventHandler(bool isPMC) { - AddPowerShellCommandExecuteData(isPMC, commandStr, _vsSolutionData); + AddPowerShellCommandExecuteData(isPMC, _vsSolutionData); } - internal void AddPowerShellCommandExecuteData(bool isPMC, string commandStr, NuGetPowershellVSSolutionCloseEvent vsSolutionData) + internal void AddPowerShellCommandExecuteData(bool isPMC, SolutionData vsSolutionData) { lock (_lock) { + // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files if (isPMC) { - { - // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files - // For PMC all installation done in one pass so no double counting. - vsSolutionData._nuGetPMCExecuteCommandCount++; - } + // For PMC all installation done in one pass so no double counting. + vsSolutionData.PMCExecuteCommandCount++; + _vsInstanceData.PMCExecuteCommandCount++; } else { - // Please note: Direct PMC and PMUI don't share same code path for installing packages with *.ps1 files // This one is called for both init.ps1 and install.ps1 seperately. - // For MSBuildNuGetProject projects install.ps1 can even further increase duplicate counting: See MSBuildNuGetProject.cs#L377 - L396 - // Also this concern valid for dependent packages with *.ps1 files. - vsSolutionData._nuGetPMUIExecuteCommandCount++; - } - - // Check and add to telemetery if a command is NugetCommand like 'install-package' etc. - if (vsSolutionData._nuGetCommandUsed) - { - return; + // install.ps1 running inside MSBuildNuGetProject.cs (InstallPackageAsync method) may result in duplicate counting. + // Also this concern valid for dependent packages (of installing package) with *.ps1 files. + vsSolutionData.PMUIExecuteCommandCount++; + _vsInstanceData.PMUIExecuteCommandCount++; } + } + } - if (!string.IsNullOrWhiteSpace(commandStr)) - { - string command = commandStr.Trim().ToUpperInvariant(); - string[] commandParts = command.Split(' '); - if (commandParts.Count() > 1) - { - command = commandParts[0]; - } + private void NuGetPowerShellUsage_NuGetCmdletExecutedEventHandler() + { + AddNuGetCmdletExecutedData(_vsSolutionData); + } - switch (command) - { - case "GET-HELP": - case "FIND-PACKAGE": - case "GET-PACKAGE": - case "INSTALL-PACKAGE": - case "UNINSTALL-PACKAGE": - case "UPDATE-PACKAGE": - case "SYNC-PACKAGE": - case "ADD-BINDINGREDIRECT": - case "GET-PROJECT": - case "REGISTER-TABEXPANSION": - // Nuget Command executed - vsSolutionData._nuGetCommandUsed = true; - break; - } - } + internal void AddNuGetCmdletExecutedData(SolutionData vsSolutionData) + { + lock (_lock) + { + // NugetCommand like 'install-package' etc executed + vsSolutionData.NuGetCommandUsed = true; } } - private void NuGetPowerShellUsage_InitPs1LoadEvent(bool isPMC) + private void NuGetPowerShellUsage_InitPs1LoadEventHandler(bool isPMC) { AddInitPs1LoadData(isPMC, _vsSolutionData); } - internal void AddInitPs1LoadData(bool isPMC, NuGetPowershellVSSolutionCloseEvent vsSolutionData) + internal void AddInitPs1LoadData(bool isPMC, SolutionData vsSolutionData) { lock (_lock) { // Test bit flag for if init.ps1 already loaded from PMC or PMUI - if (isPMC && (!vsSolutionData._firstTimeLoadedFromPMC && !vsSolutionData._firstTimeLoadedFromPMUI)) + if (isPMC && (!vsSolutionData.FirstTimeLoadedFromPMC && !vsSolutionData.FirstTimeLoadedFromPMUI)) { // if not then set initialization origin bit - vsSolutionData._initPs1LoadedFromPMCFirst = true; + vsSolutionData.InitPs1LoadedFromPMCFirst = true; } if (isPMC) { - vsSolutionData._initPs1LoadPMC = true; - vsSolutionData._loadedFromPMC = true; + vsSolutionData.InitPs1LoadPMC = true; + + if (!vsSolutionData.LoadedFromPMC) + { + vsSolutionData.FirstTimeLoadedFromPMC = true; + _vsInstanceData.PMCLoadedSolutionCount++; + } + + vsSolutionData.LoadedFromPMC = true; } else { - vsSolutionData._initPs1LoadPMUI = true; - vsSolutionData._loadedFromPMUI = true; + vsSolutionData.InitPs1LoadPMUI = true; + + if (!vsSolutionData.LoadedFromPMUI) + { + vsSolutionData.FirstTimeLoadedFromPMUI = true; + _vsInstanceData.PMUILoadedSolutionCount++; + } + + vsSolutionData.LoadedFromPMUI = true; } } } @@ -216,11 +201,12 @@ internal void AddPMCWindowsEventData(bool isLoad) { if (isLoad) { - _vsSolutionData._nuGetPMCWindowLoadCount++; + _vsSolutionData.PMCWindowLoadCount++; + _vsInstanceData.PMCWindowLoadCount++; } // Shutdown call happen before Unload event for PMCWindow so VSInstanceClose event going to have current up to date status. - _vsInstanceData._reOpenAtStart = isLoad; + _vsInstanceData.ReOpenAtStart = isLoad; } } @@ -228,19 +214,11 @@ private void NuGetPowerShellUsage_SolutionOpenHandler() { lock (_lock) { - // Emit previously not sent telemetry due to Telemetry service was not available yet. - if (_powerShellLoadEvent != null) - { - TelemetryActivity.EmitTelemetryEvent(_powerShellLoadEvent); - _powerShellLoadEvent = null; - } - // Edge case: PMC used without solution load - if (!_vsSolutionData._solutionLoaded && _vsSolutionData._nuGetPMCExecuteCommandCount > 0) + if (!_vsSolutionData.SolutionLoaded && _vsSolutionData.PMCExecuteCommandCount > 0) { // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event aggregation before loading a solution. TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); - IncrementUpdateVSInstanceData(); } ClearSolutionData(); @@ -251,12 +229,11 @@ private void NuGetPowerShellUsage_SolutionCloseHandler() { lock (_lock) { - _vsInstanceData._solutionCount = ++_solutionCount; - _vsSolutionData._solutionLoaded = true; + _vsInstanceData.SolutionCount = ++_solutionCount; + _vsSolutionData.SolutionLoaded = true; // Emit solution telemetry TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); - IncrementUpdateVSInstanceData(); ClearSolutionData(); } } @@ -265,18 +242,11 @@ private void NuGetPowerShellUsage_VSInstanseCloseHandler() { lock (_lock) { - if (_powerShellLoadEvent != null) - { - TelemetryActivity.EmitTelemetryEvent(_powerShellLoadEvent); - _powerShellLoadEvent = null; - } - // Edge case: PMC used without solution load - if (!_vsSolutionData._solutionLoaded && _vsSolutionData._nuGetPMCExecuteCommandCount > 0) + if (!_vsSolutionData.SolutionLoaded && _vsSolutionData.PMCExecuteCommandCount > 0) { // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event aggregation before loading a solution. TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); - IncrementUpdateVSInstanceData(); } // Emit VS Instance telemetry @@ -287,72 +257,56 @@ private void NuGetPowerShellUsage_VSInstanseCloseHandler() //If open new solution then need to clear previous solution events. But powershell remain loaded in memory. private void ClearSolutionData() { - bool pmcPowershellLoad = _vsSolutionData._loadedFromPMC; - bool pmuiPowershellLoad = _vsSolutionData._loadedFromPMUI; - - _vsSolutionData = new NuGetPowershellVSSolutionCloseEvent(); + bool pmcPowershellLoad = _vsSolutionData.LoadedFromPMC; + bool pmuiPowershellLoad = _vsSolutionData.LoadedFromPMUI; - _vsSolutionData._loadedFromPMC = pmcPowershellLoad; - _vsSolutionData._loadedFromPMUI = pmuiPowershellLoad; - } - - private void IncrementUpdateVSInstanceData() - { - _vsInstanceData._nugetPMCExecuteCommandCount += _vsSolutionData._nuGetPMCExecuteCommandCount; - _vsInstanceData._nugetPMCWindowLoadCount += _vsSolutionData._nuGetPMCWindowLoadCount; - _vsInstanceData._nugetPMUIExecuteCommandCount += _vsSolutionData._nuGetPMUIExecuteCommandCount; + _vsSolutionData = new SolutionData(); - if (_vsSolutionData._loadedFromPMC) - { - _vsInstanceData._pmcLoadedSolutionCount++; - } - - if (_vsSolutionData._loadedFromPMUI) - { - _vsInstanceData._pmuiLoadedSolutionCount++; - } + _vsSolutionData.LoadedFromPMC = pmcPowershellLoad; + _vsSolutionData.LoadedFromPMUI = pmuiPowershellLoad; } public void Dispose() { NuGetPowerShellUsage.PowerShellLoadEvent -= NuGetPowerShellUsage_PMCLoadEventHandler; - NuGetPowerShellUsage.PowerShellCommandExecuteEvent -= NuGetPowerShellUsage_PowerShellCommandExecuteEvent; - NuGetPowerShellUsage.InitPs1LoadEvent -= NuGetPowerShellUsage_InitPs1LoadEvent; + NuGetPowerShellUsage.PowerShellCommandExecuteEvent -= NuGetPowerShellUsage_PowerShellCommandExecuteEventHandler; + NuGetPowerShellUsage.NuGetCmdletExecutedEvent -= NuGetPowerShellUsage_NuGetCmdletExecutedEventHandler; + NuGetPowerShellUsage.InitPs1LoadEvent -= NuGetPowerShellUsage_InitPs1LoadEventHandler; NuGetPowerShellUsage.PMCWindowsEvent -= NuGetPowerShellUsage_PMCWindowsEventHandler; NuGetPowerShellUsage.SolutionOpenEvent -= NuGetPowerShellUsage_SolutionOpenHandler; NuGetPowerShellUsage.SolutionCloseEvent -= NuGetPowerShellUsage_SolutionCloseHandler; NuGetPowerShellUsage.VSInstanceCloseEvent -= NuGetPowerShellUsage_VSInstanseCloseHandler; } - internal class NuGetPowershellVSSolutionCloseEvent + internal class SolutionData { - internal bool _firstTimeLoadedFromPMC; - internal bool _firstTimeLoadedFromPMUI; - internal bool _initPs1LoadedFromPMCFirst; - internal bool _initPs1LoadPMC; - internal bool _initPs1LoadPMUI; - internal bool _loadedFromPMC; - internal bool _loadedFromPMUI; - internal bool _nuGetCommandUsed; - internal int _nuGetPMCExecuteCommandCount; - internal int _nuGetPMCWindowLoadCount; - internal int _nuGetPMUIExecuteCommandCount; - internal bool _solutionLoaded; - - internal NuGetPowershellVSSolutionCloseEvent() + internal bool FirstTimeLoadedFromPMC { get; set; } + internal bool FirstTimeLoadedFromPMUI { get; set; } + internal bool InitPs1LoadedFromPMCFirst { get; set; } + internal bool InitPs1LoadPMC { get; set; } + internal bool InitPs1LoadPMUI { get; set; } + internal bool LoadedFromPMC { get; set; } + internal bool LoadedFromPMUI { get; set; } + internal bool NuGetCommandUsed { get; set; } + internal int PMCExecuteCommandCount { get; set; } + internal int PMCWindowLoadCount { get; set; } + internal int PMUIExecuteCommandCount { get; set; } + internal bool SolutionLoaded { get; set; } + + internal SolutionData() { - _firstTimeLoadedFromPMC = false; - _firstTimeLoadedFromPMUI = false; - _initPs1LoadedFromPMCFirst = false; - _initPs1LoadPMC = false; - _initPs1LoadPMUI = false; - _loadedFromPMC = false; - _loadedFromPMUI = false; - _nuGetCommandUsed = false; - _nuGetPMCExecuteCommandCount = 0; - _nuGetPMCWindowLoadCount = 0; - _nuGetPMUIExecuteCommandCount = 0; - _solutionLoaded = false; + FirstTimeLoadedFromPMC = false; + FirstTimeLoadedFromPMUI = false; + InitPs1LoadedFromPMCFirst = false; + InitPs1LoadPMC = false; + InitPs1LoadPMUI = false; + LoadedFromPMC = false; + LoadedFromPMUI = false; + NuGetCommandUsed = false; + PMCExecuteCommandCount = 0; + PMCWindowLoadCount = 0; + PMUIExecuteCommandCount = 0; + SolutionLoaded = false; } @@ -361,43 +315,43 @@ internal TelemetryEvent ToTelemetryEvent() var telemetry = new TelemetryEvent(NuGetVSSolutionClose, new Dictionary() { - { NuGetPowershellPrefix + FirstTimeLoadedFromPMC , _firstTimeLoadedFromPMC }, - { NuGetPowershellPrefix + FirstTimeLoadedFromPMUI , _firstTimeLoadedFromPMUI }, - { NuGetPowershellPrefix + InitPs1LoadedFromPMCFirst , _initPs1LoadedFromPMCFirst }, - { NuGetPowershellPrefix + InitPs1LoadPMC , _initPs1LoadPMC }, - { NuGetPowershellPrefix + InitPs1LoadPMUI , _initPs1LoadPMUI }, - { NuGetPowershellPrefix + LoadedFromPMC , _loadedFromPMC }, - { NuGetPowershellPrefix + LoadedFromPMUI , _loadedFromPMUI }, - { NuGetPowershellPrefix + NuGetCommandUsed , _nuGetCommandUsed }, - { NuGetPowershellPrefix + NuGetPMCExecuteCommandCount , _nuGetPMCExecuteCommandCount }, - { NuGetPowershellPrefix + NuGetPMCWindowLoadCount , _nuGetPMCWindowLoadCount }, - { NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount , _nuGetPMUIExecuteCommandCount }, - { NuGetPowershellPrefix + SolutionLoaded , _solutionLoaded } + { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.FirstTimeLoadedFromPMC , FirstTimeLoadedFromPMC }, + { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.FirstTimeLoadedFromPMUI , FirstTimeLoadedFromPMUI }, + { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.InitPs1LoadedFromPMCFirst , InitPs1LoadedFromPMCFirst }, + { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.InitPs1LoadPMC , InitPs1LoadPMC }, + { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.InitPs1LoadPMUI , InitPs1LoadPMUI }, + { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.LoadedFromPMC , LoadedFromPMC }, + { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.LoadedFromPMUI , LoadedFromPMUI }, + { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.NuGetCommandUsed , NuGetCommandUsed }, + { NuGetPowershellPrefix + NuGetPMCExecuteCommandCount , PMCExecuteCommandCount }, + { NuGetPowershellPrefix + NuGetPMCWindowLoadCount , PMCWindowLoadCount }, + { NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount , PMUIExecuteCommandCount }, + { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.SolutionLoaded , SolutionLoaded } }); return telemetry; } } - internal class NuGetPowershellVSInstanceCloseEvent + internal class InstanceData { - internal int _nugetPMCExecuteCommandCount; - internal int _nugetPMCWindowLoadCount; - internal int _nugetPMUIExecuteCommandCount; - internal int _pmcLoadedSolutionCount; - internal int _pmuiLoadedSolutionCount; - internal bool _reOpenAtStart; - internal int _solutionCount; - - internal NuGetPowershellVSInstanceCloseEvent() + internal int PMCExecuteCommandCount { get; set; } + internal int PMCWindowLoadCount { get; set; } + internal int PMUIExecuteCommandCount { get; set; } + internal int PMCLoadedSolutionCount { get; set; } + internal int PMUILoadedSolutionCount { get; set; } + internal bool ReOpenAtStart { get; set; } + internal int SolutionCount { get; set; } + + internal InstanceData() { - _nugetPMCExecuteCommandCount = 0; - _nugetPMCWindowLoadCount = 0; - _nugetPMUIExecuteCommandCount = 0; - _pmcLoadedSolutionCount = 0; - _pmuiLoadedSolutionCount = 0; - _reOpenAtStart = false; - _solutionCount = 0; + PMCExecuteCommandCount = 0; + PMCWindowLoadCount = 0; + PMUIExecuteCommandCount = 0; + PMCLoadedSolutionCount = 0; + PMUILoadedSolutionCount = 0; + ReOpenAtStart = false; + SolutionCount = 0; } internal TelemetryEvent ToTelemetryEvent() @@ -405,13 +359,13 @@ internal TelemetryEvent ToTelemetryEvent() var telemetry = new TelemetryEvent(NuGetVSInstanceClose, new Dictionary() { - { NuGetPowershellPrefix + NuGetPMCExecuteCommandCount , _nugetPMCExecuteCommandCount }, - { NuGetPowershellPrefix + NuGetPMCWindowLoadCount , _nugetPMCWindowLoadCount }, - { NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount , _nugetPMUIExecuteCommandCount }, - { NuGetPowershellPrefix + PMCPowerShellLoadedSolutionCount , _pmcLoadedSolutionCount }, - { NuGetPowershellPrefix + PMUIPowerShellLoadedSolutionCount , _pmuiLoadedSolutionCount }, - { NuGetPowershellPrefix + ReOpenAtStart , _reOpenAtStart }, - { NuGetPowershellPrefix + SolutionCount , _solutionCount }, + { NuGetPowershellPrefix + NuGetPMCExecuteCommandCount , PMCExecuteCommandCount }, + { NuGetPowershellPrefix + NuGetPMCWindowLoadCount , PMCWindowLoadCount }, + { NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount , PMUIExecuteCommandCount }, + { NuGetPowershellPrefix + PMCPowerShellLoadedSolutionCount , PMCLoadedSolutionCount }, + { NuGetPowershellPrefix + PMUIPowerShellLoadedSolutionCount , PMUILoadedSolutionCount }, + { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.ReOpenAtStart , ReOpenAtStart }, + { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.SolutionCount , SolutionCount }, }); return telemetry; diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs index 70c83d4a9ff..f85c60fdff2 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs @@ -17,9 +17,12 @@ public static class NuGetPowerShellUsage public delegate void PowerShellLoadEventHandler(bool isPMC); public static event PowerShellLoadEventHandler PowerShellLoadEvent; - public delegate void PowerShellCommandExecuteEventHandler(bool isPMC, string commandStr); + public delegate void PowerShellCommandExecuteEventHandler(bool isPMC); public static event PowerShellCommandExecuteEventHandler PowerShellCommandExecuteEvent; + public delegate void NuGetCmdletExecutedEventHandler(); + public static event NuGetCmdletExecutedEventHandler NuGetCmdletExecutedEvent; + public delegate void InitPs1LoadEventHandler(bool isPMC); public static event InitPs1LoadEventHandler InitPs1LoadEvent; @@ -31,9 +34,14 @@ public static void RaisePowerShellLoadEvent(bool isPMC) PowerShellLoadEvent?.Invoke(isPMC); } - public static void RaiseCommandExecuteEvent(bool isPMC, string commandStr) + public static void RaiseCommandExecuteEvent(bool isPMC) + { + PowerShellCommandExecuteEvent?.Invoke(isPMC); + } + + public static void RaiseNuGetCmdletExecutedEvent() { - PowerShellCommandExecuteEvent?.Invoke(isPMC, commandStr); + NuGetCmdletExecutedEvent?.Invoke(); } public static void RaisInitPs1LoadEvent(bool isPMC) diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index d6c40209c25..7dd77314db0 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -314,9 +314,6 @@ public void Initialize(IConsole console) UpdateWorkingDirectory(); // Emit first time Powershell load event to find out later how many VS instance crash after loading powershell. - NuGetPowerShellUsageCollector nuGetPowerShellUsageCollectorInstance = ServiceLocator.GetInstance(); - Assumes.NotNull(nuGetPowerShellUsageCollectorInstance); - NuGetPowerShellUsage.RaisePowerShellLoadEvent(console is IWpfConsole); await ExecuteInitScriptsAsync(); @@ -541,8 +538,8 @@ public bool Execute(IConsole console, string command, params object[] inputs) throw new ArgumentNullException(nameof(command)); } - // Increase command execution counters for PMC/PMUI - NuGetPowerShellUsage.RaiseCommandExecuteEvent(console is IWpfConsole, command); + // Record origin of powershell command + NuGetPowerShellUsage.RaiseCommandExecuteEvent(console is IWpfConsole); // since install.ps1/uninstall.ps1 could depend on init scripts, so we need to make sure // to run it once for each solution From ec4ae8d608efb9e241eb2cd3649798ae3b96980a Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Thu, 21 Jan 2021 15:18:56 -0800 Subject: [PATCH 54/59] Clean up fix and fix typos. --- .../NuGet.Console/Xamls/ConsoleContainer.xaml.cs | 1 - .../NuGetPowerShellBaseCommand.cs | 2 +- .../IDE/VSSolutionManager.cs | 2 +- .../Telemetry/NuGetPowerShellUsageCollector.cs | 10 ++-------- .../NuGetConsole.Host.PowerShell/PowerShellHost.cs | 3 +-- 5 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index 91805df17dd..d6258ea778e 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -14,7 +14,6 @@ using NuGet.VisualStudio; using NuGet.VisualStudio.Common; using NuGet.VisualStudio.Internal.Contracts; -using NuGet.VisualStudio.Telemetry; using NuGet.VisualStudio.Telemetry.Powershell; namespace NuGetConsole diff --git a/src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/NuGetPowerShellBaseCommand.cs b/src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/NuGetPowerShellBaseCommand.cs index 855424c9f2d..ea8e72f4bfa 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/NuGetPowerShellBaseCommand.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/NuGetPowerShellBaseCommand.cs @@ -215,7 +215,7 @@ protected override sealed void ProcessRecord() stopWatch.Start(); try { - // Recond NuGetCmdlet executed + // Record NuGetCmdlet executed NuGetPowerShellUsage.RaiseNuGetCmdletExecutedEvent(); ProcessRecordCore(); diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs index 32a51eacfba..cadab521e6e 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs @@ -26,7 +26,6 @@ using NuGet.Protocol; using NuGet.Protocol.Core.Types; using NuGet.VisualStudio; -using NuGet.VisualStudio.Telemetry; using NuGet.VisualStudio.Telemetry.Powershell; using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; using Task = System.Threading.Tasks.Task; @@ -529,6 +528,7 @@ private void OnAfterClosing() private void OnBeforeClosing() { NuGetPowerShellUsage.RaiseSolutionCloseEvent(); + SolutionClosing?.Invoke(this, EventArgs.Empty); } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs index d5e30682423..4cbc0237794 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using NuGet.Common; using NuGet.VisualStudio.Telemetry.Powershell; @@ -23,16 +22,13 @@ public sealed class NuGetPowerShellUsageCollector : IDisposable public const string LoadedFromPMC = nameof(LoadedFromPMC); public const string FirstTimeLoadedFromPMC = nameof(FirstTimeLoadedFromPMC); public const string SolutionLoaded = nameof(SolutionLoaded); - public const string PowerShellExecuteCommand = nameof(PowerShellExecuteCommand); public const string NuGetPowerShellLoaded = nameof(NuGetPowerShellLoaded); // PMC UI Console Container telemetry consts - public const string PackageManagerConsoleWindowsLoad = nameof(PackageManagerConsoleWindowsLoad); public const string NuGetPMCWindowLoadCount = nameof(NuGetPMCWindowLoadCount); public const string ReOpenAtStart = nameof(ReOpenAtStart); // Const name for emitting when VS solution close or VS instance close. - public const string Name = nameof(Name); public const string NuGetPowershellPrefix = "NuGetPowershellPrefix."; // Using prefix prevent accidental same name property collission from different type telemetry. public const string NuGetVSSolutionClose = nameof(NuGetVSSolutionClose); public const string NuGetVSInstanceClose = nameof(NuGetVSInstanceClose); @@ -156,10 +152,8 @@ internal void AddInitPs1LoadData(bool isPMC, SolutionData vsSolutionData) { lock (_lock) { - // Test bit flag for if init.ps1 already loaded from PMC or PMUI if (isPMC && (!vsSolutionData.FirstTimeLoadedFromPMC && !vsSolutionData.FirstTimeLoadedFromPMUI)) { - // if not then set initialization origin bit vsSolutionData.InitPs1LoadedFromPMCFirst = true; } @@ -217,7 +211,7 @@ private void NuGetPowerShellUsage_SolutionOpenHandler() // Edge case: PMC used without solution load if (!_vsSolutionData.SolutionLoaded && _vsSolutionData.PMCExecuteCommandCount > 0) { - // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event aggregation before loading a solution. + // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event. TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); } @@ -245,7 +239,7 @@ private void NuGetPowerShellUsage_VSInstanseCloseHandler() // Edge case: PMC used without solution load if (!_vsSolutionData.SolutionLoaded && _vsSolutionData.PMCExecuteCommandCount > 0) { - // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event aggregation before loading a solution. + // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event. TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); } diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 7dd77314db0..4c6c89715e3 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -318,7 +318,7 @@ public void Initialize(IConsole console) await ExecuteInitScriptsAsync(); - // check if PMC console is actually opened, then only hook to solution load events. + // check if PMC console is actually opened, then only hook to solution load/close events. if (console is IWpfConsole) { // Hook up solution events @@ -332,7 +332,6 @@ public void Initialize(IConsole console) NuGetUIThreadHelper.JoinableTaskFactory.Run(CommandUiUtilities.InvalidateDefaultProjectAsync); }; } - _solutionManager.Value.NuGetProjectAdded += (o, e) => UpdateWorkingDirectoryAndAvailableProjects(); _solutionManager.Value.NuGetProjectRenamed += (o, e) => UpdateWorkingDirectoryAndAvailableProjects(); _solutionManager.Value.NuGetProjectUpdated += (o, e) => UpdateWorkingDirectoryAndAvailableProjects(); From 9179b9c30c15974a8b8b7e503a73f83140c53b34 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Thu, 21 Jan 2021 15:30:22 -0800 Subject: [PATCH 55/59] Fix if PMC opened without solution then that window load is not counted. --- .../Telemetry/NuGetPowerShellUsageCollector.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs index 4cbc0237794..7ccfc9ddb42 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs @@ -253,11 +253,13 @@ private void ClearSolutionData() { bool pmcPowershellLoad = _vsSolutionData.LoadedFromPMC; bool pmuiPowershellLoad = _vsSolutionData.LoadedFromPMUI; + int pmcWindowLoadCount = _vsSolutionData.PMCWindowLoadCount; _vsSolutionData = new SolutionData(); _vsSolutionData.LoadedFromPMC = pmcPowershellLoad; _vsSolutionData.LoadedFromPMUI = pmuiPowershellLoad; + _vsSolutionData.PMCWindowLoadCount = pmcWindowLoadCount; } public void Dispose() From 99b450d1ce86b6de33abb8013a341ac0341d7a19 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Thu, 21 Jan 2021 18:56:19 -0800 Subject: [PATCH 56/59] Address PR comments by Fernando. --- .../Xamls/ConsoleContainer.xaml.cs | 6 +- .../NuGetPowerShellBaseCommand.cs | 2 +- .../IDE/VSSolutionManager.cs | 2 +- src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs | 2 +- .../NuGetPowerShellUsageCollector.cs | 84 ++++++++++--------- .../Powershell/NuGetPowerShellUsage.cs | 2 +- .../PowerShellHost.cs | 8 +- 7 files changed, 57 insertions(+), 49 deletions(-) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index d6258ea778e..636e5cca585 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -14,7 +14,7 @@ using NuGet.VisualStudio; using NuGet.VisualStudio.Common; using NuGet.VisualStudio.Internal.Contracts; -using NuGet.VisualStudio.Telemetry.Powershell; +using NuGet.VisualStudio.Telemetry.PowerShell; namespace NuGetConsole { @@ -94,12 +94,12 @@ public void Dispose() void ConsoleContainer_Loaded(object sender, RoutedEventArgs e) { - NuGetPowerShellUsage.RaisePMCWindowsLoadEvent(true); + NuGetPowerShellUsage.RaisePMCWindowsLoadEvent(isLoad: true); } void ConsoleContainer_UnLoaded(object sender, RoutedEventArgs e) { - NuGetPowerShellUsage.RaisePMCWindowsLoadEvent(false); + NuGetPowerShellUsage.RaisePMCWindowsLoadEvent(isLoad: false); } } } diff --git a/src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/NuGetPowerShellBaseCommand.cs b/src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/NuGetPowerShellBaseCommand.cs index ea8e72f4bfa..831d3e57341 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/NuGetPowerShellBaseCommand.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/NuGetPowerShellBaseCommand.cs @@ -28,7 +28,7 @@ using NuGet.Protocol.Core.Types; using NuGet.Versioning; using NuGet.VisualStudio; -using NuGet.VisualStudio.Telemetry.Powershell; +using NuGet.VisualStudio.Telemetry.PowerShell; using ExecutionContext = NuGet.ProjectManagement.ExecutionContext; namespace NuGet.PackageManagement.PowerShellCmdlets diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs index cadab521e6e..ffe312f6ad4 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs @@ -26,7 +26,7 @@ using NuGet.Protocol; using NuGet.Protocol.Core.Types; using NuGet.VisualStudio; -using NuGet.VisualStudio.Telemetry.Powershell; +using NuGet.VisualStudio.Telemetry.PowerShell; using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; using Task = System.Threading.Tasks.Task; diff --git a/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs b/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs index c8882e6e66a..e487567017f 100644 --- a/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs +++ b/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs @@ -28,7 +28,7 @@ using NuGet.VisualStudio.Common; using NuGet.VisualStudio.Internal.Contracts; using NuGet.VisualStudio.Telemetry; -using NuGet.VisualStudio.Telemetry.Powershell; +using NuGet.VisualStudio.Telemetry.PowerShell; using NuGetConsole; using NuGetConsole.Implementation; using ContractsNuGetServices = NuGet.VisualStudio.Contracts.NuGetServices; diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs index 7ccfc9ddb42..dd8e3886611 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs @@ -4,15 +4,15 @@ using System; using System.Collections.Generic; using NuGet.Common; -using NuGet.VisualStudio.Telemetry.Powershell; +using NuGet.VisualStudio.Telemetry.PowerShell; namespace NuGet.VisualStudio.Telemetry { public sealed class NuGetPowerShellUsageCollector : IDisposable { // PMC, PMUI powershell telemetry consts - public const string NuGetPMCExecuteCommandCount = nameof(NuGetPMCExecuteCommandCount); - public const string NuGetPMUIExecuteCommandCount = nameof(NuGetPMUIExecuteCommandCount); + public const string PMCExecuteCommandCount = nameof(PMCExecuteCommandCount); + public const string PMUIExecuteCommandCount = nameof(PMUIExecuteCommandCount); public const string NuGetCommandUsed = nameof(NuGetCommandUsed); public const string InitPs1LoadPMUI = nameof(InitPs1LoadPMUI); public const string InitPs1LoadPMC = nameof(InitPs1LoadPMC); @@ -22,16 +22,16 @@ public sealed class NuGetPowerShellUsageCollector : IDisposable public const string LoadedFromPMC = nameof(LoadedFromPMC); public const string FirstTimeLoadedFromPMC = nameof(FirstTimeLoadedFromPMC); public const string SolutionLoaded = nameof(SolutionLoaded); - public const string NuGetPowerShellLoaded = nameof(NuGetPowerShellLoaded); + public const string PowerShellLoaded = nameof(PowerShellLoaded); // PMC UI Console Container telemetry consts - public const string NuGetPMCWindowLoadCount = nameof(NuGetPMCWindowLoadCount); + public const string PMCWindowLoadCount = nameof(PMCWindowLoadCount); public const string ReOpenAtStart = nameof(ReOpenAtStart); // Const name for emitting when VS solution close or VS instance close. - public const string NuGetPowershellPrefix = "NuGetPowershellPrefix."; // Using prefix prevent accidental same name property collission from different type telemetry. - public const string NuGetVSSolutionClose = nameof(NuGetVSSolutionClose); - public const string NuGetVSInstanceClose = nameof(NuGetVSInstanceClose); + public const string PackageCommands = "PackageCommands."; // Using prefix prevent accidental same name property collission from different type telemetry. + public const string PowerShellVSSolutionClose = nameof(PowerShellVSSolutionClose); + public const string PowerShellVSInstanceClose = nameof(PowerShellVSInstanceClose); public const string SolutionCount = nameof(SolutionCount); public const string PMCPowerShellLoadedSolutionCount = nameof(PMCPowerShellLoadedSolutionCount); public const string PMUIPowerShellLoadedSolutionCount = nameof(PMUIPowerShellLoadedSolutionCount); @@ -70,9 +70,9 @@ internal void AddPowerShellLoadedData(bool isPMC, SolutionData vsSolutionData) // PowerShellHost loaded first time, let's emit this to find out later how many VS instance crash after loading powershell. if (!vsSolutionData.LoadedFromPMC && !vsSolutionData.LoadedFromPMUI) { - var telemetryEvent = new TelemetryEvent(NuGetPowerShellLoaded, new Dictionary + var telemetryEvent = new TelemetryEvent(PowerShellLoaded, new Dictionary { - { NuGetPowershellPrefix + LoadedFromPMC, isPMC } + { PackageCommands + LoadedFromPMC, isPMC } }); TelemetryActivity.EmitTelemetryEvent(telemetryEvent); @@ -138,7 +138,7 @@ internal void AddNuGetCmdletExecutedData(SolutionData vsSolutionData) { lock (_lock) { - // NugetCommand like 'install-package' etc executed + // NuGetCommand executed vsSolutionData.NuGetCommandUsed = true; } } @@ -213,9 +213,22 @@ private void NuGetPowerShellUsage_SolutionOpenHandler() { // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event. TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); + ClearSolutionData(); } - ClearSolutionData(); + if (_vsSolutionData.LoadedFromPMC) + { + _vsInstanceData.PMCLoadedSolutionCount++; + } + + if (_vsSolutionData.LoadedFromPMUI) + { + _vsInstanceData.PMUILoadedSolutionCount++; + } + + _vsInstanceData.SolutionCount = ++_solutionCount; + _vsSolutionData.SolutionLoaded = true; + } } @@ -223,9 +236,6 @@ private void NuGetPowerShellUsage_SolutionCloseHandler() { lock (_lock) { - _vsInstanceData.SolutionCount = ++_solutionCount; - _vsSolutionData.SolutionLoaded = true; - // Emit solution telemetry TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); ClearSolutionData(); @@ -253,13 +263,11 @@ private void ClearSolutionData() { bool pmcPowershellLoad = _vsSolutionData.LoadedFromPMC; bool pmuiPowershellLoad = _vsSolutionData.LoadedFromPMUI; - int pmcWindowLoadCount = _vsSolutionData.PMCWindowLoadCount; _vsSolutionData = new SolutionData(); _vsSolutionData.LoadedFromPMC = pmcPowershellLoad; _vsSolutionData.LoadedFromPMUI = pmuiPowershellLoad; - _vsSolutionData.PMCWindowLoadCount = pmcWindowLoadCount; } public void Dispose() @@ -308,21 +316,21 @@ internal SolutionData() internal TelemetryEvent ToTelemetryEvent() { - var telemetry = new TelemetryEvent(NuGetVSSolutionClose, + var telemetry = new TelemetryEvent(PowerShellVSSolutionClose, new Dictionary() { - { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.FirstTimeLoadedFromPMC , FirstTimeLoadedFromPMC }, - { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.FirstTimeLoadedFromPMUI , FirstTimeLoadedFromPMUI }, - { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.InitPs1LoadedFromPMCFirst , InitPs1LoadedFromPMCFirst }, - { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.InitPs1LoadPMC , InitPs1LoadPMC }, - { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.InitPs1LoadPMUI , InitPs1LoadPMUI }, - { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.LoadedFromPMC , LoadedFromPMC }, - { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.LoadedFromPMUI , LoadedFromPMUI }, - { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.NuGetCommandUsed , NuGetCommandUsed }, - { NuGetPowershellPrefix + NuGetPMCExecuteCommandCount , PMCExecuteCommandCount }, - { NuGetPowershellPrefix + NuGetPMCWindowLoadCount , PMCWindowLoadCount }, - { NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount , PMUIExecuteCommandCount }, - { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.SolutionLoaded , SolutionLoaded } + { PackageCommands + NuGetPowerShellUsageCollector.FirstTimeLoadedFromPMC , FirstTimeLoadedFromPMC }, + { PackageCommands + NuGetPowerShellUsageCollector.FirstTimeLoadedFromPMUI , FirstTimeLoadedFromPMUI }, + { PackageCommands + NuGetPowerShellUsageCollector.InitPs1LoadedFromPMCFirst , InitPs1LoadedFromPMCFirst }, + { PackageCommands + NuGetPowerShellUsageCollector.InitPs1LoadPMC , InitPs1LoadPMC }, + { PackageCommands + NuGetPowerShellUsageCollector.InitPs1LoadPMUI , InitPs1LoadPMUI }, + { PackageCommands + NuGetPowerShellUsageCollector.LoadedFromPMC , LoadedFromPMC }, + { PackageCommands + NuGetPowerShellUsageCollector.LoadedFromPMUI , LoadedFromPMUI }, + { PackageCommands + NuGetPowerShellUsageCollector.NuGetCommandUsed , NuGetCommandUsed }, + { PackageCommands + NuGetPowerShellUsageCollector.PMCExecuteCommandCount , PMCExecuteCommandCount }, + { PackageCommands + NuGetPowerShellUsageCollector.PMCWindowLoadCount , PMCWindowLoadCount }, + { PackageCommands + NuGetPowerShellUsageCollector.PMUIExecuteCommandCount , PMUIExecuteCommandCount }, + { PackageCommands + NuGetPowerShellUsageCollector.SolutionLoaded , SolutionLoaded } }); return telemetry; @@ -352,16 +360,16 @@ internal InstanceData() internal TelemetryEvent ToTelemetryEvent() { - var telemetry = new TelemetryEvent(NuGetVSInstanceClose, + var telemetry = new TelemetryEvent(PowerShellVSInstanceClose, new Dictionary() { - { NuGetPowershellPrefix + NuGetPMCExecuteCommandCount , PMCExecuteCommandCount }, - { NuGetPowershellPrefix + NuGetPMCWindowLoadCount , PMCWindowLoadCount }, - { NuGetPowershellPrefix + NuGetPMUIExecuteCommandCount , PMUIExecuteCommandCount }, - { NuGetPowershellPrefix + PMCPowerShellLoadedSolutionCount , PMCLoadedSolutionCount }, - { NuGetPowershellPrefix + PMUIPowerShellLoadedSolutionCount , PMUILoadedSolutionCount }, - { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.ReOpenAtStart , ReOpenAtStart }, - { NuGetPowershellPrefix + NuGetPowerShellUsageCollector.SolutionCount , SolutionCount }, + { PackageCommands + NuGetPowerShellUsageCollector.PMCExecuteCommandCount , PMCExecuteCommandCount }, + { PackageCommands + NuGetPowerShellUsageCollector.PMCWindowLoadCount , PMCWindowLoadCount }, + { PackageCommands + NuGetPowerShellUsageCollector.PMUIExecuteCommandCount , PMUIExecuteCommandCount }, + { PackageCommands + PMCPowerShellLoadedSolutionCount , PMCLoadedSolutionCount }, + { PackageCommands + PMUIPowerShellLoadedSolutionCount , PMUILoadedSolutionCount }, + { PackageCommands + NuGetPowerShellUsageCollector.ReOpenAtStart , ReOpenAtStart }, + { PackageCommands + NuGetPowerShellUsageCollector.SolutionCount , SolutionCount }, }); return telemetry; diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs index f85c60fdff2..320742f534d 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -namespace NuGet.VisualStudio.Telemetry.Powershell +namespace NuGet.VisualStudio.Telemetry.PowerShell { public static class NuGetPowerShellUsage { diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 4c6c89715e3..d302691566e 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -29,7 +29,7 @@ using NuGet.Protocol.Core.Types; using NuGet.VisualStudio; using NuGet.VisualStudio.Telemetry; -using NuGet.VisualStudio.Telemetry.Powershell; +using NuGet.VisualStudio.Telemetry.PowerShell; using Task = System.Threading.Tasks.Task; namespace NuGetConsole.Host.PowerShell.Implementation @@ -314,7 +314,7 @@ public void Initialize(IConsole console) UpdateWorkingDirectory(); // Emit first time Powershell load event to find out later how many VS instance crash after loading powershell. - NuGetPowerShellUsage.RaisePowerShellLoadEvent(console is IWpfConsole); + NuGetPowerShellUsage.RaisePowerShellLoadEvent(isPMC: console is IWpfConsole); await ExecuteInitScriptsAsync(); @@ -478,7 +478,7 @@ private async Task ExecuteInitPs1Async(string installPath, PackageIdentity ident if (File.Exists(scriptPath)) { // Record if init.ps1 is loaded. - NuGetPowerShellUsage.RaisInitPs1LoadEvent(_activeConsole is IWpfConsole); + NuGetPowerShellUsage.RaisInitPs1LoadEvent(isPMC: _activeConsole is IWpfConsole); if (_scriptExecutor.Value.TryMarkVisited(identity, PackageInitPS1State.FoundAndExecuted)) { @@ -538,7 +538,7 @@ public bool Execute(IConsole console, string command, params object[] inputs) } // Record origin of powershell command - NuGetPowerShellUsage.RaiseCommandExecuteEvent(console is IWpfConsole); + NuGetPowerShellUsage.RaiseCommandExecuteEvent(isPMC: console is IWpfConsole); // since install.ps1/uninstall.ps1 could depend on init scripts, so we need to make sure // to run it once for each solution From ccf74a045a66fcb398abcb96f26e86d3ea2dfdaa Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Thu, 21 Jan 2021 21:09:34 -0800 Subject: [PATCH 57/59] Address latest Andy's comments (removing prefix etc) --- .../NuGetPowerShellUsageCollector.cs | 53 +++++++------------ .../Powershell/NuGetPowerShellUsage.cs | 2 +- .../PowerShellHost.cs | 17 ++++-- 3 files changed, 33 insertions(+), 39 deletions(-) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs index dd8e3886611..e49a290190f 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs @@ -22,14 +22,12 @@ public sealed class NuGetPowerShellUsageCollector : IDisposable public const string LoadedFromPMC = nameof(LoadedFromPMC); public const string FirstTimeLoadedFromPMC = nameof(FirstTimeLoadedFromPMC); public const string SolutionLoaded = nameof(SolutionLoaded); - public const string PowerShellLoaded = nameof(PowerShellLoaded); // PMC UI Console Container telemetry consts public const string PMCWindowLoadCount = nameof(PMCWindowLoadCount); public const string ReOpenAtStart = nameof(ReOpenAtStart); // Const name for emitting when VS solution close or VS instance close. - public const string PackageCommands = "PackageCommands."; // Using prefix prevent accidental same name property collission from different type telemetry. public const string PowerShellVSSolutionClose = nameof(PowerShellVSSolutionClose); public const string PowerShellVSInstanceClose = nameof(PowerShellVSInstanceClose); public const string SolutionCount = nameof(SolutionCount); @@ -37,9 +35,7 @@ public sealed class NuGetPowerShellUsageCollector : IDisposable public const string PMUIPowerShellLoadedSolutionCount = nameof(PMUIPowerShellLoadedSolutionCount); private int _solutionCount; - // _vsSolutionData hold telemetry data for current VS solution session. private SolutionData _vsSolutionData; - // _vsInstanceData hold telemetry for current VS instance session. private readonly InstanceData _vsInstanceData; private object _lock = new object(); @@ -67,17 +63,6 @@ internal void AddPowerShellLoadedData(bool isPMC, SolutionData vsSolutionData) { lock (_lock) { - // PowerShellHost loaded first time, let's emit this to find out later how many VS instance crash after loading powershell. - if (!vsSolutionData.LoadedFromPMC && !vsSolutionData.LoadedFromPMUI) - { - var telemetryEvent = new TelemetryEvent(PowerShellLoaded, new Dictionary - { - { PackageCommands + LoadedFromPMC, isPMC } - }); - - TelemetryActivity.EmitTelemetryEvent(telemetryEvent); - } - if (isPMC) { if (!vsSolutionData.LoadedFromPMC) @@ -319,18 +304,18 @@ internal TelemetryEvent ToTelemetryEvent() var telemetry = new TelemetryEvent(PowerShellVSSolutionClose, new Dictionary() { - { PackageCommands + NuGetPowerShellUsageCollector.FirstTimeLoadedFromPMC , FirstTimeLoadedFromPMC }, - { PackageCommands + NuGetPowerShellUsageCollector.FirstTimeLoadedFromPMUI , FirstTimeLoadedFromPMUI }, - { PackageCommands + NuGetPowerShellUsageCollector.InitPs1LoadedFromPMCFirst , InitPs1LoadedFromPMCFirst }, - { PackageCommands + NuGetPowerShellUsageCollector.InitPs1LoadPMC , InitPs1LoadPMC }, - { PackageCommands + NuGetPowerShellUsageCollector.InitPs1LoadPMUI , InitPs1LoadPMUI }, - { PackageCommands + NuGetPowerShellUsageCollector.LoadedFromPMC , LoadedFromPMC }, - { PackageCommands + NuGetPowerShellUsageCollector.LoadedFromPMUI , LoadedFromPMUI }, - { PackageCommands + NuGetPowerShellUsageCollector.NuGetCommandUsed , NuGetCommandUsed }, - { PackageCommands + NuGetPowerShellUsageCollector.PMCExecuteCommandCount , PMCExecuteCommandCount }, - { PackageCommands + NuGetPowerShellUsageCollector.PMCWindowLoadCount , PMCWindowLoadCount }, - { PackageCommands + NuGetPowerShellUsageCollector.PMUIExecuteCommandCount , PMUIExecuteCommandCount }, - { PackageCommands + NuGetPowerShellUsageCollector.SolutionLoaded , SolutionLoaded } + { NuGetPowerShellUsageCollector.FirstTimeLoadedFromPMC , FirstTimeLoadedFromPMC }, + { NuGetPowerShellUsageCollector.FirstTimeLoadedFromPMUI , FirstTimeLoadedFromPMUI }, + { NuGetPowerShellUsageCollector.InitPs1LoadedFromPMCFirst , InitPs1LoadedFromPMCFirst }, + { NuGetPowerShellUsageCollector.InitPs1LoadPMC , InitPs1LoadPMC }, + { NuGetPowerShellUsageCollector.InitPs1LoadPMUI , InitPs1LoadPMUI }, + { NuGetPowerShellUsageCollector.LoadedFromPMC , LoadedFromPMC }, + { NuGetPowerShellUsageCollector.LoadedFromPMUI , LoadedFromPMUI }, + { NuGetPowerShellUsageCollector.NuGetCommandUsed , NuGetCommandUsed }, + { NuGetPowerShellUsageCollector.PMCExecuteCommandCount , PMCExecuteCommandCount }, + { NuGetPowerShellUsageCollector.PMCWindowLoadCount , PMCWindowLoadCount }, + { NuGetPowerShellUsageCollector.PMUIExecuteCommandCount , PMUIExecuteCommandCount }, + { NuGetPowerShellUsageCollector.SolutionLoaded , SolutionLoaded } }); return telemetry; @@ -363,13 +348,13 @@ internal TelemetryEvent ToTelemetryEvent() var telemetry = new TelemetryEvent(PowerShellVSInstanceClose, new Dictionary() { - { PackageCommands + NuGetPowerShellUsageCollector.PMCExecuteCommandCount , PMCExecuteCommandCount }, - { PackageCommands + NuGetPowerShellUsageCollector.PMCWindowLoadCount , PMCWindowLoadCount }, - { PackageCommands + NuGetPowerShellUsageCollector.PMUIExecuteCommandCount , PMUIExecuteCommandCount }, - { PackageCommands + PMCPowerShellLoadedSolutionCount , PMCLoadedSolutionCount }, - { PackageCommands + PMUIPowerShellLoadedSolutionCount , PMUILoadedSolutionCount }, - { PackageCommands + NuGetPowerShellUsageCollector.ReOpenAtStart , ReOpenAtStart }, - { PackageCommands + NuGetPowerShellUsageCollector.SolutionCount , SolutionCount }, + { NuGetPowerShellUsageCollector.PMCExecuteCommandCount , PMCExecuteCommandCount }, + { NuGetPowerShellUsageCollector.PMCWindowLoadCount , PMCWindowLoadCount }, + { NuGetPowerShellUsageCollector.PMUIExecuteCommandCount , PMUIExecuteCommandCount }, + { PMCPowerShellLoadedSolutionCount , PMCLoadedSolutionCount }, + { PMUIPowerShellLoadedSolutionCount , PMUILoadedSolutionCount }, + { NuGetPowerShellUsageCollector.ReOpenAtStart , ReOpenAtStart }, + { NuGetPowerShellUsageCollector.SolutionCount , SolutionCount }, }); return telemetry; diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs index 320742f534d..2acdde94b67 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs @@ -44,7 +44,7 @@ public static void RaiseNuGetCmdletExecutedEvent() NuGetCmdletExecutedEvent?.Invoke(); } - public static void RaisInitPs1LoadEvent(bool isPMC) + public static void RaiseInitPs1LoadEvent(bool isPMC) { InitPs1LoadEvent?.Invoke(isPMC); } diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index d302691566e..2612d523441 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -39,6 +39,7 @@ internal abstract class PowerShellHost : IHost, IPathExpansion, IDisposable private static readonly string AggregateSourceName = Resources.AggregateSourceName; private static readonly TimeSpan ExecuteInitScriptsRetryDelay = TimeSpan.FromMilliseconds(400); private static readonly int MaxTasks = 16; + private static bool PowerShellLoaded = false; private Microsoft.VisualStudio.Threading.AsyncLazy _vsMonitorSelection; private IVsMonitorSelection VsMonitorSelection => ThreadHelper.JoinableTaskFactory.Run(_vsMonitorSelection.GetValueAsync); @@ -313,7 +314,17 @@ public void Initialize(IConsole console) UpdateWorkingDirectory(); - // Emit first time Powershell load event to find out later how many VS instance crash after loading powershell. + if (!PowerShellLoaded) + { + var telemetryEvent = new TelemetryEvent("PowerShellLoaded", new Dictionary + { + { "Trigger", console is IWpfConsole ? "PMC" : "PMUI" } + }); + + TelemetryActivity.EmitTelemetryEvent(telemetryEvent); + PowerShellLoaded = true; + } + NuGetPowerShellUsage.RaisePowerShellLoadEvent(isPMC: console is IWpfConsole); await ExecuteInitScriptsAsync(); @@ -477,8 +488,7 @@ private async Task ExecuteInitPs1Async(string installPath, PackageIdentity ident var scriptPath = Path.Combine(toolsPath, PowerShellScripts.Init); if (File.Exists(scriptPath)) { - // Record if init.ps1 is loaded. - NuGetPowerShellUsage.RaisInitPs1LoadEvent(isPMC: _activeConsole is IWpfConsole); + NuGetPowerShellUsage.RaiseInitPs1LoadEvent(isPMC: _activeConsole is IWpfConsole); if (_scriptExecutor.Value.TryMarkVisited(identity, PackageInitPS1State.FoundAndExecuted)) { @@ -537,7 +547,6 @@ public bool Execute(IConsole console, string command, params object[] inputs) throw new ArgumentNullException(nameof(command)); } - // Record origin of powershell command NuGetPowerShellUsage.RaiseCommandExecuteEvent(isPMC: console is IWpfConsole); // since install.ps1/uninstall.ps1 could depend on init scripts, so we need to make sure From aa0ba5b82cc85f8690c35c86dd2370fe0c1638b6 Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Fri, 22 Jan 2021 13:16:09 -0800 Subject: [PATCH 58/59] Address latest PR comment from Andy and Fernando. Now solution close event called vs/nuget/solutionclose and vs instance close event called vs/nuget/instanceclose. --- .../NuGetPowerShellUsageCollector.cs | 69 ++++++++++--------- .../PowerShellHost.cs | 9 +-- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs index e49a290190f..9dae32d2030 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs @@ -11,8 +11,8 @@ namespace NuGet.VisualStudio.Telemetry public sealed class NuGetPowerShellUsageCollector : IDisposable { // PMC, PMUI powershell telemetry consts - public const string PMCExecuteCommandCount = nameof(PMCExecuteCommandCount); - public const string PMUIExecuteCommandCount = nameof(PMUIExecuteCommandCount); + public const string PmcExecuteCommandCount = nameof(PmcExecuteCommandCount); + public const string PmuiExecuteCommandCount = nameof(PmuiExecuteCommandCount); public const string NuGetCommandUsed = nameof(NuGetCommandUsed); public const string InitPs1LoadPMUI = nameof(InitPs1LoadPMUI); public const string InitPs1LoadPMC = nameof(InitPs1LoadPMC); @@ -22,17 +22,22 @@ public sealed class NuGetPowerShellUsageCollector : IDisposable public const string LoadedFromPMC = nameof(LoadedFromPMC); public const string FirstTimeLoadedFromPMC = nameof(FirstTimeLoadedFromPMC); public const string SolutionLoaded = nameof(SolutionLoaded); + public const string Trigger = nameof(Trigger); + public const string Pmc = nameof(Pmc); + public const string Pmui = nameof(Pmui); // PMC UI Console Container telemetry consts - public const string PMCWindowLoadCount = nameof(PMCWindowLoadCount); + public const string PmcWindowLoadCount = nameof(PmcWindowLoadCount); public const string ReOpenAtStart = nameof(ReOpenAtStart); // Const name for emitting when VS solution close or VS instance close. - public const string PowerShellVSSolutionClose = nameof(PowerShellVSSolutionClose); - public const string PowerShellVSInstanceClose = nameof(PowerShellVSInstanceClose); + public const string SolutionClose = nameof(SolutionClose); + public const string InstanceClose = nameof(InstanceClose); + public const string PowerShellHost = "PowerShellHost."; public const string SolutionCount = nameof(SolutionCount); - public const string PMCPowerShellLoadedSolutionCount = nameof(PMCPowerShellLoadedSolutionCount); - public const string PMUIPowerShellLoadedSolutionCount = nameof(PMUIPowerShellLoadedSolutionCount); + public const string PmcPowerShellLoadedSolutionCount = nameof(PmcPowerShellLoadedSolutionCount); + public const string PmuiPowerShellLoadedSolutionCount = nameof(PmuiPowerShellLoadedSolutionCount); + public const string PowerShellLoaded = nameof(PowerShellLoaded); private int _solutionCount; private SolutionData _vsSolutionData; @@ -105,7 +110,7 @@ internal void AddPowerShellCommandExecuteData(bool isPMC, SolutionData vsSolutio else { // This one is called for both init.ps1 and install.ps1 seperately. - // install.ps1 running inside MSBuildNuGetProject.cs (InstallPackageAsync method) may result in duplicate counting. + // install.ps1 running inside MSBuildNuGetProject.cs (InstallPackageAsync method) may result in duplicate counting. // Also this concern valid for dependent packages (of installing package) with *.ps1 files. vsSolutionData.PMUIExecuteCommandCount++; _vsInstanceData.PMUIExecuteCommandCount++; @@ -113,7 +118,6 @@ internal void AddPowerShellCommandExecuteData(bool isPMC, SolutionData vsSolutio } } - private void NuGetPowerShellUsage_NuGetCmdletExecutedEventHandler() { AddNuGetCmdletExecutedData(_vsSolutionData); @@ -123,7 +127,6 @@ internal void AddNuGetCmdletExecutedData(SolutionData vsSolutionData) { lock (_lock) { - // NuGetCommand executed vsSolutionData.NuGetCommandUsed = true; } } @@ -213,7 +216,6 @@ private void NuGetPowerShellUsage_SolutionOpenHandler() _vsInstanceData.SolutionCount = ++_solutionCount; _vsSolutionData.SolutionLoaded = true; - } } @@ -221,7 +223,6 @@ private void NuGetPowerShellUsage_SolutionCloseHandler() { lock (_lock) { - // Emit solution telemetry TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); ClearSolutionData(); } @@ -243,7 +244,7 @@ private void NuGetPowerShellUsage_VSInstanseCloseHandler() } } - //If open new solution then need to clear previous solution events. But powershell remain loaded in memory. + // If open new solution then need to clear previous solution events. But powershell remain loaded in memory. private void ClearSolutionData() { bool pmcPowershellLoad = _vsSolutionData.LoadedFromPMC; @@ -301,21 +302,21 @@ internal SolutionData() internal TelemetryEvent ToTelemetryEvent() { - var telemetry = new TelemetryEvent(PowerShellVSSolutionClose, + var telemetry = new TelemetryEvent(SolutionClose, new Dictionary() { - { NuGetPowerShellUsageCollector.FirstTimeLoadedFromPMC , FirstTimeLoadedFromPMC }, - { NuGetPowerShellUsageCollector.FirstTimeLoadedFromPMUI , FirstTimeLoadedFromPMUI }, - { NuGetPowerShellUsageCollector.InitPs1LoadedFromPMCFirst , InitPs1LoadedFromPMCFirst }, - { NuGetPowerShellUsageCollector.InitPs1LoadPMC , InitPs1LoadPMC }, - { NuGetPowerShellUsageCollector.InitPs1LoadPMUI , InitPs1LoadPMUI }, - { NuGetPowerShellUsageCollector.LoadedFromPMC , LoadedFromPMC }, - { NuGetPowerShellUsageCollector.LoadedFromPMUI , LoadedFromPMUI }, - { NuGetPowerShellUsageCollector.NuGetCommandUsed , NuGetCommandUsed }, - { NuGetPowerShellUsageCollector.PMCExecuteCommandCount , PMCExecuteCommandCount }, - { NuGetPowerShellUsageCollector.PMCWindowLoadCount , PMCWindowLoadCount }, - { NuGetPowerShellUsageCollector.PMUIExecuteCommandCount , PMUIExecuteCommandCount }, - { NuGetPowerShellUsageCollector.SolutionLoaded , SolutionLoaded } + { PowerShellHost + NuGetPowerShellUsageCollector.FirstTimeLoadedFromPMC , FirstTimeLoadedFromPMC }, + { PowerShellHost + NuGetPowerShellUsageCollector.FirstTimeLoadedFromPMUI , FirstTimeLoadedFromPMUI }, + { PowerShellHost + NuGetPowerShellUsageCollector.InitPs1LoadedFromPMCFirst , InitPs1LoadedFromPMCFirst }, + { PowerShellHost + NuGetPowerShellUsageCollector.InitPs1LoadPMC , InitPs1LoadPMC }, + { PowerShellHost + NuGetPowerShellUsageCollector.InitPs1LoadPMUI , InitPs1LoadPMUI }, + { PowerShellHost + NuGetPowerShellUsageCollector.LoadedFromPMC , LoadedFromPMC }, + { PowerShellHost + NuGetPowerShellUsageCollector.LoadedFromPMUI , LoadedFromPMUI }, + { PowerShellHost + NuGetPowerShellUsageCollector.NuGetCommandUsed , NuGetCommandUsed }, + { PowerShellHost + NuGetPowerShellUsageCollector.PmcExecuteCommandCount , PMCExecuteCommandCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.PmcWindowLoadCount , PMCWindowLoadCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.PmuiExecuteCommandCount , PMUIExecuteCommandCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.SolutionLoaded , SolutionLoaded } }); return telemetry; @@ -345,16 +346,16 @@ internal InstanceData() internal TelemetryEvent ToTelemetryEvent() { - var telemetry = new TelemetryEvent(PowerShellVSInstanceClose, + var telemetry = new TelemetryEvent(InstanceClose, new Dictionary() { - { NuGetPowerShellUsageCollector.PMCExecuteCommandCount , PMCExecuteCommandCount }, - { NuGetPowerShellUsageCollector.PMCWindowLoadCount , PMCWindowLoadCount }, - { NuGetPowerShellUsageCollector.PMUIExecuteCommandCount , PMUIExecuteCommandCount }, - { PMCPowerShellLoadedSolutionCount , PMCLoadedSolutionCount }, - { PMUIPowerShellLoadedSolutionCount , PMUILoadedSolutionCount }, - { NuGetPowerShellUsageCollector.ReOpenAtStart , ReOpenAtStart }, - { NuGetPowerShellUsageCollector.SolutionCount , SolutionCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.PmcExecuteCommandCount , PMCExecuteCommandCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.PmcWindowLoadCount , PMCWindowLoadCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.PmuiExecuteCommandCount , PMUIExecuteCommandCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.PmcPowerShellLoadedSolutionCount , PMCLoadedSolutionCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.PmuiPowerShellLoadedSolutionCount , PMUILoadedSolutionCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.ReOpenAtStart , ReOpenAtStart }, + { PowerShellHost + NuGetPowerShellUsageCollector.SolutionCount , SolutionCount }, }); return telemetry; diff --git a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs index 2612d523441..7c9428fe7d3 100644 --- a/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs +++ b/src/NuGet.Clients/NuGetConsole.Host.PowerShell/PowerShellHost.cs @@ -301,6 +301,7 @@ public void Initialize(IConsole console) { try { + bool _isPmc = console is IWpfConsole; var result = _runspaceManager.GetRunspace(console, _name); Runspace = result.Item1; _nugetHost = result.Item2; @@ -316,21 +317,21 @@ public void Initialize(IConsole console) if (!PowerShellLoaded) { - var telemetryEvent = new TelemetryEvent("PowerShellLoaded", new Dictionary + var telemetryEvent = new TelemetryEvent(NuGetPowerShellUsageCollector.PowerShellLoaded, new Dictionary { - { "Trigger", console is IWpfConsole ? "PMC" : "PMUI" } + { NuGetPowerShellUsageCollector.Trigger, _isPmc ? NuGetPowerShellUsageCollector.Pmc : NuGetPowerShellUsageCollector.Pmui } }); TelemetryActivity.EmitTelemetryEvent(telemetryEvent); PowerShellLoaded = true; } - NuGetPowerShellUsage.RaisePowerShellLoadEvent(isPMC: console is IWpfConsole); + NuGetPowerShellUsage.RaisePowerShellLoadEvent(isPMC: _isPmc); await ExecuteInitScriptsAsync(); // check if PMC console is actually opened, then only hook to solution load/close events. - if (console is IWpfConsole) + if (_isPmc) { // Hook up solution events _solutionManager.Value.SolutionOpened += (_, __) => HandleSolutionOpened(); From a9607e71b29fce98fa05e0ff7b33ed47d1d82ffa Mon Sep 17 00:00:00 2001 From: Erick Yondon Date: Fri, 22 Jan 2021 13:37:32 -0800 Subject: [PATCH 59/59] Fix naming conventions. --- .../Xamls/ConsoleContainer.xaml.cs | 4 +- .../NuGetPowerShellUsageCollector.cs | 180 +++++++++--------- .../Powershell/NuGetPowerShellUsage.cs | 8 +- 3 files changed, 96 insertions(+), 96 deletions(-) diff --git a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs index 636e5cca585..6c715d45e8d 100644 --- a/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs +++ b/src/NuGet.Clients/NuGet.Console/Xamls/ConsoleContainer.xaml.cs @@ -94,12 +94,12 @@ public void Dispose() void ConsoleContainer_Loaded(object sender, RoutedEventArgs e) { - NuGetPowerShellUsage.RaisePMCWindowsLoadEvent(isLoad: true); + NuGetPowerShellUsage.RaisePmcWindowsLoadEvent(isLoad: true); } void ConsoleContainer_UnLoaded(object sender, RoutedEventArgs e) { - NuGetPowerShellUsage.RaisePMCWindowsLoadEvent(isLoad: false); + NuGetPowerShellUsage.RaisePmcWindowsLoadEvent(isLoad: false); } } } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs index 9dae32d2030..5e9405f3a41 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/NuGetPowerShellUsageCollector.cs @@ -14,13 +14,13 @@ public sealed class NuGetPowerShellUsageCollector : IDisposable public const string PmcExecuteCommandCount = nameof(PmcExecuteCommandCount); public const string PmuiExecuteCommandCount = nameof(PmuiExecuteCommandCount); public const string NuGetCommandUsed = nameof(NuGetCommandUsed); - public const string InitPs1LoadPMUI = nameof(InitPs1LoadPMUI); - public const string InitPs1LoadPMC = nameof(InitPs1LoadPMC); - public const string InitPs1LoadedFromPMCFirst = nameof(InitPs1LoadedFromPMCFirst); - public const string LoadedFromPMUI = nameof(LoadedFromPMUI); - public const string FirstTimeLoadedFromPMUI = nameof(FirstTimeLoadedFromPMUI); - public const string LoadedFromPMC = nameof(LoadedFromPMC); - public const string FirstTimeLoadedFromPMC = nameof(FirstTimeLoadedFromPMC); + public const string InitPs1LoadPmui = nameof(InitPs1LoadPmui); + public const string InitPs1LoadPmc = nameof(InitPs1LoadPmc); + public const string InitPs1LoadedFromPmcFirst = nameof(InitPs1LoadedFromPmcFirst); + public const string LoadedFromPmui = nameof(LoadedFromPmui); + public const string FirstTimeLoadedFromPmui = nameof(FirstTimeLoadedFromPmui); + public const string LoadedFromPmc = nameof(LoadedFromPmc); + public const string FirstTimeLoadedFromPmc = nameof(FirstTimeLoadedFromPmc); public const string SolutionLoaded = nameof(SolutionLoaded); public const string Trigger = nameof(Trigger); public const string Pmc = nameof(Pmc); @@ -53,7 +53,7 @@ public NuGetPowerShellUsageCollector() NuGetPowerShellUsage.PowerShellCommandExecuteEvent += NuGetPowerShellUsage_PowerShellCommandExecuteEventHandler; NuGetPowerShellUsage.NuGetCmdletExecutedEvent += NuGetPowerShellUsage_NuGetCmdletExecutedEventHandler; NuGetPowerShellUsage.InitPs1LoadEvent += NuGetPowerShellUsage_InitPs1LoadEventHandler; - NuGetPowerShellUsage.PMCWindowsEvent += NuGetPowerShellUsage_PMCWindowsEventHandler; + NuGetPowerShellUsage.PmcWindowsEvent += NuGetPowerShellUsage_PMCWindowsEventHandler; NuGetPowerShellUsage.SolutionOpenEvent += NuGetPowerShellUsage_SolutionOpenHandler; NuGetPowerShellUsage.SolutionCloseEvent += NuGetPowerShellUsage_SolutionCloseHandler; NuGetPowerShellUsage.VSInstanceCloseEvent += NuGetPowerShellUsage_VSInstanseCloseHandler; @@ -70,23 +70,23 @@ internal void AddPowerShellLoadedData(bool isPMC, SolutionData vsSolutionData) { if (isPMC) { - if (!vsSolutionData.LoadedFromPMC) + if (!vsSolutionData.LoadedFromPmc) { - vsSolutionData.FirstTimeLoadedFromPMC = true; - _vsInstanceData.PMCLoadedSolutionCount++; + vsSolutionData.FirstTimeLoadedFromPmc = true; + _vsInstanceData.PmcLoadedSolutionCount++; } - vsSolutionData.LoadedFromPMC = true; + vsSolutionData.LoadedFromPmc = true; } else { - if (!vsSolutionData.LoadedFromPMUI) + if (!vsSolutionData.LoadedFromPmui) { - vsSolutionData.FirstTimeLoadedFromPMUI = true; - _vsInstanceData.PMUILoadedSolutionCount++; + vsSolutionData.FirstTimeLoadedFromPmui = true; + _vsInstanceData.PmuiLoadedSolutionCount++; } - vsSolutionData.LoadedFromPMUI = true; + vsSolutionData.LoadedFromPmui = true; } } } @@ -104,16 +104,16 @@ internal void AddPowerShellCommandExecuteData(bool isPMC, SolutionData vsSolutio if (isPMC) { // For PMC all installation done in one pass so no double counting. - vsSolutionData.PMCExecuteCommandCount++; - _vsInstanceData.PMCExecuteCommandCount++; + vsSolutionData.PmcExecuteCommandCount++; + _vsInstanceData.PmcExecuteCommandCount++; } else { // This one is called for both init.ps1 and install.ps1 seperately. // install.ps1 running inside MSBuildNuGetProject.cs (InstallPackageAsync method) may result in duplicate counting. // Also this concern valid for dependent packages (of installing package) with *.ps1 files. - vsSolutionData.PMUIExecuteCommandCount++; - _vsInstanceData.PMUIExecuteCommandCount++; + vsSolutionData.PmuiExecuteCommandCount++; + _vsInstanceData.PmuiExecuteCommandCount++; } } } @@ -140,34 +140,34 @@ internal void AddInitPs1LoadData(bool isPMC, SolutionData vsSolutionData) { lock (_lock) { - if (isPMC && (!vsSolutionData.FirstTimeLoadedFromPMC && !vsSolutionData.FirstTimeLoadedFromPMUI)) + if (isPMC && (!vsSolutionData.FirstTimeLoadedFromPmc && !vsSolutionData.FirstTimeLoadedFromPmui)) { - vsSolutionData.InitPs1LoadedFromPMCFirst = true; + vsSolutionData.InitPs1LoadedFromPmcFirst = true; } if (isPMC) { - vsSolutionData.InitPs1LoadPMC = true; + vsSolutionData.InitPs1LoadPmc = true; - if (!vsSolutionData.LoadedFromPMC) + if (!vsSolutionData.LoadedFromPmc) { - vsSolutionData.FirstTimeLoadedFromPMC = true; - _vsInstanceData.PMCLoadedSolutionCount++; + vsSolutionData.FirstTimeLoadedFromPmc = true; + _vsInstanceData.PmcLoadedSolutionCount++; } - vsSolutionData.LoadedFromPMC = true; + vsSolutionData.LoadedFromPmc = true; } else { - vsSolutionData.InitPs1LoadPMUI = true; + vsSolutionData.InitPs1LoadPmui = true; - if (!vsSolutionData.LoadedFromPMUI) + if (!vsSolutionData.LoadedFromPmui) { - vsSolutionData.FirstTimeLoadedFromPMUI = true; - _vsInstanceData.PMUILoadedSolutionCount++; + vsSolutionData.FirstTimeLoadedFromPmui = true; + _vsInstanceData.PmuiLoadedSolutionCount++; } - vsSolutionData.LoadedFromPMUI = true; + vsSolutionData.LoadedFromPmui = true; } } } @@ -183,8 +183,8 @@ internal void AddPMCWindowsEventData(bool isLoad) { if (isLoad) { - _vsSolutionData.PMCWindowLoadCount++; - _vsInstanceData.PMCWindowLoadCount++; + _vsSolutionData.PmcWindowLoadCount++; + _vsInstanceData.PmcWindowLoadCount++; } // Shutdown call happen before Unload event for PMCWindow so VSInstanceClose event going to have current up to date status. @@ -197,21 +197,21 @@ private void NuGetPowerShellUsage_SolutionOpenHandler() lock (_lock) { // Edge case: PMC used without solution load - if (!_vsSolutionData.SolutionLoaded && _vsSolutionData.PMCExecuteCommandCount > 0) + if (!_vsSolutionData.SolutionLoaded && _vsSolutionData.PmcExecuteCommandCount > 0) { // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event. TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); ClearSolutionData(); } - if (_vsSolutionData.LoadedFromPMC) + if (_vsSolutionData.LoadedFromPmc) { - _vsInstanceData.PMCLoadedSolutionCount++; + _vsInstanceData.PmcLoadedSolutionCount++; } - if (_vsSolutionData.LoadedFromPMUI) + if (_vsSolutionData.LoadedFromPmui) { - _vsInstanceData.PMUILoadedSolutionCount++; + _vsInstanceData.PmuiLoadedSolutionCount++; } _vsInstanceData.SolutionCount = ++_solutionCount; @@ -233,7 +233,7 @@ private void NuGetPowerShellUsage_VSInstanseCloseHandler() lock (_lock) { // Edge case: PMC used without solution load - if (!_vsSolutionData.SolutionLoaded && _vsSolutionData.PMCExecuteCommandCount > 0) + if (!_vsSolutionData.SolutionLoaded && _vsSolutionData.PmcExecuteCommandCount > 0) { // PMC used before any solution is loaded, let's emit what we have for nugetvsinstanceclose event. TelemetryActivity.EmitTelemetryEvent(_vsSolutionData.ToTelemetryEvent()); @@ -247,13 +247,13 @@ private void NuGetPowerShellUsage_VSInstanseCloseHandler() // If open new solution then need to clear previous solution events. But powershell remain loaded in memory. private void ClearSolutionData() { - bool pmcPowershellLoad = _vsSolutionData.LoadedFromPMC; - bool pmuiPowershellLoad = _vsSolutionData.LoadedFromPMUI; + bool pmcPowershellLoad = _vsSolutionData.LoadedFromPmc; + bool pmuiPowershellLoad = _vsSolutionData.LoadedFromPmui; _vsSolutionData = new SolutionData(); - _vsSolutionData.LoadedFromPMC = pmcPowershellLoad; - _vsSolutionData.LoadedFromPMUI = pmuiPowershellLoad; + _vsSolutionData.LoadedFromPmc = pmcPowershellLoad; + _vsSolutionData.LoadedFromPmui = pmuiPowershellLoad; } public void Dispose() @@ -262,7 +262,7 @@ public void Dispose() NuGetPowerShellUsage.PowerShellCommandExecuteEvent -= NuGetPowerShellUsage_PowerShellCommandExecuteEventHandler; NuGetPowerShellUsage.NuGetCmdletExecutedEvent -= NuGetPowerShellUsage_NuGetCmdletExecutedEventHandler; NuGetPowerShellUsage.InitPs1LoadEvent -= NuGetPowerShellUsage_InitPs1LoadEventHandler; - NuGetPowerShellUsage.PMCWindowsEvent -= NuGetPowerShellUsage_PMCWindowsEventHandler; + NuGetPowerShellUsage.PmcWindowsEvent -= NuGetPowerShellUsage_PMCWindowsEventHandler; NuGetPowerShellUsage.SolutionOpenEvent -= NuGetPowerShellUsage_SolutionOpenHandler; NuGetPowerShellUsage.SolutionCloseEvent -= NuGetPowerShellUsage_SolutionCloseHandler; NuGetPowerShellUsage.VSInstanceCloseEvent -= NuGetPowerShellUsage_VSInstanseCloseHandler; @@ -270,32 +270,32 @@ public void Dispose() internal class SolutionData { - internal bool FirstTimeLoadedFromPMC { get; set; } - internal bool FirstTimeLoadedFromPMUI { get; set; } - internal bool InitPs1LoadedFromPMCFirst { get; set; } - internal bool InitPs1LoadPMC { get; set; } - internal bool InitPs1LoadPMUI { get; set; } - internal bool LoadedFromPMC { get; set; } - internal bool LoadedFromPMUI { get; set; } + internal bool FirstTimeLoadedFromPmc { get; set; } + internal bool FirstTimeLoadedFromPmui { get; set; } + internal bool InitPs1LoadedFromPmcFirst { get; set; } + internal bool InitPs1LoadPmc { get; set; } + internal bool InitPs1LoadPmui { get; set; } + internal bool LoadedFromPmc { get; set; } + internal bool LoadedFromPmui { get; set; } internal bool NuGetCommandUsed { get; set; } - internal int PMCExecuteCommandCount { get; set; } - internal int PMCWindowLoadCount { get; set; } - internal int PMUIExecuteCommandCount { get; set; } + internal int PmcExecuteCommandCount { get; set; } + internal int PmcWindowLoadCount { get; set; } + internal int PmuiExecuteCommandCount { get; set; } internal bool SolutionLoaded { get; set; } internal SolutionData() { - FirstTimeLoadedFromPMC = false; - FirstTimeLoadedFromPMUI = false; - InitPs1LoadedFromPMCFirst = false; - InitPs1LoadPMC = false; - InitPs1LoadPMUI = false; - LoadedFromPMC = false; - LoadedFromPMUI = false; + FirstTimeLoadedFromPmc = false; + FirstTimeLoadedFromPmui = false; + InitPs1LoadedFromPmcFirst = false; + InitPs1LoadPmc = false; + InitPs1LoadPmui = false; + LoadedFromPmc = false; + LoadedFromPmui = false; NuGetCommandUsed = false; - PMCExecuteCommandCount = 0; - PMCWindowLoadCount = 0; - PMUIExecuteCommandCount = 0; + PmcExecuteCommandCount = 0; + PmcWindowLoadCount = 0; + PmuiExecuteCommandCount = 0; SolutionLoaded = false; } @@ -305,17 +305,17 @@ internal TelemetryEvent ToTelemetryEvent() var telemetry = new TelemetryEvent(SolutionClose, new Dictionary() { - { PowerShellHost + NuGetPowerShellUsageCollector.FirstTimeLoadedFromPMC , FirstTimeLoadedFromPMC }, - { PowerShellHost + NuGetPowerShellUsageCollector.FirstTimeLoadedFromPMUI , FirstTimeLoadedFromPMUI }, - { PowerShellHost + NuGetPowerShellUsageCollector.InitPs1LoadedFromPMCFirst , InitPs1LoadedFromPMCFirst }, - { PowerShellHost + NuGetPowerShellUsageCollector.InitPs1LoadPMC , InitPs1LoadPMC }, - { PowerShellHost + NuGetPowerShellUsageCollector.InitPs1LoadPMUI , InitPs1LoadPMUI }, - { PowerShellHost + NuGetPowerShellUsageCollector.LoadedFromPMC , LoadedFromPMC }, - { PowerShellHost + NuGetPowerShellUsageCollector.LoadedFromPMUI , LoadedFromPMUI }, + { PowerShellHost + NuGetPowerShellUsageCollector.FirstTimeLoadedFromPmc , FirstTimeLoadedFromPmc }, + { PowerShellHost + NuGetPowerShellUsageCollector.FirstTimeLoadedFromPmui , FirstTimeLoadedFromPmui }, + { PowerShellHost + NuGetPowerShellUsageCollector.InitPs1LoadedFromPmcFirst , InitPs1LoadedFromPmcFirst }, + { PowerShellHost + NuGetPowerShellUsageCollector.InitPs1LoadPmc , InitPs1LoadPmc }, + { PowerShellHost + NuGetPowerShellUsageCollector.InitPs1LoadPmui , InitPs1LoadPmui }, + { PowerShellHost + NuGetPowerShellUsageCollector.LoadedFromPmc , LoadedFromPmc }, + { PowerShellHost + NuGetPowerShellUsageCollector.LoadedFromPmui , LoadedFromPmui }, { PowerShellHost + NuGetPowerShellUsageCollector.NuGetCommandUsed , NuGetCommandUsed }, - { PowerShellHost + NuGetPowerShellUsageCollector.PmcExecuteCommandCount , PMCExecuteCommandCount }, - { PowerShellHost + NuGetPowerShellUsageCollector.PmcWindowLoadCount , PMCWindowLoadCount }, - { PowerShellHost + NuGetPowerShellUsageCollector.PmuiExecuteCommandCount , PMUIExecuteCommandCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.PmcExecuteCommandCount , PmcExecuteCommandCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.PmcWindowLoadCount , PmcWindowLoadCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.PmuiExecuteCommandCount , PmuiExecuteCommandCount }, { PowerShellHost + NuGetPowerShellUsageCollector.SolutionLoaded , SolutionLoaded } }); @@ -325,21 +325,21 @@ internal TelemetryEvent ToTelemetryEvent() internal class InstanceData { - internal int PMCExecuteCommandCount { get; set; } - internal int PMCWindowLoadCount { get; set; } - internal int PMUIExecuteCommandCount { get; set; } - internal int PMCLoadedSolutionCount { get; set; } - internal int PMUILoadedSolutionCount { get; set; } + internal int PmcExecuteCommandCount { get; set; } + internal int PmcWindowLoadCount { get; set; } + internal int PmuiExecuteCommandCount { get; set; } + internal int PmcLoadedSolutionCount { get; set; } + internal int PmuiLoadedSolutionCount { get; set; } internal bool ReOpenAtStart { get; set; } internal int SolutionCount { get; set; } internal InstanceData() { - PMCExecuteCommandCount = 0; - PMCWindowLoadCount = 0; - PMUIExecuteCommandCount = 0; - PMCLoadedSolutionCount = 0; - PMUILoadedSolutionCount = 0; + PmcExecuteCommandCount = 0; + PmcWindowLoadCount = 0; + PmuiExecuteCommandCount = 0; + PmcLoadedSolutionCount = 0; + PmuiLoadedSolutionCount = 0; ReOpenAtStart = false; SolutionCount = 0; } @@ -349,11 +349,11 @@ internal TelemetryEvent ToTelemetryEvent() var telemetry = new TelemetryEvent(InstanceClose, new Dictionary() { - { PowerShellHost + NuGetPowerShellUsageCollector.PmcExecuteCommandCount , PMCExecuteCommandCount }, - { PowerShellHost + NuGetPowerShellUsageCollector.PmcWindowLoadCount , PMCWindowLoadCount }, - { PowerShellHost + NuGetPowerShellUsageCollector.PmuiExecuteCommandCount , PMUIExecuteCommandCount }, - { PowerShellHost + NuGetPowerShellUsageCollector.PmcPowerShellLoadedSolutionCount , PMCLoadedSolutionCount }, - { PowerShellHost + NuGetPowerShellUsageCollector.PmuiPowerShellLoadedSolutionCount , PMUILoadedSolutionCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.PmcExecuteCommandCount , PmcExecuteCommandCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.PmcWindowLoadCount , PmcWindowLoadCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.PmuiExecuteCommandCount , PmuiExecuteCommandCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.PmcPowerShellLoadedSolutionCount , PmcLoadedSolutionCount }, + { PowerShellHost + NuGetPowerShellUsageCollector.PmuiPowerShellLoadedSolutionCount , PmuiLoadedSolutionCount }, { PowerShellHost + NuGetPowerShellUsageCollector.ReOpenAtStart , ReOpenAtStart }, { PowerShellHost + NuGetPowerShellUsageCollector.SolutionCount , SolutionCount }, }); diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs index 2acdde94b67..2241b429648 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/Powershell/NuGetPowerShellUsage.cs @@ -26,8 +26,8 @@ public static class NuGetPowerShellUsage public delegate void InitPs1LoadEventHandler(bool isPMC); public static event InitPs1LoadEventHandler InitPs1LoadEvent; - public delegate void PMCWindowEventHandler(bool isLoad); - public static event PMCWindowEventHandler PMCWindowsEvent; + public delegate void PmcWindowEventHandler(bool isLoad); + public static event PmcWindowEventHandler PmcWindowsEvent; public static void RaisePowerShellLoadEvent(bool isPMC) { @@ -49,9 +49,9 @@ public static void RaiseInitPs1LoadEvent(bool isPMC) InitPs1LoadEvent?.Invoke(isPMC); } - public static void RaisePMCWindowsLoadEvent(bool isLoad) + public static void RaisePmcWindowsLoadEvent(bool isLoad) { - PMCWindowsEvent?.Invoke(isLoad); + PmcWindowsEvent?.Invoke(isLoad); } public static void RaiseSolutionOpenEvent()