Conversation
…ull-context filter
ed81240 to
c99c974
Compare
There was a problem hiding this comment.
Pull request overview
This PR addresses a /tl (Terminal Logger) usability regression where coordinator “waiting/grant” messages (global, BuildEventContext == null) were being dropped, reducing visibility into build delays.
Changes:
- Adjusts
ForwardingTerminalLoggerto forward null-context HIGH importance coordinator/global messages (while still respecting quiet mode). - Adds
TerminalLoggerhandling for null-context HIGH importance messages so they can be rendered to the terminal. - Introduces new unit tests validating
ForwardingTerminalLoggerforwarding behavior for null-context/global messages.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/Build/Logging/TerminalLogger/TerminalLogger.cs | Adds rendering path for null-context HIGH-importance messages in the terminal logger. |
| src/Build/Logging/TerminalLogger/ForwardingTerminalLogger.cs | Changes message forwarding order/filters so global coordinator messages aren’t dropped before reaching the central logger. |
| src/Build.UnitTests/ForwardingTerminalLogger_Tests.cs | Adds regression/unit tests ensuring null-context forwarding behavior and quiet-mode suppression. |
| // For global/coordinator messages with high importance | ||
| if (buildEventContext is null && message is not null && e.Importance == MessageImportance.High) | ||
| { |
| var buildEventContext = e.BuildEventContext; | ||
| string? message = e.Message; | ||
| // For global/coordinator messages with high importance | ||
| if (buildEventContext is null && message is not null && e.Importance == MessageImportance.High) |
There was a problem hiding this comment.
I was very confused about how the BEC could be null here, since it should be set to Invalid when the coordinator event throws. It looks like in the case I was debugging, the message is originally raised in the NuGet.Build.Tasks.Console.exe helper process, which serializes it back to be logged here.
That assembly compiles against Microsoft.Build.Utilities.v4.0, and the LogMessage there explicitly passes null for BuildEventContext.
Because RestoreTaskEx generally runs on the entrypoint node we might be able to avoid pushing the change to the forwarding logger, but I don't think that's worth changing.
There was a problem hiding this comment.
Thanks for the investigation, that makes sense. I think we are fine with defensive guard in the logger, let's keep it as is
| var buildEventContext = e.BuildEventContext; | ||
| string? message = e.Message; | ||
| // For global/coordinator messages with high importance | ||
| if (buildEventContext is null && message is not null && e.Importance == MessageImportance.High) |
There was a problem hiding this comment.
I would ideally like to check more than context-is-null-and-importance-is-high. Should we add an unlocalized keyword to the message so we can scan for it like IsAuthProviderMessage?
There was a problem hiding this comment.
That would be more robust, but it depends on scope. If coordinator messages are defined in a few places we control, a keyword marker makes sense. If they come from legacy helpers we don't control, the null-bec+HIGH guard might be the most pragmatic boundary. How many sources emit these messages?
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
could you paste here the before and after (either in plaintext or screenshots)? |
| string? message = e.Message; | ||
|
|
||
| // For global/coordinator messages with high importance | ||
| if (buildEventContext is null) |
There was a problem hiding this comment.
The real coordinator message is logged with BuildEventContext.Invalid, not null, so the in-process path is still swallowed.
| // For global/coordinator messages with high importance | ||
| if (buildEventContext is null) | ||
| { | ||
| if (Verbosity > LoggerVerbosity.Quiet && message is not null && e.Importance == MessageImportance.High) |
There was a problem hiding this comment.
null context + high importance is too broad a trigger I think. It may render arbitrary third-party messages.
Before the fix global/coordinator messages (with null BuildEventContext, or BuildEventContext.Invalid from the build coordinator) were silently dropped by TerminalLogger, even at High importance, so we just had:
After the fix the same messages are now rendered before the build-succeeded summary: |
JanProvaznik
left a comment
There was a problem hiding this comment.
I think controlling flow by strings is bad for maintainability.
Could we somehow use the type system?
This may require refactoring the coordinator messages to separate type instead of a normal LogComment or a better mechanism for distinguishing events that should be rendered in TL.
@baronfel may have suggestions.
… of message-text comparison.
Good point, and I agree strings aren't great for this. Here's what I did and what a fully-typed fix would probably have to do. The current commit: the coordinator's "waiting for nodes" message is now tagged with an A fully type-based fix would mean a dedicated event class (e.g. @JanProvaznik @baronfel Do you think I should do that here, file it as a follow-up issue, or is the current fix good enough? |
the coding agent can do it 😉 it has precedent so shouldn't be too hard |
…rdinatorWaitingForNodesEventArgs`
Ok, done, seems fine too me. |
| e is Microsoft.Build.Framework.Coordinator.CoordinatorWaitingForNodesEventArgs || | ||
| e is IExtendedBuildEventArgs { ExtendedType: Microsoft.Build.Framework.Coordinator.Constants.WaitingForNodesEventType }; |
| if (buildEventContext is null) | ||
| string? message = e.Message; | ||
|
|
||
| // Null context (e.g. an out-of-process helper) is trusted alone; BuildEventContext.Invalid additionally | ||
| // requires a recognized coordinator diagnostic, since Invalid can also be (mis-)used by in-process code | ||
| // that IS associated with the current build. | ||
| if (buildEventContext is null || buildEventContext == BuildEventContext.Invalid) | ||
| { | ||
| bool isRecognizedGlobalMessage = buildEventContext is null || IsCoordinatorMessage(e); | ||
|
|
||
| if (Verbosity > LoggerVerbosity.Quiet && message is not null && e.Importance == MessageImportance.High && isRecognizedGlobalMessage) | ||
| { | ||
| RenderImmediateMessage(message); | ||
| } | ||
|
|
There was a problem hiding this comment.
isn't this stale in the updated design with dedicated type?
| // Null context (e.g. an out-of-process helper) is trusted alone; BuildEventContext.Invalid additionally | ||
| // requires a recognized coordinator diagnostic, since Invalid can also be (mis-)used by in-process code | ||
| // that IS associated with the current build. | ||
| if (buildEventContext is null || buildEventContext == BuildEventContext.Invalid) |
There was a problem hiding this comment.
This suppresses unrelated high-importance messages with BuildEventContext.Invalid at Detailed/Diagnostic verbosity. Could maybe unrecognized Invalid-context messages fall through to the existing verbosity-dependent handling?
| { | ||
| return LoggingEventType.ExtendedBuildMessageEvent; | ||
| } | ||
| else if (eventType == typeof(Microsoft.Build.Framework.Coordinator.CoordinatorWaitingForNodesEventArgs)) |
There was a problem hiding this comment.
Could we add a node-packet round-trip test for this event? The TerminalLogger test manually constructs the generic ExtendedBuildMessageEventArgs, so it does not verify that this mapping preserves the message, importance, context, and ExtendedType across serialization.
…or event internal and add verbosity boundary + tests
Fixes: #14345
Issue
When MSBuild is invoked with the
/tl(Terminal Logger) flag, coordinator wait messages were silently dropped, leaving users without visibility into build delays during nested grant scenarios. This made it difficult to diagnose why builds appeared to hang.Root Cause
Coordinator messages have
null BuildEventContextandMessageImportance.Highto indicate they are global diagnostics. These were filtered out at two levels:Changes
Testing
ForwardingTerminalLogger_Tests.cs: