blob: 9a6fa14d2a52f81b547ebaa3a07893fe433b16b1 [file] [log] [blame]
=====================
Additional Visibility
=====================
Burr comes with the ability to see inside your actions. This is a very pluggable framework
that comes with the default tracking client.
-------
Tracing
-------
Burr comes with a tracing capability to see recursive spans inside an action. This is similar to
the `OpenTelemetry <https://opentelemetry.io/>`_ sdk, although it is simplified significantly.
To add the tracing capability, the action first has to declare a ``__tracer`` input. This is a
an object that instantiates spans and tracks state.
Then, using the `__tracer` as a callable, you can instantiate spans and track state.
For the function-based API, this would look as follows:
.. code-block:: python
from burr.visibility import TracingFactory
from burr.core import action
@action(reads=['input_var'], writes=['output_var'])
def my_action(state: State, __tracer: TracingFactory) -> Tuple[dict, State]:
with __tracer('process_data'):
initial_data = _process_data(state['input_var'])
with __tracer('validate_data'):
_validate(initial_data)
with __tracer('transform_data', dependencies=['process_data']):
transformed_data = _transform(initial_data)
return {'output_var': transformed_data}, state.update({'output_var': transformed_data})
This would create the following traces:
#. ``process_data``
#. ``validate_data`` as a child of ``process_data``
#. ``transform_data`` as a causal dependent of ``process_data``
Dependencies are used to express [dag](-style structures of spans within actions. This is useful for gaining visibility into the internal structure
of an action, but is likely best used with integrations with micro-orchestration systems for implementating actions, such as Hamilton or Lanchain.
This maps to the `span link <https://opentelemetry.io/docs/concepts/signals/traces/#span-links>`_ concept in OpenTelemetry.
Note that, on the surface, this doesn't actually *do* anything. It has to be paired with the appropriate hooks.
These just function as callbacks (on enter/exit). The :py:class:`LocalTrackingClient <burr.tracking.LocalTrackingClient>`, used by the
:ref:`tracking <tracking>` feature forms one of these hooks, but we will be adding more, including:
1. An OpenTelemetry client
2. A DataDog client
.. note::
The class-based API can leverage this by declaring ``inputs`` as ``__tracer`` and then using the ``__tracer`` inside the ``run`` method.
------------
Observations
------------
(This is a work in progress, and is not complete)
You can make observations on the state by calling out to the `log_artifact` method on the `__tracer` context manager.
For instance:
.. code-block:: python
from burr.visibility import TracingFactory, ArtifactLogger
from burr.core import action
@action(reads=['input_var'], writes=['output_var'])
def my_action(
state: State,
__tracer: TracingFactory,
__logger: ArtifactLogger
) -> Tuple[dict, State]:
with __tracer('process_data'):
initial_data = _process_data(state['input_var'])
with __tracer('validate_data'):
validation_results = _validate(initial_data)
t.log_artifact(validation_results=validation_results)
with __tracer('transform_data', dependencies=['process_data'])
transformed_data = _transform(initial_data)
__logger.log_artifact(transformed_data_size=len(transformed_data))
return {'output_var': transformed_data}, state.update({'output_var': transformed_data})
The output can be any "json-dumpable" object (or pydantic model). This will be stored along with the span and can be used for debugging or analysis.
You can read more in the :ref:`reference documentation <visibility>`.