Hashed telemetry and event name change (#7919)
This commit is contained in:
parent
60c6814a90
commit
cef9a90ad1
15 changed files with 340 additions and 155 deletions
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Cli.Utils
|
namespace Microsoft.DotNet.Cli.Utils
|
||||||
{
|
{
|
||||||
|
@ -83,5 +84,11 @@ namespace Microsoft.DotNet.Cli.Utils
|
||||||
public string EventName { get; }
|
public string EventName { get; }
|
||||||
public IDictionary<string, string> Properties { get; }
|
public IDictionary<string, string> Properties { get; }
|
||||||
public IDictionary<string, double> Measurements { get; }
|
public IDictionary<string, double> Measurements { get; }
|
||||||
|
|
||||||
|
public ApplicationInsightsEntryFormat WithAppliedToPropertiesValue(Func<string, string> func)
|
||||||
|
{
|
||||||
|
var appliedProperties = Properties.ToDictionary(p => p.Key, p => func(p.Value));
|
||||||
|
return new ApplicationInsightsEntryFormat(EventName, appliedProperties, Measurements);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,7 @@ namespace Microsoft.DotNet.Cli
|
||||||
var command = string.Empty;
|
var command = string.Empty;
|
||||||
var lastArg = 0;
|
var lastArg = 0;
|
||||||
var cliFallbackFolderPathCalculator = new CliFallbackFolderPathCalculator();
|
var cliFallbackFolderPathCalculator = new CliFallbackFolderPathCalculator();
|
||||||
|
TopLevelCommandParserResult topLevelCommandParserResult = TopLevelCommandParserResult.Empty;
|
||||||
using (INuGetCacheSentinel nugetCacheSentinel = new NuGetCacheSentinel(cliFallbackFolderPathCalculator))
|
using (INuGetCacheSentinel nugetCacheSentinel = new NuGetCacheSentinel(cliFallbackFolderPathCalculator))
|
||||||
using (IFirstTimeUseNoticeSentinel disposableFirstTimeUseNoticeSentinel =
|
using (IFirstTimeUseNoticeSentinel disposableFirstTimeUseNoticeSentinel =
|
||||||
new FirstTimeUseNoticeSentinel(cliFallbackFolderPathCalculator))
|
new FirstTimeUseNoticeSentinel(cliFallbackFolderPathCalculator))
|
||||||
|
@ -120,7 +121,13 @@ namespace Microsoft.DotNet.Cli
|
||||||
// It's the command, and we're done!
|
// It's the command, and we're done!
|
||||||
command = args[lastArg];
|
command = args[lastArg];
|
||||||
|
|
||||||
if (IsDotnetBeingInvokedFromNativeInstaller(command))
|
if (string.IsNullOrEmpty(command))
|
||||||
|
{
|
||||||
|
command = "help";
|
||||||
|
}
|
||||||
|
|
||||||
|
topLevelCommandParserResult = new TopLevelCommandParserResult(args[lastArg]);
|
||||||
|
if (IsDotnetBeingInvokedFromNativeInstaller(topLevelCommandParserResult))
|
||||||
{
|
{
|
||||||
firstTimeUseNoticeSentinel = new NoOpFirstTimeUseNoticeSentinel();
|
firstTimeUseNoticeSentinel = new NoOpFirstTimeUseNoticeSentinel();
|
||||||
}
|
}
|
||||||
|
@ -144,7 +151,7 @@ namespace Microsoft.DotNet.Cli
|
||||||
telemetryClient = new Telemetry.Telemetry(firstTimeUseNoticeSentinel);
|
telemetryClient = new Telemetry.Telemetry(firstTimeUseNoticeSentinel);
|
||||||
}
|
}
|
||||||
TelemetryEventEntry.Subscribe(telemetryClient.TrackEvent);
|
TelemetryEventEntry.Subscribe(telemetryClient.TrackEvent);
|
||||||
TelemetryEventEntry.TelemetryFilter = new TelemetryFilter();
|
TelemetryEventEntry.TelemetryFilter = new TelemetryFilter(Sha256Hasher.HashWithNormalizedCasing);
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<string> appArgs =
|
IEnumerable<string> appArgs =
|
||||||
|
@ -158,23 +165,18 @@ namespace Microsoft.DotNet.Cli
|
||||||
Console.WriteLine($"Telemetry is: {(telemetryClient.Enabled ? "Enabled" : "Disabled")}");
|
Console.WriteLine($"Telemetry is: {(telemetryClient.Enabled ? "Enabled" : "Disabled")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(command))
|
TelemetryEventEntry.SendFiltered(topLevelCommandParserResult);
|
||||||
{
|
|
||||||
command = "help";
|
|
||||||
}
|
|
||||||
|
|
||||||
TelemetryEventEntry.TrackEvent(command, null, null);
|
|
||||||
|
|
||||||
int exitCode;
|
int exitCode;
|
||||||
if (BuiltInCommandsCatalog.Commands.TryGetValue(command, out var builtIn))
|
if (BuiltInCommandsCatalog.Commands.TryGetValue(topLevelCommandParserResult.Command, out var builtIn))
|
||||||
{
|
{
|
||||||
TelemetryEventEntry.SendFiltered(Parser.Instance.ParseFrom($"dotnet {command}", appArgs.ToArray()));
|
TelemetryEventEntry.SendFiltered(Parser.Instance.ParseFrom($"dotnet {topLevelCommandParserResult.Command}", appArgs.ToArray()));
|
||||||
exitCode = builtIn.Command(appArgs.ToArray());
|
exitCode = builtIn.Command(appArgs.ToArray());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CommandResult result = Command.Create(
|
CommandResult result = Command.Create(
|
||||||
"dotnet-" + command,
|
"dotnet-" + topLevelCommandParserResult.Command,
|
||||||
appArgs,
|
appArgs,
|
||||||
FrameworkConstants.CommonFrameworks.NetStandardApp15)
|
FrameworkConstants.CommonFrameworks.NetStandardApp15)
|
||||||
.Execute();
|
.Execute();
|
||||||
|
@ -183,9 +185,9 @@ namespace Microsoft.DotNet.Cli
|
||||||
return exitCode;
|
return exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsDotnetBeingInvokedFromNativeInstaller(string command)
|
private static bool IsDotnetBeingInvokedFromNativeInstaller(TopLevelCommandParserResult parseResult)
|
||||||
{
|
{
|
||||||
return command == "internal-reportinstallsuccess";
|
return parseResult.Command == "internal-reportinstallsuccess";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ConfigureDotNetForFirstTimeUse(
|
private static void ConfigureDotNetForFirstTimeUse(
|
||||||
|
|
|
@ -29,9 +29,10 @@ namespace Microsoft.DotNet.Cli.Telemetry
|
||||||
if (firstOption != null)
|
if (firstOption != null)
|
||||||
{
|
{
|
||||||
result.Add(new ApplicationInsightsEntryFormat(
|
result.Add(new ApplicationInsightsEntryFormat(
|
||||||
"dotnet-" + topLevelCommandNameFromParse,
|
"sublevelparser/command",
|
||||||
new Dictionary<string, string>
|
new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
|
{ "verb", topLevelCommandNameFromParse},
|
||||||
{"argument", firstOption}
|
{"argument", firstOption}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,10 @@ namespace Microsoft.DotNet.Cli.Telemetry
|
||||||
if (firstArgument != null)
|
if (firstArgument != null)
|
||||||
{
|
{
|
||||||
result.Add(new ApplicationInsightsEntryFormat(
|
result.Add(new ApplicationInsightsEntryFormat(
|
||||||
"dotnet-" + topLevelCommandNameFromParse,
|
"sublevelparser/command",
|
||||||
new Dictionary<string, string>
|
new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
|
{"verb", topLevelCommandNameFromParse},
|
||||||
{"argument", firstArgument}
|
{"argument", firstArgument}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,11 @@ namespace Microsoft.DotNet.Cli.Telemetry
|
||||||
return HashInFormat(sha256, text);
|
return HashInFormat(sha256, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string HashWithNormalizedCasing(string text)
|
||||||
|
{
|
||||||
|
return Hash(text.ToUpperInvariant());
|
||||||
|
}
|
||||||
|
|
||||||
private static string HashInFormat(SHA256 sha256, string text)
|
private static string HashInFormat(SHA256 sha256, string text)
|
||||||
{
|
{
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(text);
|
byte[] bytes = Encoding.UTF8.GetBytes(text);
|
||||||
|
|
|
@ -120,7 +120,7 @@ namespace Microsoft.DotNet.Cli.Telemetry
|
||||||
Dictionary<string, string> eventProperties = GetEventProperties(properties);
|
Dictionary<string, string> eventProperties = GetEventProperties(properties);
|
||||||
Dictionary<string, double> eventMeasurements = GetEventMeasures(measurements);
|
Dictionary<string, double> eventMeasurements = GetEventMeasures(measurements);
|
||||||
|
|
||||||
_client.TrackEvent(eventName, eventProperties, eventMeasurements);
|
_client.TrackEvent(PrependProducerNamespace(eventName), eventProperties, eventMeasurements);
|
||||||
_client.Flush();
|
_client.Flush();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -129,6 +129,11 @@ namespace Microsoft.DotNet.Cli.Telemetry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string PrependProducerNamespace(string eventName)
|
||||||
|
{
|
||||||
|
return "dotnet/cli/" + eventName;
|
||||||
|
}
|
||||||
|
|
||||||
private Dictionary<string, double> GetEventMeasures(IDictionary<string, double> measurements)
|
private Dictionary<string, double> GetEventMeasures(IDictionary<string, double> measurements)
|
||||||
{
|
{
|
||||||
Dictionary<string, double> eventMeasurements = new Dictionary<string, double>(_commonMeasurements);
|
Dictionary<string, double> eventMeasurements = new Dictionary<string, double>(_commonMeasurements);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.DotNet.Cli.CommandLine;
|
using Microsoft.DotNet.Cli.CommandLine;
|
||||||
using Microsoft.DotNet.Cli.Utils;
|
using Microsoft.DotNet.Cli.Utils;
|
||||||
|
@ -11,48 +12,15 @@ namespace Microsoft.DotNet.Cli.Telemetry
|
||||||
internal class TelemetryFilter : ITelemetryFilter
|
internal class TelemetryFilter : ITelemetryFilter
|
||||||
{
|
{
|
||||||
private const string DotnetName = "dotnet";
|
private const string DotnetName = "dotnet";
|
||||||
|
private readonly Func<string, string> _hash;
|
||||||
|
|
||||||
|
public TelemetryFilter(Func<string, string> hash)
|
||||||
|
{
|
||||||
|
_hash = hash ?? throw new ArgumentNullException(nameof(hash));
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<ApplicationInsightsEntryFormat> Filter(object objectToFilter)
|
public IEnumerable<ApplicationInsightsEntryFormat> Filter(object objectToFilter)
|
||||||
{
|
{
|
||||||
var ruleSet = new List<IParseResultLogRule>
|
|
||||||
{ new AllowListToSendFirstArgument(new HashSet<string>{ "new", "help" }),
|
|
||||||
new AllowListToSendFirstAppliedOptions(new HashSet<string>{ "add", "remove", "list", "sln", "nuget" }),
|
|
||||||
new TopLevelCommandNameAndOptionToLog
|
|
||||||
(
|
|
||||||
topLevelCommandName: new HashSet<string> { "new" },
|
|
||||||
optionsToLog: new HashSet<string> { "language" }
|
|
||||||
),
|
|
||||||
new TopLevelCommandNameAndOptionToLog
|
|
||||||
(
|
|
||||||
topLevelCommandName: new HashSet<string> { "build", "publish" },
|
|
||||||
optionsToLog: new HashSet<string> { "framework", "runtime", "configuration" }
|
|
||||||
),
|
|
||||||
new TopLevelCommandNameAndOptionToLog
|
|
||||||
(
|
|
||||||
topLevelCommandName: new HashSet<string> { "run", "clean", "test" },
|
|
||||||
optionsToLog: new HashSet<string> { "framework", "configuration" }
|
|
||||||
),
|
|
||||||
new TopLevelCommandNameAndOptionToLog
|
|
||||||
(
|
|
||||||
topLevelCommandName: new HashSet<string> { "pack" },
|
|
||||||
optionsToLog: new HashSet<string> { "configuration" }
|
|
||||||
),
|
|
||||||
new TopLevelCommandNameAndOptionToLog
|
|
||||||
(
|
|
||||||
topLevelCommandName: new HashSet<string> { "migrate" },
|
|
||||||
optionsToLog: new HashSet<string> { "sdk-package-version" }
|
|
||||||
),
|
|
||||||
new TopLevelCommandNameAndOptionToLog
|
|
||||||
(
|
|
||||||
topLevelCommandName: new HashSet<string> { "vstest" },
|
|
||||||
optionsToLog: new HashSet<string> { "platform", "framework", "logger" }
|
|
||||||
),
|
|
||||||
new TopLevelCommandNameAndOptionToLog
|
|
||||||
(
|
|
||||||
topLevelCommandName: new HashSet<string> { "publish" },
|
|
||||||
optionsToLog: new HashSet<string> { "runtime" }
|
|
||||||
)
|
|
||||||
};
|
|
||||||
var result = new List<ApplicationInsightsEntryFormat>();
|
var result = new List<ApplicationInsightsEntryFormat>();
|
||||||
|
|
||||||
if (objectToFilter is ParseResult parseResult)
|
if (objectToFilter is ParseResult parseResult)
|
||||||
|
@ -62,15 +30,72 @@ namespace Microsoft.DotNet.Cli.Telemetry
|
||||||
{
|
{
|
||||||
LogVerbosityForAllTopLevelCommand(result, parseResult, topLevelCommandName);
|
LogVerbosityForAllTopLevelCommand(result, parseResult, topLevelCommandName);
|
||||||
|
|
||||||
foreach (IParseResultLogRule rule in ruleSet)
|
foreach (IParseResultLogRule rule in ParseResultLogRules)
|
||||||
{
|
{
|
||||||
result.AddRange(rule.AllowList(parseResult));
|
result.AddRange(rule.AllowList(parseResult));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(objectToFilter is TopLevelCommandParserResult topLevelCommandParserResult)
|
||||||
|
{
|
||||||
|
result.Add(new ApplicationInsightsEntryFormat(
|
||||||
|
"toplevelparser/command",
|
||||||
|
new Dictionary<string, string>()
|
||||||
|
{{ "verb", topLevelCommandParserResult.Command}}
|
||||||
|
));
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
else if (objectToFilter is InstallerSuccessReport installerSuccessReport)
|
||||||
|
{
|
||||||
|
result.Add(new ApplicationInsightsEntryFormat(
|
||||||
|
"install/reportsuccess",
|
||||||
|
new Dictionary<string, string> {{ "exeName", installerSuccessReport.ExeName}}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.Select(r => r.WithAppliedToPropertiesValue(_hash)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<IParseResultLogRule> ParseResultLogRules => new List<IParseResultLogRule>
|
||||||
|
{
|
||||||
|
new AllowListToSendFirstArgument(new HashSet<string> {"new", "help"}),
|
||||||
|
new AllowListToSendFirstAppliedOptions(new HashSet<string> {"add", "remove", "list", "sln", "nuget"}),
|
||||||
|
new TopLevelCommandNameAndOptionToLog
|
||||||
|
(
|
||||||
|
topLevelCommandName: new HashSet<string> {"new"},
|
||||||
|
optionsToLog: new HashSet<string> {"language"}
|
||||||
|
),
|
||||||
|
new TopLevelCommandNameAndOptionToLog
|
||||||
|
(
|
||||||
|
topLevelCommandName: new HashSet<string> {"build", "publish"},
|
||||||
|
optionsToLog: new HashSet<string> {"framework", "runtime", "configuration"}
|
||||||
|
),
|
||||||
|
new TopLevelCommandNameAndOptionToLog
|
||||||
|
(
|
||||||
|
topLevelCommandName: new HashSet<string> {"run", "clean", "test"},
|
||||||
|
optionsToLog: new HashSet<string> {"framework", "configuration"}
|
||||||
|
),
|
||||||
|
new TopLevelCommandNameAndOptionToLog
|
||||||
|
(
|
||||||
|
topLevelCommandName: new HashSet<string> {"pack"},
|
||||||
|
optionsToLog: new HashSet<string> {"configuration"}
|
||||||
|
),
|
||||||
|
new TopLevelCommandNameAndOptionToLog
|
||||||
|
(
|
||||||
|
topLevelCommandName: new HashSet<string> {"migrate"},
|
||||||
|
optionsToLog: new HashSet<string> {"sdk-package-version"}
|
||||||
|
),
|
||||||
|
new TopLevelCommandNameAndOptionToLog
|
||||||
|
(
|
||||||
|
topLevelCommandName: new HashSet<string> {"vstest"},
|
||||||
|
optionsToLog: new HashSet<string> {"platform", "framework", "logger"}
|
||||||
|
),
|
||||||
|
new TopLevelCommandNameAndOptionToLog
|
||||||
|
(
|
||||||
|
topLevelCommandName: new HashSet<string> {"publish"},
|
||||||
|
optionsToLog: new HashSet<string> {"runtime"}
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
private static void LogVerbosityForAllTopLevelCommand(
|
private static void LogVerbosityForAllTopLevelCommand(
|
||||||
ICollection<ApplicationInsightsEntryFormat> result,
|
ICollection<ApplicationInsightsEntryFormat> result,
|
||||||
|
@ -84,9 +109,10 @@ namespace Microsoft.DotNet.Cli.Telemetry
|
||||||
parseResult[DotnetName][topLevelCommandName].AppliedOptions["verbosity"];
|
parseResult[DotnetName][topLevelCommandName].AppliedOptions["verbosity"];
|
||||||
|
|
||||||
result.Add(new ApplicationInsightsEntryFormat(
|
result.Add(new ApplicationInsightsEntryFormat(
|
||||||
"dotnet-" + topLevelCommandName,
|
"sublevelparser/command",
|
||||||
new Dictionary<string, string>()
|
new Dictionary<string, string>()
|
||||||
{
|
{
|
||||||
|
{ "verb", topLevelCommandName},
|
||||||
{"verbosity", appliedOptions.Arguments.ElementAt(0)}
|
{"verbosity", appliedOptions.Arguments.ElementAt(0)}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,9 +36,10 @@ namespace Microsoft.DotNet.Cli.Telemetry
|
||||||
parseResult[DotnetName][topLevelCommandName]
|
parseResult[DotnetName][topLevelCommandName]
|
||||||
.AppliedOptions[option];
|
.AppliedOptions[option];
|
||||||
result.Add(new ApplicationInsightsEntryFormat(
|
result.Add(new ApplicationInsightsEntryFormat(
|
||||||
"dotnet-" + topLevelCommandName,
|
"sublevelparser/command",
|
||||||
new Dictionary<string, string>
|
new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
|
{ "verb", topLevelCommandName},
|
||||||
{option, appliedOptions.Arguments.ElementAt(0)}
|
{option, appliedOptions.Arguments.ElementAt(0)}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
21
src/dotnet/TopLevelCommandParserResult.cs
Normal file
21
src/dotnet/TopLevelCommandParserResult.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Cli
|
||||||
|
{
|
||||||
|
internal class TopLevelCommandParserResult
|
||||||
|
{
|
||||||
|
public static TopLevelCommandParserResult Empty
|
||||||
|
{
|
||||||
|
get { return new TopLevelCommandParserResult(string.Empty); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Command { get; }
|
||||||
|
|
||||||
|
public TopLevelCommandParserResult(string command)
|
||||||
|
{
|
||||||
|
Command = command;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.DotNet.Cli.CommandLine;
|
||||||
using Microsoft.DotNet.Configurer;
|
using Microsoft.DotNet.Configurer;
|
||||||
using Microsoft.DotNet.Cli.Telemetry;
|
using Microsoft.DotNet.Cli.Telemetry;
|
||||||
|
|
||||||
|
@ -28,12 +29,13 @@ namespace Microsoft.DotNet.Cli
|
||||||
var result = parser.ParseFrom("dotnet internal-reportinstallsuccess", args);
|
var result = parser.ParseFrom("dotnet internal-reportinstallsuccess", args);
|
||||||
|
|
||||||
var internalReportinstallsuccess = result["dotnet"]["internal-reportinstallsuccess"];
|
var internalReportinstallsuccess = result["dotnet"]["internal-reportinstallsuccess"];
|
||||||
|
|
||||||
var exeName = Path.GetFileName(internalReportinstallsuccess.Arguments.Single());
|
var exeName = Path.GetFileName(internalReportinstallsuccess.Arguments.Single());
|
||||||
telemetry.TrackEvent(
|
|
||||||
"reportinstallsuccess",
|
var filter = new TelemetryFilter(Sha256Hasher.HashWithNormalizedCasing);
|
||||||
new Dictionary<string, string> { { "exeName", exeName } },
|
foreach (var e in filter.Filter(new InstallerSuccessReport(exeName)))
|
||||||
new Dictionary<string, double>());
|
{
|
||||||
|
telemetry.TrackEvent(e.EventName, e.Properties, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class ThreadBlockingTelemetry : ITelemetry
|
internal class ThreadBlockingTelemetry : ITelemetry
|
||||||
|
@ -54,4 +56,14 @@ namespace Microsoft.DotNet.Cli
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class InstallerSuccessReport
|
||||||
|
{
|
||||||
|
public string ExeName { get; }
|
||||||
|
|
||||||
|
public InstallerSuccessReport(string exeName)
|
||||||
|
{
|
||||||
|
ExeName = exeName ?? throw new ArgumentNullException(nameof(exeName));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -7,6 +7,7 @@ using Microsoft.Build.Utilities;
|
||||||
using Microsoft.DotNet.Cli;
|
using Microsoft.DotNet.Cli;
|
||||||
using Microsoft.DotNet.Cli.Telemetry;
|
using Microsoft.DotNet.Cli.Telemetry;
|
||||||
using Microsoft.DotNet.Configurer;
|
using Microsoft.DotNet.Configurer;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Tools.MSBuild
|
namespace Microsoft.DotNet.Tools.MSBuild
|
||||||
{
|
{
|
||||||
|
@ -15,6 +16,9 @@ namespace Microsoft.DotNet.Tools.MSBuild
|
||||||
private readonly IFirstTimeUseNoticeSentinel _sentinel =
|
private readonly IFirstTimeUseNoticeSentinel _sentinel =
|
||||||
new FirstTimeUseNoticeSentinel(new CliFallbackFolderPathCalculator());
|
new FirstTimeUseNoticeSentinel(new CliFallbackFolderPathCalculator());
|
||||||
private readonly ITelemetry _telemetry;
|
private readonly ITelemetry _telemetry;
|
||||||
|
private const string NewEventName = "msbuild";
|
||||||
|
private const string TargetFrameworkTelemetryEventName = "targetframeworkeval";
|
||||||
|
private const string TargetFrameworkVersionTelemetryPropertyKey= "TargetFrameworkVersion";
|
||||||
|
|
||||||
public MSBuildLogger()
|
public MSBuildLogger()
|
||||||
{
|
{
|
||||||
|
@ -52,9 +56,24 @@ namespace Microsoft.DotNet.Tools.MSBuild
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void FormatAndSend(ITelemetry telemetry, TelemetryEventArgs args)
|
||||||
|
{
|
||||||
|
if (args.EventName == TargetFrameworkTelemetryEventName)
|
||||||
|
{
|
||||||
|
var newEventName = $"msbuild/{TargetFrameworkTelemetryEventName}";
|
||||||
|
Dictionary<string, string> maskedProperties = new Dictionary<string, string>();
|
||||||
|
if (args.Properties.TryGetValue(TargetFrameworkVersionTelemetryPropertyKey, out string value))
|
||||||
|
{
|
||||||
|
maskedProperties.Add(TargetFrameworkVersionTelemetryPropertyKey, Sha256Hasher.HashWithNormalizedCasing(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
telemetry.TrackEvent(newEventName, maskedProperties, measurements: null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnTelemetryLogged(object sender, TelemetryEventArgs args)
|
private void OnTelemetryLogged(object sender, TelemetryEventArgs args)
|
||||||
{
|
{
|
||||||
_telemetry.TrackEvent(args.EventName, args.Properties, measurements: null);
|
FormatAndSend(_telemetry, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace Microsoft.DotNet.Tools.New
|
||||||
{
|
{
|
||||||
if (telemetry.Enabled)
|
if (telemetry.Enabled)
|
||||||
{
|
{
|
||||||
telemetry.TrackEvent(name, props, measures);
|
telemetry.TrackEvent($"template/{name}", props, measures);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
76
test/dotnet-msbuild.Tests/GivenMSBuildLogger.cs
Normal file
76
test/dotnet-msbuild.Tests/GivenMSBuildLogger.cs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
using FluentAssertions;
|
||||||
|
using Xunit;
|
||||||
|
using Microsoft.DotNet.Tools.MSBuild;
|
||||||
|
using Microsoft.DotNet.Cli.Telemetry;
|
||||||
|
using Microsoft.Build.Framework;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Cli.MSBuild.Tests
|
||||||
|
{
|
||||||
|
public class GivenMSBuildLogger
|
||||||
|
{
|
||||||
|
[Fact(DisplayName = "It blocks telemetry that is not in the list")]
|
||||||
|
public void ItBlocks()
|
||||||
|
{
|
||||||
|
var fakeTelemetry = new FakeTelemetry();
|
||||||
|
var telemetryEventArgs = new TelemetryEventArgs
|
||||||
|
{
|
||||||
|
EventName = "User Defined Event Name",
|
||||||
|
Properties = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "User Defined Key", "User Defined Value"},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MSBuildLogger.FormatAndSend(fakeTelemetry, telemetryEventArgs);
|
||||||
|
|
||||||
|
fakeTelemetry.LogEntry.Should().BeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact(DisplayName = "It masks event name with targetframeworkeval only on TargetFrameworkVersion")]
|
||||||
|
public void ItMasksTargetFrameworkEventname()
|
||||||
|
{
|
||||||
|
var fakeTelemetry = new FakeTelemetry();
|
||||||
|
var telemetryEventArgs = new TelemetryEventArgs
|
||||||
|
{
|
||||||
|
EventName = "targetframeworkeval",
|
||||||
|
Properties = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "TargetFrameworkVersion", ".NETStandard,Version=v2.0"},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MSBuildLogger.FormatAndSend(fakeTelemetry, telemetryEventArgs);
|
||||||
|
|
||||||
|
fakeTelemetry.LogEntry.EventName.Should().Be("msbuild/targetframeworkeval");
|
||||||
|
fakeTelemetry.LogEntry.Properties.Keys.Count.Should().Be(1);
|
||||||
|
var expectedKey = "TargetFrameworkVersion";
|
||||||
|
fakeTelemetry.LogEntry.Properties.Should().ContainKey(expectedKey);
|
||||||
|
fakeTelemetry.LogEntry.Properties[expectedKey].Should().Be(Sha256Hasher.Hash(".NETSTANDARD,VERSION=V2.0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FakeTelemetry : ITelemetry
|
||||||
|
{
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
|
||||||
|
public void TrackEvent(string eventName, IDictionary<string, string> properties, IDictionary<string, double> measurements)
|
||||||
|
{
|
||||||
|
LogEntry = new LogEntry { EventName = eventName, Properties = properties, Measurement = measurements };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogEntry LogEntry { get; private set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LogEntry
|
||||||
|
{
|
||||||
|
public string EventName { get; set; }
|
||||||
|
public IDictionary<string, string> Properties { get; set; }
|
||||||
|
public IDictionary<string, double> Measurement { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,116 +16,144 @@ namespace Microsoft.DotNet.Tests
|
||||||
private readonly FakeRecordEventNameTelemetry _fakeTelemetry;
|
private readonly FakeRecordEventNameTelemetry _fakeTelemetry;
|
||||||
|
|
||||||
public string EventName { get; set; }
|
public string EventName { get; set; }
|
||||||
|
|
||||||
public IDictionary<string, string> Properties { get; set; }
|
public IDictionary<string, string> Properties { get; set; }
|
||||||
|
|
||||||
public TelemetryCommandTests()
|
public TelemetryCommandTests()
|
||||||
{
|
{
|
||||||
_fakeTelemetry = new FakeRecordEventNameTelemetry();
|
_fakeTelemetry = new FakeRecordEventNameTelemetry();
|
||||||
TelemetryEventEntry.Subscribe(_fakeTelemetry.TrackEvent);
|
TelemetryEventEntry.Subscribe(_fakeTelemetry.TrackEvent);
|
||||||
TelemetryEventEntry.TelemetryFilter = new TelemetryFilter();
|
TelemetryEventEntry.TelemetryFilter = new TelemetryFilter(Sha256Hasher.HashWithNormalizedCasing);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TopLevelCommandNameShouldBeSentToTelemetry()
|
public void TopLevelCommandNameShouldBeSentToTelemetry()
|
||||||
{
|
{
|
||||||
string[] args = {"help"};
|
string[] args = { "help" };
|
||||||
Cli.Program.ProcessArgs(args);
|
Cli.Program.ProcessArgs(args);
|
||||||
_fakeTelemetry.LogEntries.Should().Contain(e => e.EventName == args[0]);
|
|
||||||
|
_fakeTelemetry.LogEntries.Should().Contain(e => e.EventName == "toplevelparser/command" &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("HELP"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DotnetNewCommandFirstArgumentShouldBeSentToTelemetry()
|
public void DotnetNewCommandFirstArgumentShouldBeSentToTelemetry()
|
||||||
{
|
{
|
||||||
const string argumentToSend = "console";
|
const string argumentToSend = "console";
|
||||||
string[] args = {"new", argumentToSend};
|
string[] args = { "new", argumentToSend };
|
||||||
Cli.Program.ProcessArgs(args);
|
Cli.Program.ProcessArgs(args);
|
||||||
_fakeTelemetry
|
_fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "dotnet-new" && e.Properties.ContainsKey("argument") &&
|
.Contain(e => e.EventName == "sublevelparser/command" &&
|
||||||
e.Properties["argument"] == argumentToSend);
|
e.Properties.ContainsKey("argument") &&
|
||||||
|
e.Properties["argument"] == Sha256Hasher.Hash(argumentToSend.ToUpper()) &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("NEW"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DotnetHelpCommandFirstArgumentShouldBeSentToTelemetry()
|
public void DotnetHelpCommandFirstArgumentShouldBeSentToTelemetry()
|
||||||
{
|
{
|
||||||
const string argumentToSend = "something";
|
const string argumentToSend = "something";
|
||||||
string[] args = {"help", argumentToSend};
|
string[] args = { "help", argumentToSend };
|
||||||
Cli.Program.ProcessArgs(args);
|
Cli.Program.ProcessArgs(args);
|
||||||
_fakeTelemetry
|
_fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "dotnet-help" && e.Properties.ContainsKey("argument") &&
|
.Contain(e => e.EventName == "sublevelparser/command" &&
|
||||||
e.Properties["argument"] == argumentToSend);
|
e.Properties.ContainsKey("argument") &&
|
||||||
|
e.Properties["argument"] == Sha256Hasher.Hash(argumentToSend.ToUpper()) &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("HELP"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DotnetAddCommandFirstArgumentShouldBeSentToTelemetry()
|
public void DotnetAddCommandFirstArgumentShouldBeSentToTelemetry()
|
||||||
{
|
{
|
||||||
const string argumentToSend = "package";
|
const string argumentToSend = "package";
|
||||||
string[] args = {"add", argumentToSend, "aPackageName"};
|
string[] args = { "add", argumentToSend, "aPackageName" };
|
||||||
Cli.Program.ProcessArgs(args);
|
Cli.Program.ProcessArgs(args);
|
||||||
_fakeTelemetry
|
_fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "dotnet-add" && e.Properties.ContainsKey("argument") &&
|
.Contain(e => e.EventName == "sublevelparser/command" &&
|
||||||
e.Properties["argument"] == argumentToSend);
|
e.Properties.ContainsKey("argument") &&
|
||||||
|
e.Properties["argument"] == Sha256Hasher.Hash(argumentToSend.ToUpper()) &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("ADD"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DotnetAddCommandFirstArgumentShouldBeSentToTelemetry2()
|
public void DotnetAddCommandFirstArgumentShouldBeSentToTelemetry2()
|
||||||
{
|
{
|
||||||
const string argumentToSend = "reference";
|
const string argumentToSend = "reference";
|
||||||
string[] args = {"add", argumentToSend, "aPackageName"};
|
string[] args = { "add", argumentToSend, "aPackageName" };
|
||||||
Cli.Program.ProcessArgs(args);
|
Cli.Program.ProcessArgs(args);
|
||||||
_fakeTelemetry
|
_fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "dotnet-add" && e.Properties.ContainsKey("argument") &&
|
.Contain(e => e.EventName == "sublevelparser/command" &&
|
||||||
e.Properties["argument"] == argumentToSend);
|
e.Properties.ContainsKey("argument") &&
|
||||||
|
e.Properties["argument"] == Sha256Hasher.Hash(argumentToSend.ToUpper()) &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("ADD"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DotnetRemoveCommandFirstArgumentShouldBeSentToTelemetry()
|
public void DotnetRemoveCommandFirstArgumentShouldBeSentToTelemetry()
|
||||||
{
|
{
|
||||||
const string argumentToSend = "package";
|
const string argumentToSend = "package";
|
||||||
string[] args = {"remove", argumentToSend, "aPackageName"};
|
string[] args = { "remove", argumentToSend, "aPackageName" };
|
||||||
Cli.Program.ProcessArgs(args);
|
Cli.Program.ProcessArgs(args);
|
||||||
_fakeTelemetry
|
_fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "dotnet-remove" && e.Properties.ContainsKey("argument") &&
|
.Contain(e => e.EventName == "sublevelparser/command" &&
|
||||||
e.Properties["argument"] == argumentToSend);
|
e.Properties.ContainsKey("argument") &&
|
||||||
|
e.Properties["argument"] == Sha256Hasher.Hash(argumentToSend.ToUpper()) &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("REMOVE"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DotnetListCommandFirstArgumentShouldBeSentToTelemetry()
|
public void DotnetListCommandFirstArgumentShouldBeSentToTelemetry()
|
||||||
{
|
{
|
||||||
const string argumentToSend = "reference";
|
const string argumentToSend = "reference";
|
||||||
string[] args = {"list", argumentToSend, "aPackageName"};
|
string[] args = { "list", argumentToSend, "aPackageName" };
|
||||||
Cli.Program.ProcessArgs(args);
|
Cli.Program.ProcessArgs(args);
|
||||||
_fakeTelemetry
|
_fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "dotnet-list" && e.Properties.ContainsKey("argument") &&
|
.Contain(e => e.EventName == "sublevelparser/command" && e.Properties.ContainsKey("argument") &&
|
||||||
e.Properties["argument"] == argumentToSend);
|
e.Properties["argument"] == Sha256Hasher.Hash(argumentToSend.ToUpper()) &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("LIST"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DotnetSlnCommandFirstArgumentShouldBeSentToTelemetry()
|
public void DotnetSlnCommandFirstArgumentShouldBeSentToTelemetry()
|
||||||
{
|
{
|
||||||
const string argumentToSend = "list";
|
const string argumentToSend = "list";
|
||||||
string[] args = {"sln", "aSolution", argumentToSend};
|
string[] args = { "sln", "aSolution", argumentToSend };
|
||||||
Cli.Program.ProcessArgs(args);
|
Cli.Program.ProcessArgs(args);
|
||||||
_fakeTelemetry
|
_fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "dotnet-sln" && e.Properties.ContainsKey("argument") &&
|
.Contain(e => e.EventName == "sublevelparser/command" &&
|
||||||
e.Properties["argument"] == argumentToSend);
|
e.Properties.ContainsKey("argument") &&
|
||||||
|
e.Properties["argument"] == Sha256Hasher.Hash(argumentToSend.ToUpper()) &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("SLN"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DotnetNugetCommandFirstArgumentShouldBeSentToTelemetry()
|
public void DotnetNugetCommandFirstArgumentShouldBeSentToTelemetry()
|
||||||
{
|
{
|
||||||
const string argumentToSend = "push";
|
const string argumentToSend = "push";
|
||||||
string[] args = {"nuget", argumentToSend, "aRoot"};
|
string[] args = { "nuget", argumentToSend, "aRoot" };
|
||||||
Cli.Program.ProcessArgs(args);
|
Cli.Program.ProcessArgs(args);
|
||||||
_fakeTelemetry
|
_fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "dotnet-nuget" && e.Properties.ContainsKey("argument") &&
|
.Contain(e => e.EventName == "sublevelparser/command" &&
|
||||||
e.Properties["argument"] == argumentToSend);
|
e.Properties.ContainsKey("argument") &&
|
||||||
|
e.Properties["argument"] == Sha256Hasher.Hash(argumentToSend.ToUpper()) &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("NUGET"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -133,12 +161,14 @@ namespace Microsoft.DotNet.Tests
|
||||||
{
|
{
|
||||||
const string optionKey = "language";
|
const string optionKey = "language";
|
||||||
const string optionValueToSend = "c#";
|
const string optionValueToSend = "c#";
|
||||||
string[] args = {"new", "console", "--" + optionKey, optionValueToSend};
|
string[] args = { "new", "console", "--" + optionKey, optionValueToSend };
|
||||||
Cli.Program.ProcessArgs(args);
|
Cli.Program.ProcessArgs(args);
|
||||||
_fakeTelemetry
|
_fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "dotnet-new" && e.Properties.ContainsKey(optionKey) &&
|
.Contain(e => e.EventName == "sublevelparser/command" && e.Properties.ContainsKey(optionKey) &&
|
||||||
e.Properties[optionKey] == optionValueToSend);
|
e.Properties[optionKey] == Sha256Hasher.Hash(optionValueToSend.ToUpper()) &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("NEW"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -146,12 +176,15 @@ namespace Microsoft.DotNet.Tests
|
||||||
{
|
{
|
||||||
const string optionKey = "verbosity";
|
const string optionKey = "verbosity";
|
||||||
const string optionValueToSend = "minimal";
|
const string optionValueToSend = "minimal";
|
||||||
string[] args = {"restore", "--" + optionKey, optionValueToSend};
|
string[] args = { "restore", "--" + optionKey, optionValueToSend };
|
||||||
Cli.Program.ProcessArgs(args);
|
Cli.Program.ProcessArgs(args);
|
||||||
_fakeTelemetry
|
_fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "dotnet-restore" && e.Properties.ContainsKey(optionKey) &&
|
.Contain(e => e.EventName == "sublevelparser/command" &&
|
||||||
e.Properties[optionKey] == optionValueToSend);
|
e.Properties.ContainsKey(optionKey) &&
|
||||||
|
e.Properties[optionKey] == Sha256Hasher.Hash(optionValueToSend.ToUpper()) &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("RESTORE"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -159,12 +192,15 @@ namespace Microsoft.DotNet.Tests
|
||||||
{
|
{
|
||||||
const string optionKey = "configuration";
|
const string optionKey = "configuration";
|
||||||
const string optionValueToSend = "Debug";
|
const string optionValueToSend = "Debug";
|
||||||
string[] args = {"build", "--" + optionKey, optionValueToSend};
|
string[] args = { "build", "--" + optionKey, optionValueToSend };
|
||||||
Cli.Program.ProcessArgs(args);
|
Cli.Program.ProcessArgs(args);
|
||||||
_fakeTelemetry
|
_fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "dotnet-build" && e.Properties.ContainsKey(optionKey) &&
|
.Contain(e => e.EventName == "sublevelparser/command" &&
|
||||||
e.Properties[optionKey] == optionValueToSend);
|
e.Properties.ContainsKey(optionKey) &&
|
||||||
|
e.Properties[optionKey] == Sha256Hasher.Hash(optionValueToSend.ToUpper()) &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("BUILD"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -176,40 +212,51 @@ namespace Microsoft.DotNet.Tests
|
||||||
Cli.Program.ProcessArgs(args);
|
Cli.Program.ProcessArgs(args);
|
||||||
_fakeTelemetry
|
_fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "dotnet-publish" && e.Properties.ContainsKey(optionKey) &&
|
.Contain(e => e.EventName == "sublevelparser/command" &&
|
||||||
e.Properties[optionKey] == optionValueToSend);
|
e.Properties.ContainsKey(optionKey) &&
|
||||||
|
e.Properties[optionKey] == Sha256Hasher.Hash(optionValueToSend.ToUpper()) &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("PUBLISH"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DotnetBuildAndPublishCommandOpinionsShouldBeSentToTelemetryWhenThereIsMultipleOption()
|
public void DotnetBuildAndPublishCommandOpinionsShouldBeSentToTelemetryWhenThereIsMultipleOption()
|
||||||
{
|
{
|
||||||
string[] args = {"build", "--configuration", "Debug", "--runtime", "osx.10.11-x64"};
|
string[] args = { "build", "--configuration", "Debug", "--runtime", "osx.10.11-x64" };
|
||||||
Cli.Program.ProcessArgs(args);
|
Cli.Program.ProcessArgs(args);
|
||||||
_fakeTelemetry
|
_fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "dotnet-build" && e.Properties.ContainsKey("configuration") &&
|
.Contain(e => e.EventName == "sublevelparser/command" && e.Properties.ContainsKey("configuration") &&
|
||||||
e.Properties["configuration"] == "Debug");
|
e.Properties["configuration"] == Sha256Hasher.Hash("DEBUG") &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("BUILD"));
|
||||||
|
|
||||||
_fakeTelemetry
|
_fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "dotnet-build" && e.Properties.ContainsKey("runtime") &&
|
.Contain(e => e.EventName == "sublevelparser/command" && e.Properties.ContainsKey("runtime") &&
|
||||||
e.Properties["runtime"] == "osx.10.11-x64");
|
e.Properties["runtime"] == Sha256Hasher.Hash("OSX.10.11-X64") &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("BUILD"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DotnetRunCleanTestCommandOpinionsShouldBeSentToTelemetryWhenThereIsMultipleOption()
|
public void DotnetRunCleanTestCommandOpinionsShouldBeSentToTelemetryWhenThereIsMultipleOption()
|
||||||
{
|
{
|
||||||
string[] args = {"clean", "--configuration", "Debug", "--framework", "netcoreapp1.0"};
|
string[] args = { "clean", "--configuration", "Debug", "--framework", "netcoreapp1.0" };
|
||||||
Cli.Program.ProcessArgs(args);
|
Cli.Program.ProcessArgs(args);
|
||||||
_fakeTelemetry
|
_fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "dotnet-clean" && e.Properties.ContainsKey("configuration") &&
|
.Contain(e => e.EventName == "sublevelparser/command" && e.Properties.ContainsKey("configuration") &&
|
||||||
e.Properties["configuration"] == "Debug");
|
e.Properties["configuration"] == Sha256Hasher.Hash("DEBUG") &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("CLEAN"));
|
||||||
|
|
||||||
_fakeTelemetry
|
_fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "dotnet-clean" && e.Properties.ContainsKey("framework") &&
|
.Contain(e => e.EventName == "sublevelparser/command" && e.Properties.ContainsKey("framework") &&
|
||||||
e.Properties["framework"] == "netcoreapp1.0");
|
e.Properties["framework"] == Sha256Hasher.Hash("NETCOREAPP1.0") &&
|
||||||
|
e.Properties.ContainsKey("verb") &&
|
||||||
|
e.Properties["verb"] == Sha256Hasher.Hash("CLEAN"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[WindowsOnlyFact]
|
[WindowsOnlyFact]
|
||||||
|
@ -222,8 +269,8 @@ namespace Microsoft.DotNet.Tests
|
||||||
|
|
||||||
fakeTelemetry
|
fakeTelemetry
|
||||||
.LogEntries.Should()
|
.LogEntries.Should()
|
||||||
.Contain(e => e.EventName == "reportinstallsuccess" && e.Properties.ContainsKey("exeName") &&
|
.Contain(e => e.EventName == "install/reportsuccess" && e.Properties.ContainsKey("exeName") &&
|
||||||
e.Properties["exeName"] == "dotnet-sdk-latest-win-x64.exe");
|
e.Properties["exeName"] == Sha256Hasher.Hash("DOTNET-SDK-LATEST-WIN-X64.EXE"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
||||||
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.DotNet.Cli;
|
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Cli.MSBuild.IntegrationTests
|
|
||||||
{
|
|
||||||
public class FakeRecordEventNameTelemetry
|
|
||||||
{
|
|
||||||
public bool Enabled { get; set; }
|
|
||||||
|
|
||||||
public string EventName { get; set; }
|
|
||||||
|
|
||||||
public void TrackEvent(string eventName,
|
|
||||||
IDictionary<string, string> properties,
|
|
||||||
IDictionary<string, double> measurements)
|
|
||||||
{
|
|
||||||
LogEntries.Add(
|
|
||||||
new LogEntry
|
|
||||||
{
|
|
||||||
EventName = eventName,
|
|
||||||
Measurement = measurements,
|
|
||||||
Properties = properties
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConcurrentBag<LogEntry> LogEntries { get; set; } = new ConcurrentBag<LogEntry>();
|
|
||||||
|
|
||||||
public class LogEntry
|
|
||||||
{
|
|
||||||
public string EventName { get; set; }
|
|
||||||
public IDictionary<string, string> Properties { get; set; }
|
|
||||||
public IDictionary<string, double> Measurement { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue