fix: log replay output (#6155)
### Description
Our output sinks expect to receive output at a line at a time. For the
most part we respect this, but we can unintentionally not uphold this*.
The fix is to hold a buffer with any bytes we receive and wait until we
receive a complete line before we forward it to a sink.
An alternative would be to make sure all calllers give complete lines,
but as described below that isn't always obvious.
*The issue in the log replay got hit by [this
line](https://github.com/vercel/turbo/blob/main/crates/turborepo-ui/src/prefixed.rs#L81).
One might assume that `writeln!` would desugar to a nice `write_all`
call and it sometimes does! But, depending on the concrete type of the
[`message: impl
Display`](https://github.com/vercel/turbo/blob/main/crates/turborepo-ui/src/prefixed.rs#L65)
there might be multiple `write_all` calls so Rust can avoid performing
an allocation to create a singular byte slice.
e.g. `writeln!(writer, "{}: {}", key, value)` could result in multiple
calls to the writer:
```
writer.write_all(key.as_bytes())?;
writer.write_all(b": ")?;
writer.write_all(value.as_bytes())?;
writer.write_all(b"\n")?;
```
(This is a simplification, the actual Rust implementation is
[here](https://doc.rust-lang.org/src/core/fmt/mod.rs.html#1106))
### Testing Instructions
Added unit test that forcibly interleave writes between two threads
before a full line is written.
Real test is to try out `turbo run build --experimental-rust-codepath`
twice and verify that the log replays are only interleaved at a line
granularity.
Closes TURBO-1451
Co-authored-by: Chris Olszewski <Chris Olszewski>