Skip to content

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.

What you’ll capture

  • 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

Before you start

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 and Monty

Install logfire:

Terminal
pip install logfire

Install Monty:

Terminal
pip install pydantic-monty

Record sandbox execution

Call logfire.instrument_monty() after configuring Logfire and before creating a Monty pool:

main.py
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.

Verify it worked

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.

Troubleshoot missing data

  • instrument_monty() reports that OpenTelemetry instrumentation is unavailable: upgrade pydantic-monty to a release that includes instrument_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() and snapshot.resume(), including the current Pydantic AI Harness CodeMode implementation, also needs explicit context propagation to nest host-function spans. Calling logfire.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 the LOGFIRE_TOKEN environment variable in production. See Getting Started.