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
[rush] Replay logs for warnings and errors as well #4668
base: main
Are you sure you want to change the base?
[rush] Replay logs for warnings and errors as well #4668
Conversation
libraries/rush-lib/src/logic/operations/OperationMetadataManager.ts
Outdated
Show resolved
Hide resolved
libraries/rush-lib/src/logic/operations/schemas/log-chunk.schema.json
Outdated
Show resolved
Hide resolved
libraries/rush-lib/src/logic/operations/schemas/log-chunk.schema.json
Outdated
Show resolved
Hide resolved
common/changes/@microsoft/rush/sennyeya-log-replays_2024-04-29-20-05.json
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This overall looks good to me, but it'd be good for @octogonz to review this too, as he originally wrote a lot of the streaming logs stuff.
build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/.gitignore
Show resolved
Hide resolved
libraries/rush-lib/src/logic/operations/OperationMetadataManager.ts
Outdated
Show resolved
Hide resolved
libraries/rush-lib/src/logic/operations/OperationMetadataManager.ts
Outdated
Show resolved
Hide resolved
libraries/rush-lib/src/logic/operations/OperationExecutionRecord.ts
Outdated
Show resolved
Hide resolved
@@ -197,6 +198,14 @@ export class OperationExecutionRecord implements IOperationRunnerContext { | |||
`${associatedPhase.logFilenameIdentifier}${logFileSuffix}` | |||
) | |||
: undefined; | |||
const logChunksWritable: LogChunksWritable | undefined = | |||
associatedPhase && associatedProject |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is unclear why associatedPhase && associatedProject
should be the criteria to determine whether we use LogChunksWritable
to save chunks or not. Perhaps associatedPhase && associatedProject
is only true when cobuilds are enabled? This code needs a comment explanation or a more intuitive criteria.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, if I want to test this PR to inspect the `${logFileBaseName}.chunks.jsonl`
output, from looking at the code it's not obvious what I would need to do to cause associatedPhase && associatedProject
to become true. Enable cobuilds?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also unclear why these are nullable in the first place, they should both be defined when an operation is created with the PhasedOperationPlugin
. Cobuilds are not a requirement. The &&
check is also part of the projectLogWritable
above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dmichon-msft do you know the answer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason these were not required when added to the interface is that I had other plugins that created operations in the graph that were not strictly related to any phase or any operation. For example I had a custom pnpm installer that added operations to the Rush graph, and those installation steps were associated with external dependencies, not Rush projects.
In current usage we can make them be required (or make them required properties of an optional subobject that also contains all the various things that depend on their presence if we want to maintain that degree of compatibility with fewer checks).
fccbf98
to
82b0c1d
Compare
libraries/rush-lib/src/logic/operations/OperationMetadataManager.ts
Outdated
Show resolved
Hide resolved
… chunk separations
…-20-05.json Co-authored-by: Ian Clanton-Thuon <[email protected]>
…/repo/.gitignore Co-authored-by: Ian Clanton-Thuon <[email protected]>
Signed-off-by: Aramis Sennyey <[email protected]>
…er.ts Co-authored-by: Ian Clanton-Thuon <[email protected]>
85d9903
to
a8a6f93
Compare
libraries/rush-lib/src/logic/operations/OperationExecutionRecord.ts
Outdated
Show resolved
Hide resolved
@@ -197,6 +198,14 @@ export class OperationExecutionRecord implements IOperationRunnerContext { | |||
`${associatedPhase.logFilenameIdentifier}${logFileSuffix}` | |||
) | |||
: undefined; | |||
const logChunksWritable: LogChunksWritable | undefined = | |||
associatedPhase && associatedProject |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason these were not required when added to the interface is that I had other plugins that created operations in the graph that were not strictly related to any phase or any operation. For example I had a custom pnpm installer that added operations to the Rush graph, and those installation steps were associated with external dependencies, not Rush projects.
In current usage we can make them be required (or make them required properties of an optional subobject that also contains all the various things that depend on their presence if we want to maintain that degree of compatibility with fewer checks).
@octogonz @dmichon-msft This is ready for re-review when you get a chance! |
if (await FileSystem.existsAsync(this._logChunksPath)) { | ||
const rawLogChunks: string = await FileSystem.readFileAsync(this._logChunksPath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's safer and faster to try/catch the await FileSystem.readFileAsync
call and handle the FileSystem.isNotExistError(err)
case. Existence checking files before reading them is well-documented as an error-prone antipattern. Yes, I know that there are many places in the rushstack repo where we do that, but we don't need to add more of them.
if (this._enableChunkedOutput) { | ||
if (!this._chunkWriter) { | ||
this._chunkWriter = FileWriter.open(this.logChunksPath); | ||
} | ||
this._chunkWriter.write(JSON.stringify(chunk) + '\n'); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like creating files inside of the write
callback. I'd rather pay the tax of creating this file up front and then letting it be empty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, there's a handler for this._logWriter
being closed; we should have the same for this._chunkWriter
for the same reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How painful would it be to add a unit test for the chunked file format?
? new ProjectLogWritable( | ||
associatedProject, | ||
this.collatedWriter.terminal, | ||
`${associatedPhase.logFilenameIdentifier}${logFileSuffix}` | ||
`${associatedPhase.logFilenameIdentifier}${logFileSuffix}`, | ||
true | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this many arguments, consider making this an options
object with named properties.
@aramissennyeydd - Looks good overall. Once you just address @dmichon-msft's comments we can get this in. |
Summary
Replays logs through the hook context's
runWithTerminalAsync
to hook into the stdio summarization functionality. This allows the report at the end of execution to be the same across machines.Operations with 2 second durations were executed on the agent, those with <.5 seconds were restored from the build cache from the cobuild.
Before:
Agent 1
Agent 2
After:
Agent 1
Agent 2
Details
This stores the log chunks into a JSON file, that is a direct write of the
ITerminalChunk
type. This is then restored while the build cache is being restored to the terminal with the correct severity following theShellOperationPlugin
logging semantics.How it was tested
Tested against the
repo
cobuild sandbox with build caching enabled. Both restoring from the original build cache and cobuild cache work as expected and display the output above.Impacted documentation