pub fn finish(self, use_error: bool) -> io::Result<Option<Vec<u8>>> {
        let Self {
            behavior,
            buffer,
            writers,
            primary,
            error,
        } = self;
        let buffers = buffer.map(|cell| cell.into_inner().expect("lock poisoned"));
        let header = use_error
            .then_some(error.header)
            .flatten()
            .or(primary.header);
        let footer = use_error
            .then_some(error.footer)
            .flatten()
            .or(primary.footer);

        if matches!(behavior, OutputClientBehavior::Grouped) {
            let buffers = buffers
                .as_ref()
                .expect("grouped logging requires buffer to be present");
            // We hold the mutex until we write all of the bytes associated for the client
            // to ensure that the bytes aren't interspersed.
            let mut writers = writers.lock().expect("lock poisoned");
            if let Some(prefix) = header {
                let start_time = chrono::Utc::now();
                writers.out.write_all(prefix(start_time).as_bytes())?;
            }
            for SinkBytes {
                buffer,
                destination,
            } in buffers
            {
                let writer = match destination {
                    Destination::Stdout => &mut writers.out,
                    Destination::Stderr => &mut writers.err,
                };
                writer.write_all(buffer)?;
            }
            if let Some(suffix) = footer {
                let end_time = chrono::Utc::now();
                writers.out.write_all(suffix(end_time).as_bytes())?;
            }
        }

        Ok(buffers.map(|buffers| {
            // TODO: it might be worth the list traversal to calculate length so we do a
            // single allocation
            let mut bytes = Vec::new();
            for SinkBytes { buffer, .. } in buffers {
                bytes.extend_from_slice(&buffer[..]);
            }
            bytes
        }))
    }
Consume the client and flush any bytes to the underlying sink if
necessary
crates/turborepo-ui/src/output.rs