Skip to content

Commit

Permalink
Add fault telemetry for (hopefully) all VS APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
zivkan committed Nov 23, 2020
1 parent 7b95f67 commit f369f19
Show file tree
Hide file tree
Showing 21 changed files with 840 additions and 455 deletions.
6 changes: 5 additions & 1 deletion src/NuGet.Clients/NuGet.Tools/NuGetBrokeredServiceFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using NuGet.VisualStudio;
using NuGet.VisualStudio.Implementation.Extensibility;
using NuGet.VisualStudio.Internal.Contracts;
using NuGet.VisualStudio.Telemetry;
using ContractsNuGetServices = NuGet.VisualStudio.Contracts.NuGetServices;
using IBrokeredServiceContainer = Microsoft.VisualStudio.Shell.ServiceBroker.IBrokeredServiceContainer;
using ISettings = NuGet.Configuration.ISettings;
Expand All @@ -30,6 +31,7 @@ internal sealed class NuGetBrokeredServiceFactory
private AsyncLazy<IVsSolutionManager> _lazySolutionManager;
private INuGetProjectManagerServiceState _projectManagerServiceSharedState;
private ISharedServiceState _sharedServiceState;
private AsyncLazy<INuGetTelemetryProvider> _lazyTelemetryProvider;

private NuGetBrokeredServiceFactory()
{
Expand Down Expand Up @@ -133,8 +135,9 @@ internal static async ValueTask ProfferServicesAsync(IAsyncServiceProvider servi

IVsSolutionManager solutionManager = await _lazySolutionManager.GetValueAsync(cancellationToken);
ISettings settings = await _lazySettings.GetValueAsync(cancellationToken);
INuGetTelemetryProvider telemetryProvider = await _lazyTelemetryProvider.GetValueAsync(cancellationToken);

return new NuGetProjectService(solutionManager, settings);
return new NuGetProjectService(solutionManager, settings, telemetryProvider);
}

private Task InitializeAsync()
Expand All @@ -143,6 +146,7 @@ private Task InitializeAsync()
_lazySolutionManager = new AsyncLazy<IVsSolutionManager>(ServiceLocator.GetInstanceAsync<IVsSolutionManager>, ThreadHelper.JoinableTaskFactory);
_projectManagerServiceSharedState = new NuGetProjectManagerServiceState();
_sharedServiceState = new SharedServiceState();
_lazyTelemetryProvider = new AsyncLazy<INuGetTelemetryProvider>(ServiceLocator.GetInstanceAsync<INuGetTelemetryProvider>, ThreadHelper.JoinableTaskFactory);

return Task.CompletedTask;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) .NET Foundation. 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.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using NuGet.Common;

