See Monty sandbox execution in Logfire
See what code does inside a Pydantic Monty sandbox, including its inputs, result, host calls, printed output, errors, and time spent running or waiting on the host. Each checkout (borrowing a sandbox worker from the pool) creates a session span (one unit of work: a single operation, with a name, a start, and a duration), with nested spans for code runs and host calls. If your application already has a current span, the session joins its trace (the full journey of one request, made of nested spans). Otherwise, it starts a new trace.
- Each checkout as a session span
- Each code run, including the Python source, inputs, result, and execution time
- Calls from the sandbox to host functions and operating-system handlers, including arguments and results
- Printed output, typing errors, and runtime errors
- Metrics (numbers tracked over time) for worker counts, checkout waits, worker terminations, session duration, run duration, and execution time
You’ll need a Logfire project. Open Add data in your project (top navigation) and follow the
setup for your language: it signs your machine in with logfire auth (a browser sign-in, no token
to copy) and, for production or other languages, creates a write token (the credential your app
uses to send data). New to Logfire? Start with Getting Started.
Install logfire:
pip install logfire
uv add logfire
conda install -c conda-forge logfire
Install Monty:
pip install pydantic-monty
Call logfire.instrument_monty() after configuring Logfire and
before creating a Monty pool:
from pydantic_monty import Monty
import logfire
logfire.configure()
logfire.instrument_monty()
with Monty() as pool:
with pool.checkout(script_name='calculation.py') as session:
result = session.feed_run('sum([20, 21, 1])')
assert result == 42
Both synchronous and asynchronous pools use the same process-wide integration. The first call selects
the Logfire instance and its instance-specific settings, such as tags set with
with_settings(). Later calls do not replace them.
Run python main.py, then open the Live view.
You should see a session calculation.py span containing a run code span with the source, result,
and duration.
Open the Metrics view to query measurements such as monty.pool.workers.live and
monty.run.duration.
Metrics cover every checkout and use a fixed set of labels, so sandbox code cannot create
new time series by choosing function names, paths, or exception classes.
Custom metric aggregation and collection settings in MetricsOptions also apply.
instrument_monty()reports that OpenTelemetry instrumentation is unavailable: upgradepydantic-montyto a release that includesinstrument_telemetry().- Tool spans appear outside the corresponding Monty call: nesting spans created inside host
functions requires Monty’s callback context propagation, which is not available in Monty 0.0.23.
Without it, those spans follow the caller’s tracing context (the parent span used for new spans).
Manual dispatch through
feed_start()andsnapshot.resume(), including the current Pydantic AI HarnessCodeModeimplementation, also needs explicit context propagation to nest host-function spans. Callinglogfire.instrument_monty()alone does not provide it. - No session spans appear: call
logfire.instrument_monty()before the first checkout. - Spans appear but pool metrics do not: create the Monty pool after calling
logfire.instrument_monty(). Pool-wide metrics are connected when the pool is created. - No data appears in Logfire: check that your write token is set. Run
logfire projects use <your-project>locally, or set theLOGFIRE_TOKENenvironment variable in production. See Getting Started.