Add telemetry to track the use of features (#24247)

This commit is contained in:
James Truher [MSFT] 2024-09-16 16:28:19 -07:00 committed by GitHub
parent 9303de597d
commit 4fdc02b698
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 83 additions and 16 deletions

View File

@ -9,6 +9,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Runtime.Loader;
using Microsoft.PowerShell.Telemetry;
namespace System.Management.Automation
{
@ -607,16 +608,19 @@ namespace System.Management.Automation
[UnmanagedCallersOnly]
public static int LoadAssemblyFromNativeMemory(IntPtr data, int size)
{
int result = 0;
try
{
using var stream = new UnmanagedMemoryStream((byte*)data, size);
AssemblyLoadContext.Default.LoadFromStream(stream);
return 0;
}
catch
{
return -1;
result = -1;
}
ApplicationInsightsTelemetry.SendUseTelemetry("PowerShellUnsafeAssemblyLoad", result == 0 ? "1" : "0");
return result;
}
}
}

View File

@ -44,8 +44,6 @@ namespace System.Management.Automation.Internal
| FileAttributes.Offline
| (FileAttributes)FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS
| (FileAttributes)FILE_ATTRIBUTE_RECALL_ON_OPEN;
return;
}
/// <summary>

View File

@ -9,6 +9,7 @@ using System.Management.Automation.Internal;
using System.Management.Automation.Language;
using System.Management.Automation.Runspaces;
using System.Threading;
using Microsoft.PowerShell.Telemetry;
namespace System.Management.Automation.Subsystem.Feedback
{
@ -287,13 +288,14 @@ namespace System.Management.Automation.Subsystem.Feedback
.AddParameter("ExpandProperty", "Name")
.Invoke<string>();
if (results.Count > 0)
{
return new FeedbackItem(
SuggestionStrings.Suggestion_CommandNotFound,
new List<string>(results),
FeedbackDisplayLayout.Landscape);
}
if (results.Count > 0)
{
ApplicationInsightsTelemetry.SendUseTelemetry("FuzzyMatching", "CommandNotFound");
return new FeedbackItem(
SuggestionStrings.Suggestion_CommandNotFound,
new List<string>(results),
FeedbackDisplayLayout.Landscape);
}
return null;
}

