From 7020f16ef7e0634a49cd66d96e3171875b5e5007 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Thu, 25 Sep 2025 02:09:14 +0000 Subject: [PATCH 1/2] Backflow from https://github.com/dotnet/dotnet / 537ecf8 build 284571 [[ commit created by automation ]] --- .../Logging/ProjectLoggingContext.cs | 10 + .../Components/Logging/ProjectTelemetry.cs | 186 ++++++++++++++++++ .../RequestBuilder/RequestBuilder.cs | 2 + .../TaskExecutionHost/TaskExecutionHost.cs | 3 + .../TaskFactories/AssemblyTaskFactory.cs | 2 + src/Build/Microsoft.Build.csproj | 1 + 6 files changed, 204 insertions(+) create mode 100644 src/Build/BackEnd/Components/Logging/ProjectTelemetry.cs diff --git a/src/Build/BackEnd/Components/Logging/ProjectLoggingContext.cs b/src/Build/BackEnd/Components/Logging/ProjectLoggingContext.cs index 2bda5a2e93f..6f467e25d9b 100644 --- a/src/Build/BackEnd/Components/Logging/ProjectLoggingContext.cs +++ b/src/Build/BackEnd/Components/Logging/ProjectLoggingContext.cs @@ -25,6 +25,11 @@ internal class ProjectLoggingContext : BuildLoggingContext /// private string _projectFullPath; + /// + /// Telemetry data for a project + /// + private readonly ProjectTelemetry _projectTelemetry = new ProjectTelemetry(); + /// /// Constructs a project logging context. /// @@ -253,6 +258,11 @@ private static ProjectStartedEventArgs CreateProjectStarted( projectContextId); } + /// + /// Telemetry data for a project + /// + internal ProjectTelemetry ProjectTelemetry => _projectTelemetry; + /// /// Log that the project has finished /// diff --git a/src/Build/BackEnd/Components/Logging/ProjectTelemetry.cs b/src/Build/BackEnd/Components/Logging/ProjectTelemetry.cs new file mode 100644 index 00000000000..b0087eff762 --- /dev/null +++ b/src/Build/BackEnd/Components/Logging/ProjectTelemetry.cs @@ -0,0 +1,186 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Globalization; +using Microsoft.Build.Framework; + +namespace Microsoft.Build.BackEnd.Logging +{ + /// + /// Tracks telemetry data for a project build. + /// + internal class ProjectTelemetry + { + // We do not have dependency on Microsoft.Build.Tasks assembly, so using hard-coded type names + private const string AssemblyTaskFactoryTypeName = "Microsoft.Build.BackEnd.AssemblyTaskFactory"; + private const string IntrinsicTaskFactoryTypeName = "Microsoft.Build.BackEnd.IntrinsicTaskFactory"; + private const string CodeTaskFactoryTypeName = "Microsoft.Build.Tasks.CodeTaskFactory"; + private const string RoslynCodeTaskFactoryTypeName = "Microsoft.Build.Tasks.RoslynCodeTaskFactory"; + private const string XamlTaskFactoryTypeName = "Microsoft.Build.Tasks.XamlTaskFactory"; + + // Important Note: these two telemetry events are not logged directly. + // SDK aggregates them per build in https://github.com/dotnet/sdk/blob/main/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs. + // Aggregation logic is very basic. Only integer properties are aggregated by summing values. Non-integer properties are ignored. + // This means that if we ever add logging non-integer properties for these events, they will not be included in the telemetry. + private const string TaskFactoryEventName = "build/tasks/taskfactory"; + private const string TasksEventName = "build/tasks"; + + private int _assemblyTaskFactoryTasksExecutedCount = 0; + private int _intrinsicTaskFactoryTasksExecutedCount = 0; + private int _codeTaskFactoryTasksExecutedCount = 0; + private int _roslynCodeTaskFactoryTasksExecutedCount = 0; + private int _xamlTaskFactoryTasksExecutedCount = 0; + private int _customTaskFactoryTasksExecutedCount = 0; + + private int _taskHostTasksExecutedCount = 0; + + /// + /// Adds a task execution to the telemetry data. + /// + public void AddTaskExecution(string taskFactoryTypeName, bool isTaskHost) + { + if (isTaskHost) + { + _taskHostTasksExecutedCount++; + } + + switch (taskFactoryTypeName) + { + case AssemblyTaskFactoryTypeName: + _assemblyTaskFactoryTasksExecutedCount++; + break; + + case IntrinsicTaskFactoryTypeName: + _intrinsicTaskFactoryTasksExecutedCount++; + break; + + case CodeTaskFactoryTypeName: + _codeTaskFactoryTasksExecutedCount++; + break; + + case RoslynCodeTaskFactoryTypeName: + _roslynCodeTaskFactoryTasksExecutedCount++; + break; + + case XamlTaskFactoryTypeName: + _xamlTaskFactoryTasksExecutedCount++; + break; + + default: + _customTaskFactoryTasksExecutedCount++; + break; + } + } + + /// + /// Logs telemetry data for a project + /// + public void LogProjectTelemetry(ILoggingService loggingService, BuildEventContext buildEventContext) + { + if (loggingService == null) + { + return; + } + + try + { + Dictionary taskFactoryProperties = GetTaskFactoryProperties(); + if (taskFactoryProperties.Count > 0) + { + loggingService.LogTelemetry(buildEventContext, TaskFactoryEventName, taskFactoryProperties); + } + + Dictionary taskTotalProperties = GetTaskProperties(); + if (taskTotalProperties.Count > 0) + { + loggingService.LogTelemetry(buildEventContext, TasksEventName, taskTotalProperties); + } + } + catch + { + // Ignore telemetry logging errors to avoid breaking builds + } + finally + { + // Reset counts after logging. + // ProjectLoggingContext context and, as a result, this class should not be reused between projects builds, + // however it is better to defensively clean up the stats if it ever happens in future. + Clean(); + } + } + + private void Clean() + { + _assemblyTaskFactoryTasksExecutedCount = 0; + _intrinsicTaskFactoryTasksExecutedCount = 0; + _codeTaskFactoryTasksExecutedCount = 0; + _roslynCodeTaskFactoryTasksExecutedCount = 0; + _xamlTaskFactoryTasksExecutedCount = 0; + _customTaskFactoryTasksExecutedCount = 0; + + _taskHostTasksExecutedCount = 0; + } + + private Dictionary GetTaskFactoryProperties() + { + Dictionary properties = new(); + + if (_assemblyTaskFactoryTasksExecutedCount > 0) + { + properties["AssemblyTaskFactoryTasksExecutedCount"] = _assemblyTaskFactoryTasksExecutedCount.ToString(CultureInfo.InvariantCulture); + } + + if (_intrinsicTaskFactoryTasksExecutedCount > 0) + { + properties["IntrinsicTaskFactoryTasksExecutedCount"] = _intrinsicTaskFactoryTasksExecutedCount.ToString(CultureInfo.InvariantCulture); + } + + if (_codeTaskFactoryTasksExecutedCount > 0) + { + properties["CodeTaskFactoryTasksExecutedCount"] = _codeTaskFactoryTasksExecutedCount.ToString(CultureInfo.InvariantCulture); + } + + if (_roslynCodeTaskFactoryTasksExecutedCount > 0) + { + properties["RoslynCodeTaskFactoryTasksExecutedCount"] = _roslynCodeTaskFactoryTasksExecutedCount.ToString(CultureInfo.InvariantCulture); + } + + if (_xamlTaskFactoryTasksExecutedCount > 0) + { + properties["XamlTaskFactoryTasksExecutedCount"] = _xamlTaskFactoryTasksExecutedCount.ToString(CultureInfo.InvariantCulture); + } + + if (_customTaskFactoryTasksExecutedCount > 0) + { + properties["CustomTaskFactoryTasksExecutedCount"] = _customTaskFactoryTasksExecutedCount.ToString(CultureInfo.InvariantCulture); + } + + return properties; + } + + private Dictionary GetTaskProperties() + { + Dictionary properties = new(); + + var totalTasksExecuted = _assemblyTaskFactoryTasksExecutedCount + + _intrinsicTaskFactoryTasksExecutedCount + + _codeTaskFactoryTasksExecutedCount + + _roslynCodeTaskFactoryTasksExecutedCount + + _xamlTaskFactoryTasksExecutedCount + + _customTaskFactoryTasksExecutedCount; + + if (totalTasksExecuted > 0) + { + properties["TasksExecutedCount"] = totalTasksExecuted.ToString(CultureInfo.InvariantCulture); + } + + if (_taskHostTasksExecutedCount > 0) + { + properties["TaskHostTasksExecutedCount"] = _taskHostTasksExecutedCount.ToString(CultureInfo.InvariantCulture); + } + + return properties; + } + } +} diff --git a/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs b/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs index 2f9006ef940..0b112221b9a 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs @@ -880,6 +880,8 @@ private void ReportResultAndCleanUp(BuildResult result) { try { + _projectLoggingContext.ProjectTelemetry.LogProjectTelemetry(_projectLoggingContext.LoggingService, _projectLoggingContext.BuildEventContext); + _projectLoggingContext.LogProjectFinished(result.OverallResult == BuildResultCode.Success); } catch (Exception ex) when (!ExceptionHandling.IsCriticalException(ex)) diff --git a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs index b5551809c8e..da42433edec 100644 --- a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs +++ b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs @@ -985,6 +985,9 @@ private ITask InstantiateTask(IDictionary taskIdentityParameters task = _taskFactoryWrapper.TaskFactory is ITaskFactory2 taskFactory2 ? taskFactory2.CreateTask(loggingHost, taskIdentityParameters) : _taskFactoryWrapper.TaskFactory.CreateTask(loggingHost); + + // Track telemetry for non-AssemblyTaskFactory task factories. No task can go to the task host. + _taskLoggingContext?.TargetLoggingContext?.ProjectLoggingContext?.ProjectTelemetry?.AddTaskExecution(_taskFactoryWrapper.TaskFactory.GetType().FullName, isTaskHost: false); } finally { diff --git a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs index 056bb45e954..0880c7dee8b 100644 --- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -365,6 +365,8 @@ internal ITask CreateTaskInstance( useTaskFactory = _taskHostFactoryExplicitlyRequested; } + _taskLoggingContext?.TargetLoggingContext?.ProjectLoggingContext?.ProjectTelemetry?.AddTaskExecution(GetType().FullName, isTaskHost: useTaskFactory); + if (useTaskFactory) { ErrorUtilities.VerifyThrowInternalNull(buildComponentHost); diff --git a/src/Build/Microsoft.Build.csproj b/src/Build/Microsoft.Build.csproj index 31e93c46613..a4c4e66cb25 100644 --- a/src/Build/Microsoft.Build.csproj +++ b/src/Build/Microsoft.Build.csproj @@ -232,6 +232,7 @@ + From 35d623b8f74597cc1627e86f5676087efd72b5db Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Thu, 25 Sep 2025 02:09:14 +0000 Subject: [PATCH 2/2] Update dependencies from https://github.com/dotnet/dotnet build 284571 No dependency updates to commit --- eng/Version.Details.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 64d8815eadc..409a60f986c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,6 +1,6 @@ - +