Skip to content

Commit 14d7bfe

Browse files
authored
Telemetry improvements for tracking experimental feature optout (PowerShell#18762)
1 parent 66a8982 commit 14d7bfe

File tree

3 files changed

+87
-4
lines changed

3 files changed

+87
-4
lines changed

Diff for: src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs

+17-1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,20 @@ static ExperimentalFeature()
157157
EnabledExperimentalFeatureNames = ProcessEnabledFeatures(enabledFeatures);
158158
}
159159

160+
/// <summary>
161+
/// We need to notify which features were not enabled.
162+
/// </summary>
163+
private static void SendTelemetryForDeactivatedFeatures(ReadOnlyBag<string> enabledFeatures)
164+
{
165+
foreach (var feature in EngineExperimentalFeatures)
166+
{
167+
if (!enabledFeatures.Contains(feature.Name))
168+
{
169+
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ExperimentalEngineFeatureDeactivation, feature.Name);
170+
}
171+
}
172+
}
173+
160174
/// <summary>
161175
/// Process the array of enabled feature names retrieved from configuration.
162176
/// Ignore invalid feature names and unavailable engine feature names, and
@@ -198,7 +212,9 @@ private static ReadOnlyBag<string> ProcessEnabledFeatures(string[] enabledFeatur
198212
}
199213
}
200214

201-
return new ReadOnlyBag<string>(new HashSet<string>(list, StringComparer.OrdinalIgnoreCase));
215+
ReadOnlyBag<string> features = new(new HashSet<string>(list, StringComparer.OrdinalIgnoreCase));
216+
SendTelemetryForDeactivatedFeatures(features);
217+
return features;
202218
}
203219

204220
/// <summary>

Diff for: src/System.Management.Automation/engine/NativeCommandProcessor.cs

+31-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using System.Text;
1919
using System.Threading;
2020
using System.Xml;
21+
using Microsoft.PowerShell.Telemetry;
2122
using Dbg = System.Management.Automation.Diagnostics;
2223

2324
namespace System.Management.Automation
@@ -872,8 +873,36 @@ internal override void Complete()
872873

873874
this.commandRuntime.PipelineProcessor.ExecutionFailed = true;
874875

875-
if (!ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandErrorActionPreferenceFeatureName)
876-
|| !Command.Context.GetBooleanPreference(SpecialVariables.PSNativeCommandUseErrorActionPreferenceVarPath, defaultPref: false, out _))
876+
// Feature is not enabled, so return
877+
if (!ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandErrorActionPreferenceFeatureName))
878+
{
879+
return;
880+
}
881+
882+
// We send telemetry information only if the feature is enabled.
883+
// This shouldn't be done once, because it's a run-time check we should send telemetry every time.
884+
// Report on the following conditions:
885+
// - The variable is not present
886+
// - The value is not set (variable is null)
887+
// - The value is set to true or false
888+
bool useDefaultSetting;
889+
bool nativeErrorActionPreferenceSetting = Command.Context.GetBooleanPreference(
890+
SpecialVariables.PSNativeCommandUseErrorActionPreferenceVarPath,
891+
defaultPref: false,
892+
out useDefaultSetting);
893+
894+
// The variable is unset
895+
if (useDefaultSetting)
896+
{
897+
ApplicationInsightsTelemetry.SendExperimentalUseData(ExperimentalFeature.PSNativeCommandErrorActionPreferenceFeatureName, "unset");
898+
return;
899+
}
900+
901+
// Send the value that was set.
902+
ApplicationInsightsTelemetry.SendExperimentalUseData(ExperimentalFeature.PSNativeCommandErrorActionPreferenceFeatureName, nativeErrorActionPreferenceSetting.ToString());
903+
904+
// if it was explicitly set to false, return
905+
if (!nativeErrorActionPreferenceSetting)
877906
{
878907
return;
879908
}

Diff for: src/System.Management.Automation/utils/Telemetry.cs

+39-1
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,31 @@ internal enum TelemetryType
3838
/// </summary>
3939
WinCompatModuleLoad,
4040