namespace NuGet.VisualStudio.Telemetry
{
public interface INuGetTelemetryProvider
{
Task EmitEventAsync(TelemetryEvent telemetryEvent);
Task PostFaultAsync(Exception e, string callerClassName, [CallerMemberName] string callerMemberName = null, IDictionary<string, object> extraProperties = null);
void PostFault(Exception e, string callerClassName, [CallerMemberName] string callerMemberName = null, IDictionary<string, object> extraProperties = null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) .NET Foundation. 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.Generic;
using System.ComponentModel.Composition;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using NuGet.Common;

namespace NuGet.VisualStudio.Telemetry
{
[Export(typeof(INuGetTelemetryProvider))]
internal class NuGetTelemetryProvider : INuGetTelemetryProvider
{
public Task EmitEventAsync(TelemetryEvent telemetryEvent)
{
TelemetryActivity.EmitTelemetryEvent(telemetryEvent);
return Task.CompletedTask;
}

public async Task PostFaultAsync(Exception e, string callerClassName, [CallerMemberName] string callerMemberName = null, IDictionary<string, object> extraProperties = null)
{
await TelemetryUtility.PostFaultAsync(e, callerClassName, callerMemberName, extraProperties);
}

public void PostFault(Exception e, string callerClassName, [CallerMemberName] string callerMemberName = null, IDictionary<string, object> extraProperties = null)
{
NuGetUIThreadHelper.JoinableTaskFactory.Run(async () =>
{
await TelemetryUtility.PostFaultAsync(e, callerClassName, callerMemberName, extraProperties);
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
Expand All @@ -19,7 +20,7 @@ namespace NuGet.VisualStudio.Telemetry
{
public static class TelemetryUtility
{
public static async Task PostFaultAsync(Exception e, string callerClassName, [CallerMemberName] string callerMemberName = null)
public static async Task PostFaultAsync(Exception e, string callerClassName, [CallerMemberName] string callerMemberName = null, IDictionary<string, object> extraProperties = null)
{
if (e == null)
{
Expand All @@ -33,6 +34,14 @@ public static async Task PostFaultAsync(Exception e, string callerClassName, [Ca

var fault = new FaultEvent($"{VSTelemetrySession.VSEventNamePrefix}Fault", description, FaultSeverity.General, e, gatherEventDetails: null);
fault.Properties[$"{VSTelemetrySession.VSPropertyNamePrefix}Fault.Caller"] = caller;
if (extraProperties != null)
{
foreach (var kvp in extraProperties)
{
fault.Properties[VSTelemetrySession.VSEventNamePrefix + kvp.Key] = kvp.Value;
}
}

TelemetryService.DefaultSession.PostEvent(fault);

if (await IsShellAvailable.GetValueAsync())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,43 +23,55 @@ public sealed class NuGetProjectService : INuGetProjectService
{
private readonly IVsSolutionManager _solutionManager;
private readonly ISettings _settings;
private readonly INuGetTelemetryProvider _telemetryProvider;

public NuGetProjectService(IVsSolutionManager solutionManager, ISettings settings)
public NuGetProjectService(IVsSolutionManager solutionManager, ISettings settings, INuGetTelemetryProvider telemetryProvider)
{
_solutionManager = solutionManager ?? throw new ArgumentNullException(nameof(solutionManager));
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
_telemetryProvider = telemetryProvider ?? throw new ArgumentNullException(nameof(telemetryProvider));
}

public async Task<InstalledPackagesResult> GetInstalledPackagesAsync(Guid projectId, CancellationToken cancellationToken)
{
// Just in case we're on the UI thread, switch to background thread. Very low cost (does not schedule new task) if already on background thread.
await TaskScheduler.Default;

NuGetProject project = await _solutionManager.GetNuGetProjectAsync(projectId.ToString());
if (project == null)
try
{
return NuGetContractsFactory.CreateInstalledPackagesResult(InstalledPackageResultStatus.ProjectNotReady, packages: null);
// Just in case we're on the UI thread, switch to background thread. Very low cost (does not schedule new task) if already on background thread.
await TaskScheduler.Default;

NuGetProject project = await _solutionManager.GetNuGetProjectAsync(projectId.ToString());
if (project == null)
{
return NuGetContractsFactory.CreateInstalledPackagesResult(InstalledPackageResultStatus.ProjectNotReady, packages: null);
}

InstalledPackageResultStatus status;
IReadOnlyCollection<NuGetInstalledPackage> installedPackages;

switch (project)
{
case BuildIntegratedNuGetProject packageReferenceProject:
(status, installedPackages) = await GetInstalledPackagesAsync(packageReferenceProject, cancellationToken);
break;

case MSBuildNuGetProject packagesConfigProject:
(status, installedPackages) = await GetInstalledPackagesAsync(packagesConfigProject, cancellationToken);
break;

default:
(status, installedPackages) = await GetInstalledPackagesAsync(project, cancellationToken);
break;
}

return NuGetContractsFactory.CreateInstalledPackagesResult(status, installedPackages);
}

InstalledPackageResultStatus status;
IReadOnlyCollection<NuGetInstalledPackage> installedPackages;

switch (project)
catch (Exception exception)
{
case BuildIntegratedNuGetProject packageReferenceProject:
(status, installedPackages) = await GetInstalledPackagesAsync(packageReferenceProject, cancellationToken);
break;

case MSBuildNuGetProject packagesConfigProject:
(status, installedPackages) = await GetInstalledPackagesAsync(packagesConfigProject, cancellationToken);
break;

default:
(status, installedPackages) = await GetInstalledPackagesAsync(project, cancellationToken);
break;
var extraProperties = new Dictionary<string, object>();
extraProperties["projectId"] = projectId.ToString();
await _telemetryProvider.PostFaultAsync(exception, typeof(NuGetProjectService).FullName, extraProperties: extraProperties);
throw;
}

return NuGetContractsFactory.CreateInstalledPackagesResult(status, installedPackages);
}

private async Task<(InstalledPackageResultStatus, IReadOnlyCollection<NuGetInstalledPackage>)> GetInstalledPackagesAsync(BuildIntegratedNuGetProject project, CancellationToken cancellationToken)
Expand Down

0 comments on commit f369f19

Please sign in to comment.