-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add telemetry to categorize build failure reasons #13007
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
YuliiaKovalova
merged 13 commits into
main
from
copilot/add-telemetry-for-build-failures
Jan 13, 2026
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
2a6dc25
Initial plan
Copilot 06f4a03
Add telemetry for build failure categorization
Copilot 50c2e0d
Address code review feedback
Copilot abe2774
Refactor: Move telemetry logic to separate class
Copilot f80215a
cleanup
YuliiaKovalova 0680d53
cleanup
YuliiaKovalova ac0fa53
Update src/Build/BackEnd/Components/Logging/BuildErrorTelemetryTracke…
YuliiaKovalova cbb39d9
Update documentation/wiki/CollectedTelemetry.md
YuliiaKovalova 353b1c4
Update src/Build/BackEnd/Components/Logging/BuildErrorTelemetryTracke…
YuliiaKovalova a63030e
Use Interlocked operations and add property namespacing
Copilot d5f855d
Distinguish NETSDK from SDK Resolvers
Copilot 6aee521
fix the comments
YuliiaKovalova 5582c98
fix review comments
YuliiaKovalova File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
215 changes: 215 additions & 0 deletions
215
src/Build.UnitTests/BackEnd/BuildTelemetryErrorCategorization_Tests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,215 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using Microsoft.Build.BackEnd.Logging; | ||
| using Microsoft.Build.Framework; | ||
| using Microsoft.Build.Framework.Telemetry; | ||
| using Microsoft.Build.Shared; | ||
| using Shouldly; | ||
| using Xunit; | ||
|
|
||
| #nullable disable | ||
|
|
||
| namespace Microsoft.Build.UnitTests.BackEnd; | ||
|
|
||
| public class BuildTelemetryErrorCategorization_Tests | ||
| { | ||
| [Theory] | ||
| [InlineData("CS0103", null, "Compiler")] | ||
| [InlineData("CS1002", "CS", "Compiler")] | ||
| [InlineData("VBC30451", "VBC", "Compiler")] | ||
| [InlineData("FS0039", null, "Compiler")] | ||
| [InlineData("MSB4018", null, "MSBuildEngine")] | ||
| [InlineData("MSB4236", null, "SDKResolvers")] | ||
| [InlineData("MSB3026", null, "Tasks")] | ||
| [InlineData("NETSDK1045", null, "NETSDK")] | ||
| [InlineData("NU1101", null, "NuGet")] | ||
| [InlineData("BC0001", null, "BuildCheck")] | ||
| [InlineData("CUSTOM001", null, "Other")] | ||
| [InlineData(null, null, "Other")] | ||
| [InlineData("", null, "Other")] | ||
| public void ErrorCategorizationWorksCorrectly(string errorCode, string subcategory, string expectedCategory) | ||
| { | ||
| // Create a LoggingService | ||
| var loggingService = LoggingService.CreateLoggingService(LoggerMode.Synchronous, 1); | ||
| loggingService.OnlyLogCriticalEvents = false; | ||
|
|
||
| try | ||
| { | ||
| // Log an error with the specified code | ||
| var errorEvent = new BuildErrorEventArgs( | ||
| subcategory, | ||
| errorCode, | ||
| "file.cs", | ||
| 1, | ||
| 1, | ||
| 0, | ||
| 0, | ||
| "Test error message", | ||
| "helpKeyword", | ||
| "sender"); | ||
|
|
||
| loggingService.LogBuildEvent(errorEvent); | ||
|
|
||
| // Populate telemetry | ||
| var buildTelemetry = new BuildTelemetry(); | ||
| loggingService.PopulateBuildTelemetryWithErrors(buildTelemetry); | ||
|
|
||
| // Verify the category is set correctly | ||
| buildTelemetry.FailureCategory.ShouldBe(expectedCategory); | ||
|
|
||
| // Verify the appropriate count is incremented | ||
| switch (expectedCategory) | ||
| { | ||
| case "Compiler": | ||
| buildTelemetry.ErrorCounts.Compiler.ShouldBe(1); | ||
| break; | ||
| case "MSBuildEngine": | ||
| buildTelemetry.ErrorCounts.MsBuildEngine.ShouldBe(1); | ||
| break; | ||
| case "Tasks": | ||
| buildTelemetry.ErrorCounts.Task.ShouldBe(1); | ||
| break; | ||
| case "SDKResolvers": | ||
| buildTelemetry.ErrorCounts.SdkResolvers.ShouldBe(1); | ||
| break; | ||
| case "NETSDK": | ||
| buildTelemetry.ErrorCounts.NetSdk.ShouldBe(1); | ||
| break; | ||
| case "NuGet": | ||
| buildTelemetry.ErrorCounts.NuGet.ShouldBe(1); | ||
| break; | ||
| case "BuildCheck": | ||
| buildTelemetry.ErrorCounts.BuildCheck.ShouldBe(1); | ||
| break; | ||
| case "Other": | ||
| buildTelemetry.ErrorCounts.Other.ShouldBe(1); | ||
| break; | ||
| } | ||
| } | ||
| finally | ||
| { | ||
| loggingService.ShutdownComponent(); | ||
| } | ||
| } | ||
|
|
||
| [Fact] | ||
| public void MultipleErrorsAreCountedByCategory() | ||
| { | ||
| var loggingService = LoggingService.CreateLoggingService(LoggerMode.Synchronous, 1); | ||
| loggingService.OnlyLogCriticalEvents = false; | ||
|
|
||
| try | ||
| { | ||
| // Log multiple errors of different categories | ||
| var errors = new[] | ||
| { | ||
| new BuildErrorEventArgs(null, "CS0103", "file.cs", 1, 1, 0, 0, "Error 1", null, "sender"), | ||
| new BuildErrorEventArgs(null, "CS1002", "file.cs", 2, 1, 0, 0, "Error 2", null, "sender"), | ||
| new BuildErrorEventArgs(null, "MSB4018", "file.proj", 10, 5, 0, 0, "Error 3", null, "sender"), | ||
| new BuildErrorEventArgs(null, "MSB3026", "file.proj", 15, 3, 0, 0, "Error 4", null, "sender"), | ||
| new BuildErrorEventArgs(null, "NU1101", "file.proj", 20, 1, 0, 0, "Error 5", null, "sender"), | ||
| new BuildErrorEventArgs(null, "CUSTOM001", "file.txt", 1, 1, 0, 0, "Error 6", null, "sender"), | ||
| }; | ||
|
|
||
| foreach (var error in errors) | ||
| { | ||
| loggingService.LogBuildEvent(error); | ||
| } | ||
|
|
||
| // Populate telemetry | ||
| var buildTelemetry = new BuildTelemetry(); | ||
| loggingService.PopulateBuildTelemetryWithErrors(buildTelemetry); | ||
|
|
||
| // Verify counts | ||
| buildTelemetry.ErrorCounts.Compiler.ShouldBe(2); | ||
| buildTelemetry.ErrorCounts.MsBuildEngine.ShouldBe(1); | ||
| buildTelemetry.ErrorCounts.Task.ShouldBe(1); | ||
| buildTelemetry.ErrorCounts.NuGet.ShouldBe(1); | ||
| buildTelemetry.ErrorCounts.Other.ShouldBe(1); | ||
|
|
||
| // Primary category should be Compiler (highest count) | ||
| buildTelemetry.FailureCategory.ShouldBe("Compiler"); | ||
| } | ||
| finally | ||
| { | ||
| loggingService.ShutdownComponent(); | ||
| } | ||
| } | ||
|
|
||
| [Fact] | ||
| public void PrimaryCategoryIsSetToHighestErrorCount() | ||
| { | ||
| var loggingService = LoggingService.CreateLoggingService(LoggerMode.Synchronous, 1); | ||
| loggingService.OnlyLogCriticalEvents = false; | ||
|
|
||
| try | ||
| { | ||
| // Log errors with Tasks having the highest count | ||
| var errors = new[] | ||
| { | ||
| new BuildErrorEventArgs(null, "MSB3026", "file.proj", 1, 1, 0, 0, "Task Error 1", null, "sender"), | ||
| new BuildErrorEventArgs(null, "MSB3027", "file.proj", 2, 1, 0, 0, "Task Error 2", null, "sender"), | ||
| new BuildErrorEventArgs(null, "MSB3028", "file.proj", 3, 1, 0, 0, "Task Error 3", null, "sender"), | ||
| new BuildErrorEventArgs(null, "CS0103", "file.cs", 4, 1, 0, 0, "Compiler Error", null, "sender"), | ||
| }; | ||
|
|
||
| foreach (var error in errors) | ||
| { | ||
| loggingService.LogBuildEvent(error); | ||
| } | ||
|
|
||
| // Populate telemetry | ||
| var buildTelemetry = new BuildTelemetry(); | ||
| loggingService.PopulateBuildTelemetryWithErrors(buildTelemetry); | ||
|
|
||
| // Primary category should be Tasks (3 errors vs 1 compiler error) | ||
| buildTelemetry.FailureCategory.ShouldBe("Tasks"); | ||
| buildTelemetry.ErrorCounts.Task.ShouldBe(3); | ||
| buildTelemetry.ErrorCounts.Compiler.ShouldBe(1); | ||
| } | ||
| finally | ||
| { | ||
| loggingService.ShutdownComponent(); | ||
| } | ||
| } | ||
|
|
||
| [Fact] | ||
| public void SubcategoryIsUsedForCompilerErrors() | ||
| { | ||
| var loggingService = LoggingService.CreateLoggingService(LoggerMode.Synchronous, 1); | ||
| loggingService.OnlyLogCriticalEvents = false; | ||
|
|
||
| try | ||
| { | ||
| // Log an error with subcategory "CS" (common for C# compiler errors) | ||
| var errorEvent = new BuildErrorEventArgs( | ||
| "CS", // subcategory | ||
| "CS0103", | ||
| "file.cs", | ||
| 1, | ||
| 1, | ||
| 0, | ||
| 0, | ||
| "The name 'foo' does not exist in the current context", | ||
| "helpKeyword", | ||
| "csc"); | ||
|
|
||
| loggingService.LogBuildEvent(errorEvent); | ||
|
|
||
| // Populate telemetry | ||
| var buildTelemetry = new BuildTelemetry(); | ||
| loggingService.PopulateBuildTelemetryWithErrors(buildTelemetry); | ||
|
|
||
| // Should be categorized as Compiler based on subcategory | ||
| buildTelemetry.FailureCategory.ShouldBe("Compiler"); | ||
| buildTelemetry.ErrorCounts.Compiler.ShouldBe(1); | ||
| } | ||
| finally | ||
| { | ||
| loggingService.ShutdownComponent(); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.