41+
/// <summary>
42+
/// Send telemetry for experimental module feature deactivation.
43+
/// All experimental engine features will be have telemetry.
44+
/// </summary>
45+
ExperimentalEngineFeatureDeactivation,
46+
4147
/// <summary>
4248
/// Send telemetry for experimental module feature activation.
4349
/// All experimental engine features will be have telemetry.
4450
/// </summary>
4551
ExperimentalEngineFeatureActivation,
4652

53+
/// <summary>
54+
/// Send telemetry for an experimental feature when use.
55+
/// </summary>
56+
ExperimentalFeatureUse,
57+
58+
/// <summary>
59+
/// Send telemetry for experimental module feature deactivation.
60+
/// Experimental module features will send telemetry based on the module it is in.
61+
/// If we send telemetry for the module, we will also do so for any experimental feature
62+
/// in that module.
63+
/// </summary>
64+
ExperimentalModuleFeatureDeactivation,
65+
4766
/// <summary>
4867
/// Send telemetry for experimental module feature activation.
4968
/// Experimental module features will send telemetry based on the module it is in.
@@ -126,7 +145,7 @@ public static class ApplicationInsightsTelemetry
126145
private static readonly HashSet<string> s_knownModules;
127146

128147
/// <summary>Gets a value indicating whether telemetry can be sent.</summary>
129-
public static bool CanSendTelemetry { get; private set; }
148+
public static bool CanSendTelemetry { get; private set; } = false;
130149

131150
/// <summary>
132151
/// Initializes static members of the <see cref="ApplicationInsightsTelemetry"/> class.
@@ -703,9 +722,12 @@ internal static void SendTelemetryMetric(TelemetryType metricId, string data)
703722
case TelemetryType.PowerShellCreate:
704723
case TelemetryType.RemoteSessionOpen:
705724
case TelemetryType.ExperimentalEngineFeatureActivation:
725+
case TelemetryType.ExperimentalEngineFeatureDeactivation:
726+
case TelemetryType.ExperimentalFeatureUse:
706727
s_telemetryClient.GetMetric(metricName, "uuid", "SessionId", "Detail").TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, data);
707728
break;
708729
case TelemetryType.ExperimentalModuleFeatureActivation:
730+
case TelemetryType.ExperimentalModuleFeatureDeactivation:
709731
string experimentalFeatureName = GetExperimentalFeatureName(data);
710732
s_telemetryClient.GetMetric(metricName, "uuid", "SessionId", "Detail").TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, experimentalFeatureName);
711733
break;
@@ -718,6 +740,21 @@ internal static void SendTelemetryMetric(TelemetryType metricId, string data)
718740
}
719741
}
720742

743+
/// <summary>
744+
/// Send additional information about an experimental feature as it is used.
745+
/// </summary>
746+
/// <param name="featureName">The name of the experimental feature.</param>
747+
/// <param name="detail">The details about the experimental feature use.</param>
748+
internal static void SendExperimentalUseData(string featureName, string detail)
749+
{
750+
if (!CanSendTelemetry)
751+
{
752+
return;
753+
}
754+
755+
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ExperimentalFeatureUse, string.Join(":", featureName, detail));
756+
}
757+
721758
// Get the experimental feature name. If we can report it, we'll return the name of the feature, otherwise, we'll return "anonymous"
722759
private static string GetExperimentalFeatureName(string featureNameToValidate)
723760
{
@@ -779,6 +816,7 @@ internal static void SendPSCoreStartupTelemetry(string mode, double parametersUs
779816
properties.Add("UUID", s_uniqueUserIdentifier);
780817
properties.Add("GitCommitID", PSVersionInfo.GitCommitId);
781818
properties.Add("OSDescription", RuntimeInformation.OSDescription);
819+
properties.Add("RuntimeIdentifier", RuntimeInformation.RuntimeIdentifier);
782820
properties.Add("OSChannel", string.IsNullOrEmpty(channel) ? "unknown" : channel);
783821
properties.Add("StartMode", string.IsNullOrEmpty(mode) ? "unknown" : mode);
784822

0 commit comments

Comments
 (0)