Skip to content

Commit 9d9e8dd

Browse files
Copilotbaronfel
andcommitted
Add telemetry logger support for API-based MSBuild usage
Co-authored-by: baronfel <[email protected]>
1 parent 95d125e commit 9d9e8dd

File tree

7 files changed

+113
-9
lines changed

7 files changed

+113
-9
lines changed

src/BuiltInTools/dotnet-watch/Build/EvaluationResult.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections.Immutable;
55
using Microsoft.Build.Graph;
6+
using Microsoft.DotNet.Cli.Extensions;
67
using Microsoft.Extensions.Logging;
78

89
namespace Microsoft.DotNet.Watch;
@@ -75,7 +76,7 @@ public void WatchFiles(FileWatcher fileWatcher)
7576
{
7677
using (var loggers = buildReporter.GetLoggers(rootNode.ProjectInstance.FullPath, "Restore"))
7778
{
78-
if (!rootNode.ProjectInstance.Build([TargetNames.Restore], loggers))
79+
if (!rootNode.ProjectInstance.BuildWithTelemetry([TargetNames.Restore], loggers))
7980
{
8081
logger.LogError("Failed to restore project '{Path}'.", rootProjectPath);
8182
loggers.ReportOutput();
@@ -103,7 +104,7 @@ public void WatchFiles(FileWatcher fileWatcher)
103104

104105
using (var loggers = buildReporter.GetLoggers(projectInstance.FullPath, "DesignTimeBuild"))
105106
{
106-
if (!projectInstance.Build([TargetNames.Compile, .. customCollectWatchItems], loggers))
107+
if (!projectInstance.BuildWithTelemetry([TargetNames.Compile, .. customCollectWatchItems], loggers))
107108
{
108109
logger.LogError("Failed to build project '{Path}'.", projectInstance.FullPath);
109110
loggers.ReportOutput();

src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Diagnostics;
66
using Microsoft.Build.Graph;
77
using Microsoft.CodeAnalysis;
8+
using Microsoft.DotNet.Cli.Extensions;
89
using Microsoft.DotNet.HotReload;
910
using Microsoft.Extensions.Logging;
1011

@@ -578,7 +579,7 @@ private void DeployProjectDependencies(ProjectGraph graph, ImmutableArray<string
578579
}
579580

580581
using var loggers = buildReporter.GetLoggers(projectPath, targetName);
581-
if (!node.ProjectInstance.Build([targetName], loggers, out var targetOutputs))
582+
if (!node.ProjectInstance.BuildWithTelemetry([targetName], loggers, null, out var targetOutputs))
582583
{
583584
_context.Logger.LogDebug("{TargetName} target failed", targetName);
584585
loggers.ReportOutput();
@@ -793,7 +794,7 @@ void Report(ChangeKind kind)
793794
}
794795

795796
string GetMessage(IReadOnlyList<ChangedFile> items, ChangeKind kind)
796-
=> items is [{Item: var item }]
797+
=> items is [{ Item: var item }]
797798
? GetSingularMessage(kind) + ": " + GetRelativeFilePath(item.FilePath)
798799
: GetPluralMessage(kind) + ": " + string.Join(", ", items.Select(f => GetRelativeFilePath(f.Item.FilePath)));
799800

src/BuiltInTools/dotnet-watch/HotReload/ScopedCssFileHandler.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44

55
using Microsoft.Build.Graph;
6+
using Microsoft.DotNet.Cli.Extensions;
67
using Microsoft.Extensions.Logging;
78

89
namespace Microsoft.DotNet.Watch
@@ -61,7 +62,7 @@ public async ValueTask HandleFileChangesAsync(IReadOnlyList<ChangedFile> files,
6162
using var loggers = buildReporter.GetLoggers(projectNode.ProjectInstance.FullPath, BuildTargetName);
6263

6364
// Deep copy so that we don't pollute the project graph:
64-
if (!projectNode.ProjectInstance.DeepCopy().Build(BuildTargetName, loggers))
65+
if (!projectNode.ProjectInstance.DeepCopy().BuildWithTelemetry([BuildTargetName], loggers))
6566
{
6667
loggers.ReportOutput();
6768
return null;

src/Cli/dotnet/Commands/Run/RunCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ static void InvokeRunArgumentsTarget(ProjectInstance project, bool noBuild, Faca
490490
loggersForBuild.Add(binaryLogger);
491491
}
492492

493-
if (!project.Build([Constants.ComputeRunArguments], loggers: loggersForBuild, remoteLoggers: null, out _))
493+
if (!project.BuildWithTelemetry([Constants.ComputeRunArguments], loggersForBuild, null, out _))
494494
{
495495
throw new GracefulException(CliCommandStrings.RunCommandEvaluationExceptionBuildFailed, Constants.ComputeRunArguments);
496496
}

src/Cli/dotnet/Commands/Test/MTP/SolutionAndProjectUtility.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Microsoft.Build.Execution;
99
using Microsoft.DotNet.Cli.Commands.Run;
1010
using Microsoft.DotNet.Cli.Commands.Run.LaunchSettings;
11+
using Microsoft.DotNet.Cli.Extensions;
1112
using Microsoft.DotNet.Cli.Utils;
1213
using Microsoft.DotNet.Cli.Utils.Extensions;
1314

@@ -353,7 +354,7 @@ static RunProperties GetRunProperties(ProjectInstance project)
353354
// NOTE: BuildManager is singleton.
354355
lock (s_buildLock)
355356
{
356-
if (!project.Build(s_computeRunArgumentsTarget, loggers: null))
357+
if (!project.BuildWithTelemetry(s_computeRunArgumentsTarget))
357358
{
358359
throw new GracefulException(CliCommandStrings.RunCommandEvaluationExceptionBuildFailed, s_computeRunArgumentsTarget[0]);
359360
}

src/Cli/dotnet/Commands/Workload/Restore/WorkloadRestoreCommand.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public override int Execute()
6060
});
6161

6262
workloadInstaller.Shutdown();
63-
63+
6464
return 0;
6565
}
6666

@@ -82,7 +82,7 @@ private List<WorkloadId> RunTargetToGetWorkloadIds(IEnumerable<string> allProjec
8282
continue;
8383
}
8484

85-
bool buildResult = project.Build([GetRequiredWorkloadsTargetName],
85+
bool buildResult = project.BuildWithTelemetry([GetRequiredWorkloadsTargetName],
8686
loggers: [
8787
new ConsoleLogger(Verbosity.ToLoggerVerbosity())
8888
],

src/Cli/dotnet/Extensions/ProjectInstanceExtensions.cs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using Microsoft.Build.Execution;
5+
using Microsoft.Build.Framework;
6+
using Microsoft.DotNet.Cli.Commands.MSBuild;
57

68
namespace Microsoft.DotNet.Cli.Extensions;
79

@@ -47,4 +49,102 @@ public static IEnumerable<string> GetConfigurations(this ProjectInstance project
4749
.Where(c => !string.IsNullOrWhiteSpace(c))
4850
.DefaultIfEmpty("Debug");
4951
}
52+
53+
/// <summary>
54+
/// Creates telemetry loggers for API-based MSBuild usage if telemetry is enabled.
55+
/// Returns null if telemetry is not enabled or if there's an error creating the loggers.
56+
/// </summary>
57+
/// <returns>A list of loggers to use with ProjectInstance.Build, or null if telemetry is disabled.</returns>
58+
public static ILogger[]? CreateTelemetryLoggers()
59+
{
60+
if (Telemetry.Telemetry.CurrentSessionId != null)
61+
{
62+
try
63+
{
64+
return [new MSBuildLogger()];
65+
}
66+
catch (Exception)
67+
{
68+
// Exceptions during telemetry shouldn't cause anything else to fail
69+
}
70+
}
71+
return null;
72+
}
73+
74+
/// <summary>
75+
/// Builds the project with the specified targets, automatically including telemetry loggers.
76+
/// </summary>
77+
public static bool BuildWithTelemetry(
78+
this ProjectInstance projectInstance,
79+
string[] targets,
80+
IEnumerable<ILogger>? additionalLoggers = null)
81+
{
82+
var loggers = new List<ILogger>();
83+
84+
var telemetryLoggers = CreateTelemetryLoggers();
85+
if (telemetryLoggers != null)
86+
{
87+
loggers.AddRange(telemetryLoggers);
88+
}
89+
90+
if (additionalLoggers != null)
91+
{
92+
loggers.AddRange(additionalLoggers);
93+
}
94+
95+
return projectInstance.Build(targets, loggers.Count > 0 ? loggers : null);
96+
}
97+
98+
/// <summary>
99+
/// Builds the project with the specified targets, automatically including telemetry loggers.
100+
/// Overload for Build with targetOutputs parameter.
101+
/// </summary>
102+
public static bool BuildWithTelemetry(
103+
this ProjectInstance projectInstance,
104+
string[] targets,
105+
IEnumerable<ILogger>? loggers,
106+
out IDictionary<string, TargetResult> targetOutputs)
107+
{
108+
var allLoggers = new List<ILogger>();
109+
110+
var telemetryLoggers = CreateTelemetryLoggers();
111+
if (telemetryLoggers != null)
112+
{
113+
allLoggers.AddRange(telemetryLoggers);
114+
}
115+
116+
if (loggers != null)
117+
{
118+
allLoggers.AddRange(loggers);
119+
}
120+
121+
return projectInstance.Build(targets, allLoggers.Count > 0 ? allLoggers : null, out targetOutputs);
122+
}
123+
124+
/// <summary>
125+
/// Builds the project with the specified targets, automatically including telemetry loggers.
126+
/// Overload for Build with loggers, remoteLoggers, and targetOutputs parameters.
127+
/// </summary>
128+
public static bool BuildWithTelemetry(
129+
this ProjectInstance projectInstance,
130+
string[] targets,
131+
IEnumerable<ILogger>? loggers,
132+
IEnumerable<Microsoft.Build.Logging.ForwardingLoggerRecord>? remoteLoggers,
133+
out IDictionary<string, TargetResult> targetOutputs)
134+
{
135+
var allLoggers = new List<ILogger>();
136+
137+
var telemetryLoggers = CreateTelemetryLoggers();
138+
if (telemetryLoggers != null)
139+
{
140+
allLoggers.AddRange(telemetryLoggers);
141+
}
142+
143+
if (loggers != null)
144+
{
145+
allLoggers.AddRange(loggers);
146+
}
147+
148+
return projectInstance.Build(targets, allLoggers.Count > 0 ? allLoggers : null, remoteLoggers, out targetOutputs);
149+
}
50150
}

0 commit comments

Comments
 (0)