View File

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Management.Automation.Internal;
using Microsoft.PowerShell.Telemetry;
namespace System.Management.Automation.Subsystem
{
@ -96,6 +97,7 @@ namespace System.Management.Automation.Subsystem
internal void RegisterImplementation(ISubsystem impl)
{
AddImplementation(impl);
ApplicationInsightsTelemetry.SendUseTelemetry(ApplicationInsightsTelemetry.s_subsystemRegistration, impl.Name);
}
internal ISubsystem UnregisterImplementation(Guid id)

View File

@ -83,6 +83,13 @@ namespace Microsoft.PowerShell.Telemetry
/// Remote session creation.
/// </summary>
RemoteSessionOpen,
/// <summary>
/// Send telemetry for a stable feature when used.
/// By making a distinction between this and experimental feature use, it will make
/// queries much easier.
/// </summary>
FeatureUse,
}
/// <summary>
@ -110,6 +117,9 @@ namespace Microsoft.PowerShell.Telemetry
/// </summary>
public static class ApplicationInsightsTelemetry
{
// The string for SubsystermRegistration
internal const string s_subsystemRegistration = "Subsystem.Registration";
// If this env var is true, yes, or 1, telemetry will NOT be sent.
private const string _telemetryOptoutEnvVar = "POWERSHELL_TELEMETRY_OPTOUT";
@ -152,6 +162,8 @@ namespace Microsoft.PowerShell.Telemetry
private static readonly HashSet<string> s_knownModules;
private static readonly HashSet<string> s_knownModuleTags;
private static readonly HashSet<string> s_knownSubsystemNames;
/// <summary>Gets a value indicating whether telemetry can be sent.</summary>
public static bool CanSendTelemetry { get; private set; } = false;
@ -620,6 +632,13 @@ namespace Microsoft.PowerShell.Telemetry
};
s_uniqueUserIdentifier = GetUniqueIdentifier().ToString();
s_knownSubsystemNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Completion",
"general",
"Windows Package Manager - WinGet",
"Az Predictor"
};
}
}
@ -715,7 +734,7 @@ namespace Microsoft.PowerShell.Telemetry
s_telemetryClient.
GetMetric(new MetricIdentifier(string.Empty, telemetryType.ToString(), "uuid", "SessionId", "ModuleName", "Version", "Tag")).
TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, allowedModuleName, allowedModuleVersion, allowedModuleTagString);
TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, allowedModuleName, allowedModuleVersion, allowedModuleTagString);
}
catch
{
@ -754,7 +773,8 @@ namespace Microsoft.PowerShell.Telemetry
/// </summary>
/// <param name="metricId">The type of telemetry that we'll be sending.</param>
/// <param name="data">The specific details about the telemetry.</param>
internal static void SendTelemetryMetric(TelemetryType metricId, string data)
/// <param name="value">The count of instances for the telemetry payload.</param>
internal static void SendTelemetryMetric(TelemetryType metricId, string data, double value = 1.0)
{
if (!CanSendTelemetry)
{
@ -776,12 +796,13 @@ namespace Microsoft.PowerShell.Telemetry
case TelemetryType.ExperimentalEngineFeatureActivation:
case TelemetryType.ExperimentalEngineFeatureDeactivation:
case TelemetryType.ExperimentalFeatureUse:
s_telemetryClient.GetMetric(metricName, "uuid", "SessionId", "Detail").TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, data);
case TelemetryType.FeatureUse:
s_telemetryClient.GetMetric(metricName, "uuid", "SessionId", "Detail").TrackValue(metricValue: value, s_uniqueUserIdentifier, s_sessionId, data);
break;
case TelemetryType.ExperimentalModuleFeatureActivation:
case TelemetryType.ExperimentalModuleFeatureDeactivation:
string experimentalFeatureName = GetExperimentalFeatureName(data);
s_telemetryClient.GetMetric(metricName, "uuid", "SessionId", "Detail").TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, experimentalFeatureName);
s_telemetryClient.GetMetric(metricName, "uuid", "SessionId", "Detail").TrackValue(metricValue: value, s_uniqueUserIdentifier, s_sessionId, experimentalFeatureName);
break;
}
}
@ -792,6 +813,35 @@ namespace Microsoft.PowerShell.Telemetry
}
}
/// <summary>
/// Send additional information about an feature as it is used.
/// </summary>
/// <param name="featureName">The name of the feature.</param>
/// <param name="detail">The details about the feature use.</param>
/// <param name="value">The value to report when sending the payload.</param>
internal static void SendUseTelemetry(string featureName, string detail, double value = 1.0)
{
if (!CanSendTelemetry)
{
return;
}
// keep payload small
if (featureName is null || detail is null || featureName.Length > 33 || detail.Length > 33)
{
return;
}
if (string.Compare(featureName, s_subsystemRegistration, true) == 0)
{
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.FeatureUse, string.Join(":", featureName, GetSubsystemName(detail)), value);
}
else
{
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.FeatureUse, string.Join(":", featureName, detail), value);
}
}
/// <summary>
/// Send additional information about an experimental feature as it is used.
/// </summary>
@ -822,7 +872,18 @@ namespace Microsoft.PowerShell.Telemetry
return Anonymous;
}
// Get the module name. If we can report it, we'll return the name, otherwise, we'll return "anonymous"
// Get the module name. If we can report it, we'll return the name, otherwise, we'll return the string "anonymous"
private static string GetSubsystemName(string subsystemNameToValidate)
{
if (s_knownSubsystemNames.Contains(subsystemNameToValidate))
{
return subsystemNameToValidate;
}
return Anonymous;
}
// Get the module name. If we can report it, we'll return the name, otherwise, we'll return anonymous.
private static string GetModuleName(string moduleNameToValidate)
{
if (s_knownModules.Contains(moduleNameToValidate))