mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Update telemetry collecting with a few changes (#17304)
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
@@ -195,6 +194,57 @@ namespace Microsoft.PowerShell
|
||||
"workingdirectory"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// These represent the parameters that are used when starting pwsh.
|
||||
/// We can query in our telemetry to determine how pwsh was invoked.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
internal enum ParameterBitmap : long
|
||||
{
|
||||
Command = 0x00000001, // -Command | -c
|
||||
ConfigurationName = 0x00000002, // -ConfigurationName | -config
|
||||
CustomPipeName = 0x00000004, // -CustomPipeName
|
||||
EncodedCommand = 0x00000008, // -EncodedCommand | -e | -ec
|
||||
EncodedArgument = 0x00000010, // -EncodedArgument
|
||||
ExecutionPolicy = 0x00000020, // -ExecutionPolicy | -ex | -ep
|
||||
File = 0x00000040, // -File | -f
|
||||
Help = 0x00000080, // -Help, -?, /?
|
||||
InputFormat = 0x00000100, // -InputFormat | -inp | -if
|
||||
Interactive = 0x00000200, // -Interactive | -i
|
||||
Login = 0x00000400, // -Login | -l
|
||||
MTA = 0x00000800, // -MTA
|
||||
NoExit = 0x00001000, // -NoExit | -noe
|
||||
NoLogo = 0x00002000, // -NoLogo | -nol
|
||||
NonInteractive = 0x00004000, // -NonInteractive | -noni
|
||||
NoProfile = 0x00008000, // -NoProfile | -nop
|
||||
OutputFormat = 0x00010000, // -OutputFormat | -o | -of
|
||||
SettingsFile = 0x00020000, // -SettingsFile | -settings
|
||||
SSHServerMode = 0x00040000, // -SSHServerMode | -sshs
|
||||
SocketServerMode = 0x00080000, // -SocketServerMode | -sockets
|
||||
ServerMode = 0x00100000, // -ServerMode | -server
|
||||
NamedPipeServerMode = 0x00200000, // -NamedPipeServerMode | -namedpipes
|
||||
STA = 0x00400000, // -STA
|
||||
Version = 0x00800000, // -Version | -v
|
||||
WindowStyle = 0x01000000, // -WindowStyle | -w
|
||||
WorkingDirectory = 0x02000000, // -WorkingDirectory | -wd
|
||||
// Enum values for specified ExecutionPolicy
|
||||
EPUnrestricted = 0x0000000100000000, // ExecutionPolicy unrestricted
|
||||
EPRemoteSigned = 0x0000000200000000, // ExecutionPolicy remote signed
|
||||
EPAllSigned = 0x0000000400000000, // ExecutionPolicy all signed
|
||||
EPRestricted = 0x0000000800000000, // ExecutionPolicy restricted
|
||||
EPDefault = 0x0000001000000000, // ExecutionPolicy default
|
||||
EPBypass = 0x0000002000000000, // ExecutionPolicy bypass
|
||||
EPUndefined = 0x0000004000000000, // ExecutionPolicy undefined
|
||||
EPIncorrect = 0x0000008000000000, // ExecutionPolicy incorrect
|
||||
}
|
||||
|
||||
internal ParameterBitmap ParametersUsed = 0;
|
||||
|
||||
internal double ParametersUsedAsDouble
|
||||
{
|
||||
get { return (double)ParametersUsed; }
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
private void AssertArgumentsParsed()
|
||||
{
|
||||
@@ -641,6 +691,53 @@ namespace Microsoft.PowerShell
|
||||
return Path.GetFullPath(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the execution policy based on the supplied string.
|
||||
/// If the string doesn't match to any known execution policy, set it to incorrect.
|
||||
/// </summary>
|
||||
/// <param name="_executionPolicy">The value provided on the command line.</param>
|
||||
/// <returns>The execution policy.</returns>
|
||||
private static ParameterBitmap GetExecutionPolicy(string? _executionPolicy)
|
||||
{
|
||||
if (_executionPolicy is null)
|
||||
{
|
||||
return ParameterBitmap.EPUndefined;
|
||||
}
|
||||
|
||||
ParameterBitmap executionPolicySetting = ParameterBitmap.EPIncorrect;
|
||||
|
||||
if (string.Equals(_executionPolicy, "default", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
executionPolicySetting = ParameterBitmap.EPDefault;
|
||||
}
|
||||
else if (string.Equals(_executionPolicy, "remotesigned", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
executionPolicySetting = ParameterBitmap.EPRemoteSigned;
|
||||
}
|
||||
else if (string.Equals(_executionPolicy, "bypass", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
executionPolicySetting = ParameterBitmap.EPBypass;
|
||||
}
|
||||
else if (string.Equals(_executionPolicy, "allsigned", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
executionPolicySetting = ParameterBitmap.EPAllSigned;
|
||||
}
|
||||
else if (string.Equals(_executionPolicy, "restricted", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
executionPolicySetting = ParameterBitmap.EPRestricted;
|
||||
}
|
||||
else if (string.Equals(_executionPolicy, "unrestricted", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
executionPolicySetting = ParameterBitmap.EPUnrestricted;
|
||||
}
|
||||
else if (string.Equals(_executionPolicy, "undefined", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
executionPolicySetting = ParameterBitmap.EPUndefined;
|
||||
}
|
||||
|
||||
return executionPolicySetting;
|
||||
}
|
||||
|
||||
private static bool MatchSwitch(string switchKey, string match, string smallestUnambiguousMatch)
|
||||
{
|
||||
Dbg.Assert(!string.IsNullOrEmpty(match), "need a value");
|
||||
@@ -755,6 +852,7 @@ namespace Microsoft.PowerShell
|
||||
_noInteractive = true;
|
||||
_skipUserInit = true;
|
||||
_noExit = false;
|
||||
ParametersUsed |= ParameterBitmap.Version;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -763,48 +861,59 @@ namespace Microsoft.PowerShell
|
||||
_showHelp = true;
|
||||
_showExtendedHelp = true;
|
||||
_abortStartup = true;
|
||||
ParametersUsed |= ParameterBitmap.Help;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "login", "l"))
|
||||
{
|
||||
// On Windows, '-Login' does nothing.
|
||||
// On *nix, '-Login' is already handled much earlier to improve startup performance, so we do nothing here.
|
||||
ParametersUsed |= ParameterBitmap.Login;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "noexit", "noe"))
|
||||
{
|
||||
_noExit = true;
|
||||
noexitSeen = true;
|
||||
ParametersUsed |= ParameterBitmap.NoExit;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "noprofile", "nop"))
|
||||
{
|
||||
_skipUserInit = true;
|
||||
ParametersUsed |= ParameterBitmap.NoProfile;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "nologo", "nol"))
|
||||
{
|
||||
_showBanner = false;
|
||||
ParametersUsed |= ParameterBitmap.NoLogo;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "noninteractive", "noni"))
|
||||
{
|
||||
_noInteractive = true;
|
||||
ParametersUsed |= ParameterBitmap.NonInteractive;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "socketservermode", "so"))
|
||||
{
|
||||
_socketServerMode = true;
|
||||
ParametersUsed |= ParameterBitmap.SocketServerMode;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "servermode", "s"))
|
||||
{
|
||||
_serverMode = true;
|
||||
ParametersUsed |= ParameterBitmap.ServerMode;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "namedpipeservermode", "nam"))
|
||||
{
|
||||
_namedPipeServerMode = true;
|
||||
ParametersUsed |= ParameterBitmap.NamedPipeServerMode;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "sshservermode", "sshs"))
|
||||
{
|
||||
_sshServerMode = true;
|
||||
ParametersUsed |= ParameterBitmap.SSHServerMode;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "interactive", "i"))
|
||||
{
|
||||
_noInteractive = false;
|
||||
ParametersUsed |= ParameterBitmap.Interactive;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "configurationname", "config"))
|
||||
{
|
||||
@@ -817,6 +926,7 @@ namespace Microsoft.PowerShell
|
||||
}
|
||||
|
||||
_configurationName = args[i];
|
||||
ParametersUsed |= ParameterBitmap.ConfigurationName;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "custompipename", "cus"))
|
||||
{
|
||||
@@ -841,7 +951,9 @@ namespace Microsoft.PowerShell
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
_customPipeName = args[i];
|
||||
ParametersUsed |= ParameterBitmap.CustomPipeName;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "command", "c"))
|
||||
{
|
||||
@@ -849,6 +961,8 @@ namespace Microsoft.PowerShell
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ParametersUsed |= ParameterBitmap.Command;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "windowstyle", "w"))
|
||||
{
|
||||
@@ -875,6 +989,8 @@ namespace Microsoft.PowerShell
|
||||
string.Format(CultureInfo.CurrentCulture, CommandLineParameterParserStrings.InvalidWindowStyleArgument, args[i], e.Message));
|
||||
break;
|
||||
}
|
||||
|
||||
ParametersUsed |= ParameterBitmap.WindowStyle;
|
||||
#endif
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "file", "f"))
|
||||
@@ -883,6 +999,8 @@ namespace Microsoft.PowerShell
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ParametersUsed |= ParameterBitmap.File;
|
||||
}
|
||||
#if DEBUG
|
||||
else if (MatchSwitch(switchKey, "isswait", "isswait"))
|
||||
@@ -894,14 +1012,18 @@ namespace Microsoft.PowerShell
|
||||
{
|
||||
ParseFormat(args, ref i, ref _outFormat, CommandLineParameterParserStrings.MissingOutputFormatParameter);
|
||||
_outputFormatSpecified = true;
|
||||
ParametersUsed |= ParameterBitmap.OutputFormat;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "inputformat", "inp") || MatchSwitch(switchKey, "if", "if"))
|
||||
{
|
||||
ParseFormat(args, ref i, ref _inFormat, CommandLineParameterParserStrings.MissingInputFormatParameter);
|
||||
ParametersUsed |= ParameterBitmap.InputFormat;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "executionpolicy", "ex") || MatchSwitch(switchKey, "ep", "ep"))
|
||||
{
|
||||
ParseExecutionPolicy(args, ref i, ref _executionPolicy, CommandLineParameterParserStrings.MissingExecutionPolicyParameter);
|
||||
ParametersUsed |= ParameterBitmap.ExecutionPolicy;
|
||||
ParametersUsed |= GetExecutionPolicy(_executionPolicy);
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "encodedcommand", "e") || MatchSwitch(switchKey, "ec", "e"))
|
||||
{
|
||||
@@ -910,6 +1032,8 @@ namespace Microsoft.PowerShell
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ParametersUsed |= ParameterBitmap.EncodedCommand;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "encodedarguments", "encodeda") || MatchSwitch(switchKey, "ea", "ea"))
|
||||
{
|
||||
@@ -917,6 +1041,8 @@ namespace Microsoft.PowerShell
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ParametersUsed |= ParameterBitmap.EncodedArgument;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "settingsfile", "settings"))
|
||||
{
|
||||
@@ -925,6 +1051,8 @@ namespace Microsoft.PowerShell
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ParametersUsed |= ParameterBitmap.SettingsFile;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "sta", "sta"))
|
||||
{
|
||||
@@ -944,6 +1072,7 @@ namespace Microsoft.PowerShell
|
||||
}
|
||||
|
||||
_staMode = true;
|
||||
ParametersUsed |= ParameterBitmap.STA;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "mta", "mta"))
|
||||
{
|
||||
@@ -963,6 +1092,7 @@ namespace Microsoft.PowerShell
|
||||
}
|
||||
|
||||
_staMode = false;
|
||||
ParametersUsed |= ParameterBitmap.MTA;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "workingdirectory", "wo") || MatchSwitch(switchKey, "wd", "wd"))
|
||||
{
|
||||
@@ -975,6 +1105,7 @@ namespace Microsoft.PowerShell
|
||||
}
|
||||
|
||||
_workingDirectory = args[i];
|
||||
ParametersUsed |= ParameterBitmap.WorkingDirectory;
|
||||
}
|
||||
#if !UNIX
|
||||
else if (MatchSwitch(switchKey, "removeworkingdirectorytrailingcharacter", "removeworkingdirectorytrailingcharacter"))
|
||||
@@ -990,6 +1121,9 @@ namespace Microsoft.PowerShell
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// default to filename being the next argument.
|
||||
ParametersUsed |= ParameterBitmap.File;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ namespace Microsoft.PowerShell
|
||||
// First check for and handle PowerShell running in a server mode.
|
||||
if (s_cpp.ServerMode)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("ServerMode");
|
||||
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("ServerMode", s_cpp.ParametersUsedAsDouble);
|
||||
ProfileOptimization.StartProfile("StartupProfileData-ServerMode");
|
||||
StdIOProcessMediator.Run(
|
||||
initialCommand: s_cpp.InitialCommand,
|
||||
@@ -202,7 +202,7 @@ namespace Microsoft.PowerShell
|
||||
}
|
||||
else if (s_cpp.SSHServerMode)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("SSHServer");
|
||||
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("SSHServer", s_cpp.ParametersUsedAsDouble);
|
||||
ProfileOptimization.StartProfile("StartupProfileData-SSHServerMode");
|
||||
StdIOProcessMediator.Run(
|
||||
initialCommand: s_cpp.InitialCommand,
|
||||
@@ -212,7 +212,7 @@ namespace Microsoft.PowerShell
|
||||
}
|
||||
else if (s_cpp.NamedPipeServerMode)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("NamedPipe");
|
||||
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("NamedPipe", s_cpp.ParametersUsedAsDouble);
|
||||
ProfileOptimization.StartProfile("StartupProfileData-NamedPipeServerMode");
|
||||
RemoteSessionNamedPipeServer.RunServerMode(
|
||||
configurationName: s_cpp.ConfigurationName);
|
||||
@@ -220,7 +220,7 @@ namespace Microsoft.PowerShell
|
||||
}
|
||||
else if (s_cpp.SocketServerMode)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("SocketServerMode");
|
||||
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("SocketServerMode", s_cpp.ParametersUsedAsDouble);
|
||||
ProfileOptimization.StartProfile("StartupProfileData-SocketServerMode");
|
||||
HyperVSocketMediator.Run(
|
||||
initialCommand: s_cpp.InitialCommand,
|
||||
@@ -255,7 +255,7 @@ namespace Microsoft.PowerShell
|
||||
PSHost.IsStdOutputRedirected = Console.IsOutputRedirected;
|
||||
|
||||
// Send startup telemetry for ConsoleHost startup
|
||||
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("Normal");
|
||||
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("Normal", s_cpp.ParametersUsedAsDouble);
|
||||
|
||||
exitCode = s_theConsoleHost.Run(s_cpp, false);
|
||||
}
|
||||
|
||||
@@ -619,7 +619,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
// avoid double reporting for WinCompat modules that go through CommandDiscovery\AutoloadSpecifiedModule
|
||||
if (!foundModule.IsWindowsPowerShellCompatModule)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, foundModule.Name);
|
||||
ApplicationInsightsTelemetry.SendModuleTelemetryMetric(TelemetryType.ModuleLoad, foundModule.Name, foundModule.Version?.ToString());
|
||||
#if LEGACYTELEMETRY
|
||||
TelemetryAPI.ReportModuleLoad(foundModule);
|
||||
#endif
|
||||
@@ -896,7 +896,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
|
||||
if (foundModule != null)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, foundModule.Name);
|
||||
ApplicationInsightsTelemetry.SendModuleTelemetryMetric(TelemetryType.ModuleLoad, foundModule.Name, foundModule.Version?.ToString());
|
||||
SetModuleBaseForEngineModules(foundModule.Name, this.Context);
|
||||
}
|
||||
|
||||
@@ -938,7 +938,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
// Send telemetry on the imported modules
|
||||
foreach (PSModuleInfo moduleInfo in remotelyImportedModules)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(usingWinCompat ? TelemetryType.WinCompatModuleLoad : TelemetryType.ModuleLoad, moduleInfo.Name);
|
||||
ApplicationInsightsTelemetry.SendModuleTelemetryMetric(usingWinCompat ? TelemetryType.WinCompatModuleLoad : TelemetryType.ModuleLoad, moduleInfo.Name, moduleInfo.Version?.ToString());
|
||||
}
|
||||
|
||||
return remotelyImportedModules;
|
||||
@@ -1369,7 +1369,8 @@ namespace Microsoft.PowerShell.Commands
|
||||
foreach (RemoteDiscoveryHelper.CimModule remoteCimModule in remotePsCimModules)
|
||||
{
|
||||
ImportModule_RemotelyViaCimModuleData(importModuleOptions, remoteCimModule, cimSession);
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, remoteCimModule.ModuleName);
|
||||
// we don't know the version of the module
|
||||
ApplicationInsightsTelemetry.SendModuleTelemetryMetric(TelemetryType.ModuleLoad, remoteCimModule.ModuleName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1874,7 +1875,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
// of doing Get-Module -list
|
||||
foreach (PSModuleInfo module in ModuleInfo)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, module.Name);
|
||||
ApplicationInsightsTelemetry.SendModuleTelemetryMetric(TelemetryType.ModuleLoad, module.Name, module.Version?.ToString());
|
||||
RemoteDiscoveryHelper.DispatchModuleInfoProcessing(
|
||||
module,
|
||||
localAction: () =>
|
||||
@@ -1902,7 +1903,8 @@ namespace Microsoft.PowerShell.Commands
|
||||
// Now load all of the supplied assemblies...
|
||||
foreach (Assembly suppliedAssembly in Assembly)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, suppliedAssembly.GetName().Name);
|
||||
// we don't know what the version of the module is.
|
||||
ApplicationInsightsTelemetry.SendModuleTelemetryMetric(TelemetryType.ModuleLoad, suppliedAssembly.GetName().Name);
|
||||
ImportModule_ViaAssembly(importModuleOptions, suppliedAssembly);
|
||||
}
|
||||
}
|
||||
@@ -1933,7 +1935,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
ImportModule_RemotelyViaPsrpSession(importModuleOptions, null, FullyQualifiedName, this.PSSession);
|
||||
foreach (ModuleSpecification modulespec in FullyQualifiedName)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, modulespec.Name);
|
||||
ApplicationInsightsTelemetry.SendModuleTelemetryMetric(TelemetryType.ModuleLoad, modulespec.Name, modulespec.Version?.ToString());
|
||||
}
|
||||
}
|
||||
else if (this.ParameterSetName.Equals(ParameterSet_ViaWinCompat, StringComparison.OrdinalIgnoreCase)
|
||||
|
||||
@@ -1077,8 +1077,6 @@ namespace System.Management.Automation.Internal
|
||||
CommandState.Started,
|
||||
commandProcessor.Command.MyInvocation);
|
||||
|
||||
// Send telemetry that includes the type of command.
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ApplicationType, commandProcessor.Command.CommandInfo.CommandType.ToString());
|
||||
#if LEGACYTELEMETRY
|
||||
Microsoft.PowerShell.Telemetry.Internal.TelemetryAPI.TraceExecutedCommand(commandProcessor.Command.CommandInfo, commandProcessor.Command.CommandOrigin);
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Management.Automation;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -94,8 +95,15 @@ namespace Microsoft.PowerShell.Telemetry
|
||||
// private const string _psCoreTelemetryKey = "ee4b2115-d347-47b0-adb6-b19c2c763808"; // Production
|
||||
private const string _psCoreTelemetryKey = "d26a5ef4-d608-452c-a6b8-a4a55935f70d"; // V7 Preview 3
|
||||
|
||||
// In the event there is a problem in creating the node identifier file, use the default identifier.
|
||||
// This can happen if we are running in a system which has a read-only filesystem.
|
||||
private static readonly Guid _defaultNodeIdentifier = new Guid("2f998828-3f4a-4741-bf50-d11c6be42f50");
|
||||
|
||||
// Use "anonymous" as the string to return when you can't report a name
|
||||
private const string _anonymous = "anonymous";
|
||||
private const string Anonymous = "anonymous";
|
||||
|
||||
// Use '0.0' as the string for an anonymous module version
|
||||
private const string AnonymousVersion = "0.0";
|
||||
|
||||
// the telemetry failure string
|
||||
private const string _telemetryFailure = "TELEMETRY_FAILURE";
|
||||
@@ -240,6 +248,7 @@ namespace Microsoft.PowerShell.Telemetry
|
||||
"Az.StorageTable",
|
||||
"Az.StreamAnalytics",
|
||||
"Az.Subscription",
|
||||
"Az.Tools.Predictor",
|
||||
"Az.TrafficManager",
|
||||
"Az.Websites",
|
||||
"Azs.Azurebridge.Admin",
|
||||
@@ -642,6 +651,28 @@ namespace Microsoft.PowerShell.Telemetry
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send module load telemetry as a metric.
|
||||
/// For modules we send the module name (if allowed), and the version.
|
||||
/// Some modules (CIM) will continue use the string alternative method.
|
||||
/// </summary>
|
||||
/// <param name="telemetryType">The type of telemetry that we'll be sending.</param>
|
||||
/// <param name="moduleName">The module name to report. If it is not allowed, then it is set to 'anonymous'.</param>
|
||||
/// <param name="moduleVersion">The module version to report. The default value is the anonymous version '0.0.0.0'.</param>
|
||||
internal static void SendModuleTelemetryMetric(TelemetryType telemetryType, string moduleName, string moduleVersion = AnonymousVersion)
|
||||
{
|
||||
try
|
||||
{
|
||||
string allowedModuleName = GetModuleName(moduleName);
|
||||
string allowedModuleVersion = allowedModuleName == Anonymous ? AnonymousVersion : moduleVersion;
|
||||
s_telemetryClient.GetMetric(telemetryType.ToString(), "uuid", "SessionId", "ModuleName", "Version").TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, allowedModuleName, allowedModuleVersion);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors.
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send telemetry as a metric.
|
||||
/// </summary>
|
||||
@@ -654,7 +685,9 @@ namespace Microsoft.PowerShell.Telemetry
|
||||
return;
|
||||
}
|
||||
|
||||
SendPSCoreStartupTelemetry("hosted");
|
||||
// These should be handled by SendModuleTelemetryMetric.
|
||||
Debug.Assert(metricId != TelemetryType.ModuleLoad, "ModuleLoad should be handled by SendModuleTelemetryMetric.");
|
||||
Debug.Assert(metricId != TelemetryType.WinCompatModuleLoad, "WinCompatModuleLoad should be handled by SendModuleTelemetryMetric.");
|
||||
|
||||
string metricName = metricId.ToString();
|
||||
try
|
||||
@@ -671,11 +704,6 @@ namespace Microsoft.PowerShell.Telemetry
|
||||
string experimentalFeatureName = GetExperimentalFeatureName(data);
|
||||
s_telemetryClient.GetMetric(metricName, "uuid", "SessionId", "Detail").TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, experimentalFeatureName);
|
||||
break;
|
||||
case TelemetryType.ModuleLoad:
|
||||
case TelemetryType.WinCompatModuleLoad:
|
||||
string moduleName = GetModuleName(data); // This will return anonymous if the modulename is not on the report list
|
||||
s_telemetryClient.GetMetric(metricName, "uuid", "SessionId", "Detail").TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, moduleName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -697,7 +725,7 @@ namespace Microsoft.PowerShell.Telemetry
|
||||
return featureNameToValidate;
|
||||
}
|
||||
|
||||
return _anonymous;
|
||||
return Anonymous;
|
||||
}
|
||||
|
||||
// Get the module name. If we can report it, we'll return the name, otherwise, we'll return "anonymous"
|
||||
@@ -708,7 +736,7 @@ namespace Microsoft.PowerShell.Telemetry
|
||||
return moduleNameToValidate;
|
||||
}
|
||||
|
||||
return _anonymous;
|
||||
return Anonymous;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -716,7 +744,8 @@ namespace Microsoft.PowerShell.Telemetry
|
||||
/// This is done only once during for the console host.
|
||||
/// </summary>
|
||||
/// <param name="mode">The "mode" of the startup.</param>
|
||||
internal static void SendPSCoreStartupTelemetry(string mode)
|
||||
/// <param name="parametersUsed">The parameter bitmap used when starting.</param>
|
||||
internal static void SendPSCoreStartupTelemetry(string mode, double parametersUsed)
|
||||
{
|
||||
// Check if we already sent startup telemetry
|
||||
if (Interlocked.CompareExchange(ref s_startupEventSent, 1, 0) == 1)
|
||||
@@ -729,22 +758,30 @@ namespace Microsoft.PowerShell.Telemetry
|
||||
return;
|
||||
}
|
||||
|
||||
// This is the payload which reports the startup information of OS and shell details.
|
||||
var properties = new Dictionary<string, string>();
|
||||
|
||||
// The variable POWERSHELL_DISTRIBUTION_CHANNEL is set in our docker images.
|
||||
// This allows us to track the actual docker OS as OSDescription provides only "linuxkit"
|
||||
// which has limited usefulness
|
||||
// This is the payload for the parameter data which is sent as a metric.
|
||||
var parameters = new Dictionary<string, double>();
|
||||
|
||||
// The variable POWERSHELL_DISTRIBUTION_CHANNEL is set in our docker images and
|
||||
// by various other environments. This allows us to track the actual docker OS as
|
||||
// OSDescription provides only "linuxkit" which has limited usefulness.
|
||||
var channel = Environment.GetEnvironmentVariable("POWERSHELL_DISTRIBUTION_CHANNEL");
|
||||
|
||||
// Construct the payload for the OS and shell details.
|
||||
properties.Add("SessionId", s_sessionId);
|
||||
properties.Add("UUID", s_uniqueUserIdentifier);
|
||||
properties.Add("GitCommitID", PSVersionInfo.GitCommitId);
|
||||
properties.Add("OSDescription", RuntimeInformation.OSDescription);
|
||||
properties.Add("OSChannel", string.IsNullOrEmpty(channel) ? "unknown" : channel);
|
||||
properties.Add("StartMode", string.IsNullOrEmpty(mode) ? "unknown" : mode);
|
||||
|
||||
// Construct the payload for the parameters used.
|
||||
parameters.Add("Param", parametersUsed);
|
||||
try
|
||||
{
|
||||
s_telemetryClient.TrackEvent("ConsoleHostStartup", properties, null);
|
||||
s_telemetryClient.TrackEvent("ConsoleHostStartup", properties, parameters);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -798,50 +835,53 @@ namespace Microsoft.PowerShell.Telemetry
|
||||
/// Try to create a unique identifier and persist it to the telemetry.uuid file.
|
||||
/// </summary>
|
||||
/// <param name="telemetryFilePath">The path to the persisted telemetry.uuid file.</param>
|
||||
/// <param name="id">The created identifier.</param>
|
||||
/// <returns>
|
||||
/// The method returns a bool indicating success or failure of creating the id.
|
||||
/// The method node id.
|
||||
/// </returns>
|
||||
private static bool TryCreateUniqueIdentifierAndFile(string telemetryFilePath, out Guid id)
|
||||
private static Guid CreateUniqueIdentifierAndFile(string telemetryFilePath)
|
||||
{
|
||||
// one last attempt to retrieve before creating incase we have a lot of simultaneous entry into the mutex.
|
||||
id = Guid.Empty;
|
||||
Guid id = Guid.Empty;
|
||||
if (TryGetIdentifier(telemetryFilePath, out id))
|
||||
{
|
||||
return true;
|
||||
return id;
|
||||
}
|
||||
|
||||
// The directory may not exist, so attempt to create it
|
||||
// CreateDirectory will simply return the directory if exists
|
||||
bool attemptFileCreation = true;
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(telemetryFilePath));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// send a telemetry indicating a problem with the cache dir
|
||||
// it's likely something is seriously wrong so we should at least report it.
|
||||
// We don't want to provide reasons here, that's not the point, but we
|
||||
// would like to know if we're having a generalized problem which we can trace statistically
|
||||
CanSendTelemetry = false;
|
||||
s_telemetryClient.GetMetric(_telemetryFailure, "Detail").TrackValue(1, "cachedir");
|
||||
return false;
|
||||
// There was a problem in creating the directory for the file, do not attempt to create the file.
|
||||
// We don't send telemetry here because there are valid reasons for the directory to not exist
|
||||
// and not be able to be created.
|
||||
attemptFileCreation = false;
|
||||
}
|
||||
|
||||
// Create and save the new identifier, and if there's a problem, disable telemetry
|
||||
try
|
||||
// If we were able to create the directory, try to create the file,
|
||||
// if this fails we will send telemetry to indicate this and then use the default identifier.
|
||||
if (attemptFileCreation)
|
||||
{
|
||||
id = Guid.NewGuid();
|
||||
File.WriteAllBytes(telemetryFilePath, id.ToByteArray());
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// another bit of telemetry to notify us about a problem with saving the unique id.
|
||||
s_telemetryClient.GetMetric(_telemetryFailure, "Detail").TrackValue(1, "saveuuid");
|
||||
try
|
||||
{
|
||||
id = Guid.NewGuid();
|
||||
File.WriteAllBytes(telemetryFilePath, id.ToByteArray());
|
||||
return id;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// another bit of telemetry to notify us about a problem with saving the unique id.
|
||||
s_telemetryClient.GetMetric(_telemetryFailure, "Detail").TrackValue(1, "saveuuid");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
// all attempts to create an identifier have failed, so use the default node id.
|
||||
id = _defaultNodeIdentifier;
|
||||
return id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -865,15 +905,12 @@ namespace Microsoft.PowerShell.Telemetry
|
||||
// simultaneous shell starts without the persisted file which attempt to create the file.
|
||||
try
|
||||
{
|
||||
// TryCreateUniqueIdentifierAndFile shouldn't throw, but the mutex might
|
||||
// CreateUniqueIdentifierAndFile shouldn't throw, but the mutex might
|
||||
using var m = new Mutex(true, "CreateUniqueUserId");
|
||||
m.WaitOne();
|
||||
try
|
||||
{
|
||||
if (TryCreateUniqueIdentifierAndFile(uuidPath, out id))
|
||||
{
|
||||
return id;
|
||||
}
|
||||
return CreateUniqueIdentifierAndFile(uuidPath);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -129,28 +129,14 @@ Describe "Telemetry for shell startup" -Tag CI {
|
||||
$result | Should -Be $expectedValue
|
||||
}
|
||||
|
||||
It "Should resend startup event if the semaphore says we haven't sent telemetry" {
|
||||
|
||||
It "Should send startup event" {
|
||||
$resultJson = & $PWSH -NoProfile -c {
|
||||
# this should ensure that the startup telemetry event is sent.
|
||||
$null = Get-Date | Out-String
|
||||
$telemetryType = [Microsoft.PowerShell.Telemetry.ApplicationInsightsTelemetry]
|
||||
$bindingFlags = [System.Reflection.BindingFlags]"NonPublic,Static"
|
||||
$initialValue = ${telemetryType}.GetMember("s_startupEventSent", $bindingFlags)[0].GetValue($null)
|
||||
# force a resend of the startup telemetry
|
||||
$null = ${telemetryType}.GetMember("s_startupEventSent", $bindingFlags)[0].SetValue($null,0)
|
||||
$null = Get-Date | Out-String
|
||||
# now check it again, it should be true now that something has executed
|
||||
$finalValue = ${telemetryType}.GetMember("s_startupEventSent", $bindingFlags)[0].GetValue($null)
|
||||
@{
|
||||
initialValue = $initialValue
|
||||
finalValue = $finalValue
|
||||
} | ConvertTo-Json -Compress
|
||||
$observedValue = ${telemetryType}.GetMember("s_startupEventSent", $bindingFlags)[0].GetValue($null)
|
||||
$observedValue | Should -Be 1 -Because "Should have sent telemetry on console startup"
|
||||
}
|
||||
|
||||
$result = $resultJson | ConvertFrom-Json
|
||||
|
||||
$result.InitialValue | Should -Be 1 -Because "Should have sent telemetry on console startup"
|
||||
|
||||
$result.FinalValue | Should -Be 1 -Because "Should have resent telemetry"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user