提交 6e7ec2c2 编写于 作者: H Heejae Chang 提交者: GitHub

Merge pull request #19598 from heejaechang/fxcoptel

log info for ab testing
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Experimentation;
using Microsoft.CodeAnalysis.Shared.Options; using Microsoft.CodeAnalysis.Shared.Options;
using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Microsoft.CodeAnalysis.Workspaces.Diagnostics;
using Roslyn.Utilities; using Roslyn.Utilities;
...@@ -223,6 +224,8 @@ public async Task SaveAsync(Project project, DiagnosticAnalysisResult result) ...@@ -223,6 +224,8 @@ public async Task SaveAsync(Project project, DiagnosticAnalysisResult result)
} }
await SerializeAsync(serializer, project, result.ProjectId, _owner.NonLocalStateName, result.Others).ConfigureAwait(false); await SerializeAsync(serializer, project, result.ProjectId, _owner.NonLocalStateName, result.Others).ConfigureAwait(false);
AnalyzerABTestLogger.LogProjectDiagnostics(project, result);
} }
public void ResetVersion() public void ResetVersion()
...@@ -241,6 +244,8 @@ public async Task MergeAsync(ActiveFileState state, Document document) ...@@ -241,6 +244,8 @@ public async Task MergeAsync(ActiveFileState state, Document document)
var syntax = state.GetAnalysisData(AnalysisKind.Syntax); var syntax = state.GetAnalysisData(AnalysisKind.Syntax);
var semantic = state.GetAnalysisData(AnalysisKind.Semantic); var semantic = state.GetAnalysisData(AnalysisKind.Semantic);
AnalyzerABTestLogger.LogDocumentDiagnostics(document, syntax.Items, semantic.Items);
var project = document.Project; var project = document.Project;
// if project didn't successfully loaded, then it is same as FSA off // if project didn't successfully loaded, then it is same as FSA off
......
// Copyright (c) Microsoft. 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.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Shared.Options;
using Microsoft.CodeAnalysis.Workspaces.Diagnostics;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Experimentation
{
internal static class AnalyzerABTestLogger
{
private static bool s_reportErrors = false;
private static readonly ConcurrentDictionary<object, object> s_reported = new ConcurrentDictionary<object, object>(concurrencyLevel: 2, capacity: 10);
private const string Name = "LiveCodeAnalysisVsix";
public static void Log(string action)
{
Logger.Log(FunctionId.Experiment_ABTesting, KeyValueLogMessage.Create(LogType.UserAction, m =>
{
m[nameof(Name)] = Name;
m[nameof(action)] = action;
}));
}
public static void LogInstallationStatus(Workspace workspace, LiveCodeAnalysisInstallStatus installStatus)
{
var vsixInstalled = workspace.Options.GetOption(AnalyzerABTestOptions.VsixInstalled);
if (!vsixInstalled && installStatus == LiveCodeAnalysisInstallStatus.Installed)
{
// first time after vsix installed
workspace.Options = workspace.Options.WithChangedOption(AnalyzerABTestOptions.VsixInstalled, true);
workspace.Options = workspace.Options.WithChangedOption(AnalyzerABTestOptions.ParticipatedInExperiment, true);
Log("Installed");
// set the system to report the errors.
s_reportErrors = true;
}
if (vsixInstalled && installStatus == LiveCodeAnalysisInstallStatus.NotInstalled)
{
// first time after vsix is uninstalled
workspace.Options = workspace.Options.WithChangedOption(AnalyzerABTestOptions.VsixInstalled, false);
Log("Uninstalled");
}
}
public static void LogCandidacyRequirementsTracking(long lastTriggeredTimeBinary)
{
if (lastTriggeredTimeBinary == AnalyzerABTestOptions.LastDateTimeUsedSuggestionAction.DefaultValue)
{
Log("StartCandidacyRequirementsTracking");
}
}
public static void LogProjectDiagnostics(Project project, DiagnosticAnalysisResult result)
{
if (!s_reportErrors || !s_reported.TryAdd(project.Id, null))
{
// doesn't meet the bar to report the issue.
return;
}
// logs count of errors for this project. this won't log anything if FSA off since
// we don't collect any diagnostics for a project if FSA is off.
var map = new Dictionary<string, int>();
foreach (var documentId in result.DocumentIdsOrEmpty)
{
CountErrors(map, result.GetResultOrEmpty(result.SyntaxLocals, documentId));
CountErrors(map, result.GetResultOrEmpty(result.SemanticLocals, documentId));
CountErrors(map, result.GetResultOrEmpty(result.NonLocals, documentId));
}
CountErrors(map, result.Others);
LogErrors(project, "ProjectDignostics", project.Id.Id, map);
}
public static void LogDocumentDiagnostics(Document document, ImmutableArray<DiagnosticData> syntax, ImmutableArray<DiagnosticData> semantic)
{
if (!s_reportErrors || !s_reported.TryAdd(document.Id, null))
{
// doesn't meet the bar to report the issue.
return;
}
// logs count of errors for this document. this only logs errors for
// this particular document. we do this since when FSA is off, this is
// only errors we get. otherwise, we don't get any info when FSA is off and
// that is default for C#.
var map = new Dictionary<string, int>();
CountErrors(map, syntax);
CountErrors(map, semantic);
LogErrors(document.Project, "DocumentDignostics", document.Id.Id, map);
}
private static void LogErrors(Project project, string action, Guid target, Dictionary<string, int> map)
{
if (map.Count == 0)
{
// nothing to report
return;
}
var fsa = ServiceFeatureOnOffOptions.IsClosedFileDiagnosticsEnabled(project);
Logger.Log(FunctionId.Experiment_ABTesting, KeyValueLogMessage.Create(LogType.UserAction, m =>
{
m[nameof(Name)] = Name;
m[nameof(action)] = action;
m[nameof(target)] = target.ToString();
m["FSA"] = fsa;
m["errors"] = string.Join("|", map.Select(kv => $"{kv.Key}={kv.Value}"));
}));
}
private static void CountErrors(Dictionary<string, int> map, ImmutableArray<DiagnosticData> diagnostics)
{
if (diagnostics.IsDefaultOrEmpty)
{
return;
}
foreach (var group in diagnostics.GroupBy(d => d.Id))
{
map[group.Key] = IDictionaryExtensions.GetValueOrDefault(map, group.Key) + group.Count();
}
}
}
}
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
using System; using System;
using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Options;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Experimentation namespace Microsoft.CodeAnalysis.Experimentation
{ {
internal static class AnalyzerABTestOptions internal static class AnalyzerABTestOptions
{ {
...@@ -25,5 +25,13 @@ internal static class AnalyzerABTestOptions ...@@ -25,5 +25,13 @@ internal static class AnalyzerABTestOptions
public static readonly Option<long> LastDateTimeInfoBarShown = new Option<long>(nameof(AnalyzerABTestOptions), nameof(LastDateTimeInfoBarShown), public static readonly Option<long> LastDateTimeInfoBarShown = new Option<long>(nameof(AnalyzerABTestOptions), nameof(LastDateTimeInfoBarShown),
defaultValue: DateTime.MinValue.ToBinary(), defaultValue: DateTime.MinValue.ToBinary(),
storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + nameof(LastDateTimeInfoBarShown))); storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + nameof(LastDateTimeInfoBarShown)));
public static readonly Option<bool> VsixInstalled = new Option<bool>(nameof(AnalyzerABTestOptions),
nameof(VsixInstalled), defaultValue: false,
storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + nameof(VsixInstalled)));
public static readonly Option<bool> ParticipatedInExperiment = new Option<bool>(nameof(AnalyzerABTestOptions),
nameof(ParticipatedInExperiment), defaultValue: false,
storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + nameof(ParticipatedInExperiment)));
} }
} }
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Experimentation namespace Microsoft.CodeAnalysis.Experimentation
{ {
internal enum LiveCodeAnalysisInstallStatus internal enum LiveCodeAnalysisInstallStatus
{ {
......
...@@ -134,6 +134,9 @@ ...@@ -134,6 +134,9 @@
<Compile Include="Diagnostics\SymbolAnalysisContextExtensions.cs" /> <Compile Include="Diagnostics\SymbolAnalysisContextExtensions.cs" />
<Compile Include="DocumentHighlighting\DocumentHighlightingOptions.cs" /> <Compile Include="DocumentHighlighting\DocumentHighlightingOptions.cs" />
<Compile Include="EncapsulateField\EncapsulateFieldRefactoringProvider.cs" /> <Compile Include="EncapsulateField\EncapsulateFieldRefactoringProvider.cs" />
<Compile Include="Experimentation\AnalyzerABTestLogger.cs" />
<Compile Include="Experimentation\AnalyzerABTestOptions.cs" />
<Compile Include="Experimentation\LiveCodeAnalysisInstallStatus.cs" />
<Compile Include="ExtractInterface\ExtractInterfaceCodeRefactoringProvider.cs" /> <Compile Include="ExtractInterface\ExtractInterfaceCodeRefactoringProvider.cs" />
<Compile Include="CodeFixes\FixAllOccurrences\FixSomeCodeAction.cs" /> <Compile Include="CodeFixes\FixAllOccurrences\FixSomeCodeAction.cs" />
<Compile Include="AddPackage\InstallPackageDirectlyCodeAction.cs" /> <Compile Include="AddPackage\InstallPackageDirectlyCodeAction.cs" />
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
using Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; using Microsoft.CodeAnalysis.Editor.Implementation.Suggestions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Experimentation;
using Microsoft.CodeAnalysis.Experiments; using Microsoft.CodeAnalysis.Experiments;
using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Extensions;
using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell;
...@@ -97,6 +98,7 @@ private bool IsVsixInstalled() ...@@ -97,6 +98,7 @@ private bool IsVsixInstalled()
else else
{ {
_installStatus = installed != 0 ? LiveCodeAnalysisInstallStatus.Installed : LiveCodeAnalysisInstallStatus.NotInstalled; _installStatus = installed != 0 ? LiveCodeAnalysisInstallStatus.Installed : LiveCodeAnalysisInstallStatus.NotInstalled;
AnalyzerABTestLogger.LogInstallationStatus(_workspace, _installStatus);
} }
} }
...@@ -105,6 +107,13 @@ private bool IsVsixInstalled() ...@@ -105,6 +107,13 @@ private bool IsVsixInstalled()
private bool IsCandidate() private bool IsCandidate()
{ {
// if this user ever participated in the experiement and then uninstall the vsix, then
// this user will never be candidate again.
if (_workspace.Options.GetOption(AnalyzerABTestOptions.ParticipatedInExperiment))
{
return false;
}
// Filter for valid A/B test candidates. Candidates fill the following critera: // Filter for valid A/B test candidates. Candidates fill the following critera:
// 1: Are a Dotnet user (as evidenced by the fact that this code is being run) // 1: Are a Dotnet user (as evidenced by the fact that this code is being run)
// 2: Have triggered a lightbulb on 3 separate days // 2: Have triggered a lightbulb on 3 separate days
...@@ -116,21 +125,26 @@ private bool IsCandidate() ...@@ -116,21 +125,26 @@ private bool IsCandidate()
if (!isCandidate) if (!isCandidate)
{ {
// We store in UTC to avoid any timezone offset weirdness // We store in UTC to avoid any timezone offset weirdness
var lastTriggeredTime = DateTime.FromBinary(options.GetOption(AnalyzerABTestOptions.LastDateTimeUsedSuggestionAction)); var lastTriggeredTimeBinary = options.GetOption(AnalyzerABTestOptions.LastDateTimeUsedSuggestionAction);
AnalyzerABTestLogger.LogCandidacyRequirementsTracking(lastTriggeredTimeBinary);
var lastTriggeredTime = DateTime.FromBinary(lastTriggeredTimeBinary);
var currentTime = DateTime.UtcNow; var currentTime = DateTime.UtcNow;
var span = currentTime - lastTriggeredTime; var span = currentTime - lastTriggeredTime;
if (span.TotalDays >= 1) if (span.TotalDays >= 1)
{ {
options = options.WithChangedOption(AnalyzerABTestOptions.LastDateTimeUsedSuggestionAction, currentTime.ToBinary()); options = options.WithChangedOption(AnalyzerABTestOptions.LastDateTimeUsedSuggestionAction, currentTime.ToBinary());
var usageCount = options.GetOption(AnalyzerABTestOptions.UsedSuggestedActionCount); var usageCount = options.GetOption(AnalyzerABTestOptions.UsedSuggestedActionCount);
usageCount++; options = options.WithChangedOption(AnalyzerABTestOptions.UsedSuggestedActionCount, ++usageCount);
options = options.WithChangedOption(AnalyzerABTestOptions.UsedSuggestedActionCount, usageCount);
if (usageCount >= 3) if (usageCount >= 3)
{ {
isCandidate = true; isCandidate = true;
options = options.WithChangedOption(AnalyzerABTestOptions.HasMetCandidacyRequirements, true); options = options.WithChangedOption(AnalyzerABTestOptions.HasMetCandidacyRequirements, true);
AnalyzerABTestLogger.Log(nameof(AnalyzerABTestOptions.HasMetCandidacyRequirements));
} }
_workspace.Options = options; _workspace.Options = options;
} }
} }
...@@ -144,6 +158,8 @@ private void ShowInfoBarIfNecessary() ...@@ -144,6 +158,8 @@ private void ShowInfoBarIfNecessary()
_infoBarChecked = true; _infoBarChecked = true;
if (_experimentationService.IsExperimentEnabled(AnalyzerEnabledFlight)) if (_experimentationService.IsExperimentEnabled(AnalyzerEnabledFlight))
{ {
AnalyzerABTestLogger.Log(nameof(AnalyzerEnabledFlight));
// If we got true from the experimentation service, then we're in the treatment // If we got true from the experimentation service, then we're in the treatment
// group, and the experiment is enabled. We determine if the infobar has been // group, and the experiment is enabled. We determine if the infobar has been
// displayed in the past 24 hours. If it hasn't been displayed, then we do so now. // displayed in the past 24 hours. If it hasn't been displayed, then we do so now.
...@@ -154,6 +170,7 @@ private void ShowInfoBarIfNecessary() ...@@ -154,6 +170,7 @@ private void ShowInfoBarIfNecessary()
if (timeSinceLastShown.TotalDays >= 1) if (timeSinceLastShown.TotalDays >= 1)
{ {
_workspace.Options = _workspace.Options.WithChangedOption(AnalyzerABTestOptions.LastDateTimeInfoBarShown, utcNow.ToBinary()); _workspace.Options = _workspace.Options.WithChangedOption(AnalyzerABTestOptions.LastDateTimeInfoBarShown, utcNow.ToBinary());
AnalyzerABTestLogger.Log("InfoBarShown");
var infoBarService = _workspace.Services.GetRequiredService<IInfoBarService>(); var infoBarService = _workspace.Services.GetRequiredService<IInfoBarService>();
infoBarService.ShowInfoBarInGlobalView( infoBarService.ShowInfoBarInGlobalView(
...@@ -173,11 +190,13 @@ private void ShowInfoBarIfNecessary() ...@@ -173,11 +190,13 @@ private void ShowInfoBarIfNecessary()
private void OpenInstallHyperlink() private void OpenInstallHyperlink()
{ {
System.Diagnostics.Process.Start(AnalyzerVsixHyperlink); System.Diagnostics.Process.Start(AnalyzerVsixHyperlink);
AnalyzerABTestLogger.Log(nameof(AnalyzerVsixHyperlink));
} }
private void DoNotShowAgain() private void DoNotShowAgain()
{ {
_workspace.Options = _workspace.Options.WithChangedOption(AnalyzerABTestOptions.NeverShowAgain, true); _workspace.Options = _workspace.Options.WithChangedOption(AnalyzerABTestOptions.NeverShowAgain, true);
AnalyzerABTestLogger.Log(nameof(AnalyzerABTestOptions.NeverShowAgain));
} }
} }
} }
...@@ -63,8 +63,6 @@ ...@@ -63,8 +63,6 @@
<Compile Include="Implementation\AnalyzerDependency\IIgnorableAssemblyList.cs" /> <Compile Include="Implementation\AnalyzerDependency\IIgnorableAssemblyList.cs" />
<Compile Include="Implementation\AnalyzerDependency\IBindingRedirectionService.cs" /> <Compile Include="Implementation\AnalyzerDependency\IBindingRedirectionService.cs" />
<Compile Include="Implementation\Experimentation\AnalyzerVsixSuggestedActionCallback.cs" /> <Compile Include="Implementation\Experimentation\AnalyzerVsixSuggestedActionCallback.cs" />
<Compile Include="Implementation\Experimentation\AnalyzerABTestOptions.cs" />
<Compile Include="Implementation\Experimentation\LiveCodeAnalysisInstallStatus.cs" />
<Compile Include="Implementation\Extensions\ServiceProviderExtensions.cs" /> <Compile Include="Implementation\Extensions\ServiceProviderExtensions.cs" />
<Compile Include="ID.InteractiveCommands.cs" /> <Compile Include="ID.InteractiveCommands.cs" />
<Compile Include="Implementation\FindReferences\VisualStudioDefinitionsAndReferencesFactory.cs" /> <Compile Include="Implementation\FindReferences\VisualStudioDefinitionsAndReferencesFactory.cs" />
......
...@@ -392,5 +392,6 @@ internal enum FunctionId ...@@ -392,5 +392,6 @@ internal enum FunctionId
CompilationService_GetCompilationAsync, CompilationService_GetCompilationAsync,
SolutionCreator_AssetDifferences, SolutionCreator_AssetDifferences,
Extension_InfoBar, Extension_InfoBar,
Experiment_ABTesting,
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册