callr 3.8.0 with OpenTelemetry support

New in callr 3.8.0: OpenTelemetry, pseudo terminals, carrier::crate(). See the release page for the complete list of changes.

OpenTelemetry support

callr is now instrumented with OpenTelemetry, the vendor-neutral observability standard. When tracing is active, callr emits spans for r(), rcmd(), rscript(), r_process and r_session.

callr now depends on the otel OpenTelemetry API package, which turns all instrumentation calls into cheap no-ops unless an SDK is loaded. To actually record telemetry you also need an SDK such as otelsdk. The simplest way to turn tracing on is to set an environment variable before starting R:

1
2
3
export OTEL_TRACES_EXPORTER=file
export OTEL_EXPORTER_OTLP_FILE_TRACES_PATH=/tmp/callr-traces.jsonl
R

No code changes in your callr-using code are needed, existing entry points start emitting spans automatically.

The really nice part is context propagation. When tracing is active, callr injects the W3C Trace Context headers (TRACEPARENT, TRACESTATE, BAGGAGE) into the subprocess environment. callr’s subprocess startup hook reads these, extracts the parent span context, and opens a top-level callr subprocess span as a child of it. Any spans created inside the subprocess by any instrumented package, are then automatically part of the same hierarchy as the parent process’s spans. The result is a single connected trace that crosses the process boundary, with no extra work on your part.

See the OpenTelemetry article in the callr docs, and the Bringing OpenTelemetry to R in production blog post for the broader context.

Pseudo terminals

r(), rcmd(), rscript() and rscript_process now accept pty = TRUE to run the child R process in a pseudo-terminal (PTY). This makes the child process believe it is attached to a real terminal, so programs that disable colour output or interactive behaviour when stdout is not a tty will now behave as if they are running interactively.

This builds on processx 3.9.0, which added PTY support on recent Windows 10/11.

One thing to keep in mind: with pty = TRUE, stdout and stderr are merged into a single stream (the PTY device), so the stderr argument must be left as NULL. The result’s $stderr will always be NULL.

carrier::crate()

carrier::crate() bundles a function together with its data dependencies into a self-contained object that can be safely sent to a remote R process. You control exactly what travels with the function, so it does not accidentally capture a huge environment or reference packages that are not available on the other side.

callr 3.8.0 works with carrier crates automatically:

1
2
3
4
5
f <- carrier::crate(function() {
rlang::fn_fmls(my_fun)
}, my_fun = ls)

callr::r(f) # just works