.. _standard-outputs-streaming:

Standard outputs streaming
==========================
BuildBox supports using the **LogStream API** for streaming the contents that a command writes to ``stdout`` and ``stderr``.

The following diagram shows the flow of information when buildbox is connected to BuildGrid, which has a built-in LogStream service implementation [#BuildGrid-LogStream]_.

.. image:: _static/standard-outputs-streaming-with-buildgrid.svg
  :alt: Streaming of standard outputs with BuildGrid

LogStream API
-------------
The LogStream API [#LogStream-API]_ defines a mechanism for transmitting logs that relies on the ByteStream API [#ByteStream-API]_ for the actual transfer of the data.

An execution service provides a worker with a ``write_resource_name``, to which the worker has exclusive access, and broadcasts an associated ``read_resource_name`` to clients that are interested in receiving those outputs.

Since the logs are uploaded to CAS after a job has finished running, the streams are only alive while the command is executing. Once the last reader closes its connection, the stream ends, and the outputs should be accessed via the stdout/err blobs stored in CAS.


BuildBox Runner
---------------
The ``Runner`` class provided by :ref:`buildbox-common-dev` offers the ``--std{out, err}--file`` CLI options to allow the process that invokes the runner to redirect the stdout/err pipes of the command to those particular paths.

If no files are given, the runner will create and use temporary files that are deleted once their contents are uploaded into CAS. If paths are specified, those files will not be deleted after that upload operation, persisting after the runner process has exited.

buildbox-common
---------------
``buildbox-common`` provides facilities to write to a LogStream and to monitor files and incrementally stream their contents as they are being updated.

In particular, it provides the ``StandardOutputStreamer`` class, which offers a high-level interface that takes a path, the address of a ByteStream endpoint and a resource name and streams the contents of an output file as it gets written.


BuildBox worker
---------------
``buildbox-worker`` scans for trailing metadata entries [#gRPC-trailing-metadata]_ that are attached to the ``UpdateBotSession()`` response coming from the execution server.

In particular, it looks for entries with name ``"executeoperationmetadata-bin"`` and whose value is a serialized ``ExecuteOperationMetadata`` [#ExecuteOperationMetadata]_ message ``m`` such that:

  * ``m.action_digest`` is equal to the digest of the pertinent action in the ``Lease``, and

  * ``m.stdout_stream_name`` and ``m.stderr_stream_name`` point to ByteStream resource names.

If such a message is found, BuildBox worker will stream the outputs of that Action's command to the given resource name.

When streaming is requested, the worker will create a pair of files, set their paths in the ``--std{out, err}-file`` options it passes to the runner, and spawn two ``StandardOutputStreamer`` instances that will monitor those files and stream to the given resource names. Once the command has finished running they will commit the streamed data and delete the files.

On-demand streaming
~~~~~~~~~~~~~~~~~~~
Because an execution service might not be able to predict whether some client will request streamed logs while a command is running, workers must always stream the outputs. That would imply wasted network traffic if those logs end up unread.

In order to avoid that, BuildGrid implements a mechanism for signaling that at least one reader is interested in the logs; in effect creating the ByteStream write resource on-demand. For that it offers the ``ByteStream.QueryWriteStatus()`` call, which will block on the server side and return either ``OK``, meaning that the worker can start writing to the stream and a reader will receive the contents, or a ``NOT_FOUND`` error, which indicates that streaming can be skipped due to the logs not being requested.

buildbox-worker will leverage that behavior and start streaming only when ``QueryWriteStatus()`` returns successfully for the ByteStream resource.

LogStream tools
---------------
:ref:`buildbox-tools` contains some utilities that allow to write to a LogStream service and receive data. Those can be useful for testing purposes or for leveraging an existing LogStream service to stream other contents. (Note that the underlying data transfers, done via the ByteStream API, can contain any arbitrary binary data.)


.. [#LogStream-API] https://groups.google.com/g/remote-execution-apis/c/LCLsBSgSnU0/m/yTTtqr-cAwAJ
.. [#ByteStream-API] https://github.com/googleapis/googleapis/blob/master/google/bytestream/bytestream.proto
.. [#ExecuteOperationMetadata] https://github.com/bazelbuild/remote-apis/blob/1252343900d9a101c5180e2e31f76216e4b615c7/build/bazel/remote/execution/v2/remote_execution.proto#L1264
.. [#gRPC-trailing-metadata] https://grpc.github.io/grpc/cpp/classgrpc__impl_1_1_client_context.html#a81d5df78eb77fb0d1e97220f656afd95
.. [#BuildGrid-LogStream] https://buildgrid.build/user/using_logstream.html
