Report eval errors through cli loggers#12095
Conversation
b1b7383 to
f3c695d
Compare
There was a problem hiding this comment.
Pull Request Overview
This PR fixes eval-time errors not being reported through CLI loggers by ensuring central loggers from distributed logger records are included in evaluation-time project collections. Previously, only single-node loggers were registered for evaluation, causing multi-node environments (default for dotnet commands) to miss eval errors while single-node environments (msbuild.exe) would see them.
Key changes:
- Add central loggers to eval-time ProjectCollection logger registration
- Prevent duplicate logger registration when ReusableLoggers wrap already-registered loggers
- Improve eval error formatting in TerminalLogger by detecting MSBuild placeholder locations
Reviewed Changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/MSBuild/XMake.cs | Modified to include central loggers from distributed logger records in evaluation logger array |
| src/MSBuild.UnitTests/XMake_Tests.cs | Updated test expectations and added better error reporting for message importance tests |
| src/Build/Microsoft.Build.csproj | Added ReusableLogger.cs to project compilation |
| src/Build/Logging/TerminalLogger/TerminalLogger.cs | Enhanced error formatting and added method to get minimum message importance |
| src/Build/Logging/ReusableLogger.cs | Moved ReusableLogger class from ProjectCollection to separate file with IEventSource5 support |
| src/Build/Definition/ProjectCollection.cs | Removed ReusableLogger inner class and updated references to use new location |
| src/Build/BackEnd/Components/Logging/LoggingService.cs | Enhanced duplicate logger detection for ReusableLoggers and updated TerminalLogger handling |
b48dd72 to
ab2fc64
Compare
|
the only failing test now:
Zero insight into this one :-/ |
it's not you, it's sidecartaskhost |
ab2fc64 to
981b23a
Compare
|
I made a quick script to test permutations on the bootstrap msbuild variants from this PR. Runners: dotnet build, dotnet msbuild, msbuild.exe To me, these look broadly fine - the console log being indented a bit is an artifact of the way the console logger prepends the node-specific logs with an indent + the node id. I'm not super concerned about changing the console logger's output here for greater usability/prettiness like I am the terminal logger. ps1 script for repro```pwsh # Script: test.ps1 # Run MSBuild variants to capture logger output.param( Determine script directory$scriptDir = $PSScriptRoot Project path in this workspace$project = Join-Path $scriptDir 'foo.csproj' The exact extra property argument requested (kept with the space as provided)$fixedProperty = '/p:"foo =bar"' Three invocation styles to test$invocations = @( Options to vary$tlOptions = @('on','off') Output folder for logs$logDir = Join-Path $scriptDir 'msbuild-logs' $results = [System.Collections.Generic.List[object]]::new() foreach ($inv in $invocations) { } Write-Host "`nSummary:" if ($ShowFailed) { Write-Host "`nLogs written to: $logDir" |
@SimaTian is this test issue known? can you please take a look? |
…t double-initialize
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jan Provazník <janprovaznik@microsoft.com>
7668c2f to
9382032
Compare
JanProvaznik
left a comment
There was a problem hiding this comment.
the concern I raised was me using the script validating the change wrong, lgtm
|
@dotnet/kitten are we clear to merge this one then? |
### Summary Fixes the MSB1025 ArgumentNullException error that occurs when using the `-dfl` (distributed file logger) flag with MSBuild 18. The issue was introduced in PR #12095 where distributed logger central loggers were passed to ProjectCollection without filtering out null values. ### Customer Impact Users running `msbuild -dfl` on MSBuild 18/VS 2026 encounter a blocking error: ``` MSBUILD : error MSB1025: An internal failure occurred while running MSBuild. System.ArgumentNullException: Value cannot be null. (Parameter 'logger') ``` This prevents builds from running when using the distributed file logger, which is commonly used for multi-node build logging. The `-dfl` flag worked correctly in MSBuild 17 and earlier versions. **Root Cause**: When using `-dfl`, the DistributedLoggerRecord intentionally has a null CentralLogger (as documented in XMake.cs line 4298-4299). PR #12095 introduced code that includes these central loggers in the evaluationLoggers array without filtering nulls, causing ProjectCollection.RegisterLoggerInternal() to throw ArgumentNullException. ### Regression? - **Yes** - Regression introduced in MSBuild 18 by PR #12095 - **Last working version**: MSBuild 17 ### Testing **Code Changes:** 1. **XMake.cs (line 1396)**: Added `.Where(l => l is not null)` to filter out null central loggers from the evaluationLoggers array 2. **XMake_Tests.cs**: Added regression test `TestNullCentralLoggerInDistributedLoggerRecord` to verify DistributedLoggerRecords with null CentralLogger don't cause exceptions **Verification:** - ✅ New unit test `TestNullCentralLoggerInDistributedLoggerRecord` passes - ✅ Build succeeded with no warnings or errors - ✅ Manual testing: `msbuild simple.proj -dfl -t:Build` now works correctly and creates distributed log file - ✅ Code review found no issues - ✅ No security vulnerabilities detected ### Risk **Low** - This is a minimal, surgical fix that: - Only filters out null loggers, which are intentionally null for distributed file loggers - Does not change behavior for non-null loggers - Restores MSBuild 17 functionality - Includes regression test to prevent future breakage <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> ---- *This section details on the original issue you should resolve* <issue_title>MsBuild 18/VS 2026 MSB1025 error when using DistributedFileLogger - The 'logger' parameter cannot be null.</issue_title> <issue_description>### Issue Description When running `msbuild -dfl` i receive such error `MSBUILD : error MSB1025: podczas uruchamiania programu MSBuild wystąpił błąd wewnętrzny. System.ArgumentNullException: Parametr „logger” nie może być zerowy. w Microsoft.Build.Shared.ErrorUtilities.ThrowArgumentNull(String parameterName, String resourceName) w Microsoft.Build.Evaluation.ProjectCollection.RegisterLoggerInternal(ILogger logger) w Microsoft.Build.Evaluation.ProjectCollection.RegisterLoggers(IEnumerable`1 loggers) w Microsoft.Build.Evaluation.ProjectCollection..ctor(IDictionary`2 globalProperties, IEnumerable`1 loggers, IEnumerable`1 remoteLoggers, ToolsetDefinitionLocations toolsetDefinitionLocations, Int32 maxNodeCount, Boolean onlyLogCriticalEvents, Boolean loadProjectsReadOnly, Boolean useAsynchronousLogging, Boolean reuseProjectRootElementCache, Boolean enableTargetOutputLogging) w Microsoft.Build.CommandLine.MSBuildApp.BuildProject(String projectFile, String[] targets, String toolsVersion, Dictionary`2 globalProperties, Dictionary`2 restoreProperties, ILogger[] loggers, LoggerVerbosity verbosity, DistributedLoggerRecord[] distributedLoggerRecords, Boolean needToValidateProject, String schemaFile, Int32 cpuCount, Boolean multiThreaded, Boolean enableNodeReuse, TextWriter preprocessWriter, TextWriter targetsWriter, Boolean detailedSummary, ISet`1 warningsAsErrors, ISet`1 warningsNotAsErrors, ISet`1 warningsAsMessages, Boolean enableRestore, ProfilerLogger profilerLogger, Boolean enableProfiler, Boolean interactive, ProjectIsolationMode isolateProjects, GraphBuildOptions graphBuildOptions, Boolean lowPriority, Boolean question, Boolean isTaskAndTargetItemLoggingRequired, Boolean isBuildCheckEnabled, String[] inputResultsCaches, String outputResultsCache, Boolean saveProjectResult, BuildResult& result, Boolean reportFileAccesses, String commandLine) w Microsoft.Build.CommandLine.MSBuildApp.Execute(String commandLine)` ### Steps to Reproduce to reproduce i create simple .net framework console solution and i run `msbuild -dfl` in solution directory ### Expected Behavior Solution should build as in previous version of msbuild (17) ### Actual Behavior Blocking error `MSBUILD : error MSB1025: podczas uruchamiania programu MSBuild wystąpił błąd wewnętrzny. System.ArgumentNullException: Parametr „logger” nie może być zerowy.` ### Analysis I tried to find any information in documentation about breaking changes in DistributedFileLogger but with no success. Trying different fileLoggerParameters did not help Switching to fileLogger works as expected ### Versions & Configurations Wersja programu MSBuild 18.0.5+e22287bf1 dla .NET Framework 18.0.5.56406 ran from C:\Program Files\Microsoft Visual Studio\18\Professional\MSBuild\Current\Bin\amd64\MSBuild.rsp and C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\MSBuild\Current\Bin\amd64\MSBuild.rsp Windows 11 </issue_description> <agent_instructions>Yes, this bug is very likely caused by the change in PR #12095. Here's the problematic code introduced in src/MSBuild/XMake.cs: csharpILogger[] evaluationLoggers = [ // all of the loggers that are single-node only .. loggers, // all of the central loggers for multi-node systems. These need to be resilient to multiple calls // to Initialize .. distributedLoggerRecords.Select(d => d.CentralLogger) ]; projectCollection = new ProjectCollection( globalProperties, evaluationLoggers, // <-- now includes distributed logger central loggers ... The problem: When you use -dfl (distributed file logger), the DistributedLoggerRecord.CentralLogger can be null. The code now blindly passes all CentralLogger values into the ProjectCollection constructor without filtering out nulls. When ProjectCollection.RegisterLoggers() iterates over these loggers and calls RegisterLoggerInternal(), it hits: csharpprivate void RegisterLoggerInternal(ILogger logger) { ErrorUtilities.VerifyThrowArgumentNull(logger); // <-- throws here when logger is null ... } The fix should filter out null loggers: csharpILogger[] evaluationLoggers = [ .. loggers, .. distributedLoggerRecords .Select(d => d.CentralLogger) .Where(l => l != null) // <-- add this filter ];</agent_instructions> ## Comments on the Issue (you are @copilot in this section) <comments> <comment_new><author>@YuliiaKovalova</author><body> Hi @kkapuscinski , Thank you for reporting the issue! It's likely to connected to #12095 change. We are working on the fix. cc: @baronfel </body></comment_new> </comments> </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes #13032 <!-- START COPILOT CODING AGENT TIPS --> --- ✨ Let Copilot coding agent [set things up for you](https://github.com/dotnet/msbuild/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: baronfel <573979+baronfel@users.noreply.github.com>



Fixes #11372
Context
Eval-time errors are missed because the eval-time ProjectCollection currently is only seeded with single-node loggers, not the central-node loggers that are created for DistributedLoggerRecords. In general, our loggers today are either-or: in single-node mode they register a single logger, but in multi-node mode (the default for
dotnetcommands) the loggers register as distributed loggers. This means that msbuild.exe-using users will see eval errors, but dotnet users will not.There is one notable exception here - the binary logger only ever registers as a central logger, because in this configuration absorbs all messages from all node automatically due to an implicit node-forwarding setup that the engine configures.
Changes Made
Add central loggers to the set of loggers registered with the main eval-time ProjectCollection
After this change, it became apparent that the TerminalLogger was getting double-messages sent to it
This led me to investigate the logging service, where logger registration occurs. There are two logging services in play here:
The eval-time logging service is primed with Loggers from the eval-time ProjectCollection. However, the eval-time ProjectCollection uses the loggers and forwarding loggers passed into it, but it wraps them as ReusableLoggers. This is a kind of logger that 'buffers' the underlying logger from the fact that evaluation and execution are two separate phases. Doing this allows the inner logger to register to a single event source (the ReusableLogger itself) and internally the ReusableLogger switches between eval-time and execution-time event sinks.
The build-time LoggingService uses the list of Loggers from the eval-time ProjectCollection (threaded through the BuildParameters class), but crucially it uses a different view of the ForwardedLoggers. This view just has the 'inner' CentralLoggers for each configured forwarded logger.
What this means is that during LoggingService initialization, when it checks if a logger is registered already, it sees a TerminalLogger registered (which happens here), and a ReusableLogger containing that same TerminalLogger asking to be registered (which happens here). To fix this, we:
Ensure that ReusableLogger's OriginalLoggers that are already registered with a LoggingService are not re-registered
With this change, both single-proc and multi-proc build report the same amount of events:
With this done I could look at the eval errors themselves. they were now reporting like this:
This is gross and has duplicated information, so now we:
Make Eval-time errors report more nicely in TerminalLogger
Testing
Added a TerminalLogger test case for the formatting.
Updated existing test cases for internal changes related to this work.
Notes
We may want to consider making the ReusableLogger public API - I think we've had issues in this area in the SDK (cc @jjonescz?) that this may have solved.