Other classes and functions
rerun
class RecordingStream
A RecordingStream is used to send data to Rerun.
You can instantiate a RecordingStream by calling either rerun.init
(to create a global
recording) or rerun.new_recording
(for more advanced use cases).
Multithreading
A RecordingStream can safely be copied and sent to other threads.
You can also set a recording as the global active one for all threads (rerun.set_global_data_recording
)
or just for the current thread (rerun.set_thread_local_data_recording
).
Similarly, the with
keyword can be used to temporarily set the active recording for the
current thread, e.g.:
with rec:
rr.log(...)
rerun.recording_stream_generator_ctx
decorator.
See also: rerun.get_data_recording
, rerun.get_global_data_recording
,
rerun.get_thread_local_data_recording
.
Available methods
Every function in the Rerun SDK that takes an optional RecordingStream as a parameter can also be called as a method on RecordingStream itself.
This includes, but isn't limited to:
- Metadata-related functions:
rerun.is_enabled
,rerun.get_recording_id
, … - Sink-related functions:
rerun.connect
,rerun.spawn
, … - Time-related functions:
rerun.set_time_seconds
,rerun.set_time_sequence
, … - Log-related functions:
rerun.log
, …
For an exhaustive list, see help(rerun.RecordingStream)
.
Micro-batching
Micro-batching using both space and time triggers (whichever comes first) is done automatically in a dedicated background thread.
You can configure the frequency of the batches using the following environment variables:
RERUN_FLUSH_TICK_SECS
: Flush frequency in seconds (default:0.05
(50ms)).RERUN_FLUSH_NUM_BYTES
: Flush threshold in bytes (default:1048576
(1MiB)).RERUN_FLUSH_NUM_ROWS
: Flush threshold in number of rows (default:18446744073709551615
(u64::MAX)).
class LoggingHandler
Bases: Handler
Provides a logging handler that forwards all events to the Rerun SDK.
Read more about logging handlers.
def __init__(path_prefix=None)
Initializes the logging handler with an optional path prefix.
PARAMETER | DESCRIPTION |
---|---|
path_prefix
|
A common prefix for all logged entity paths. Defaults to no prefix.
TYPE:
|
def emit(record)
Emits a record to the Rerun SDK.
class MemoryRecording
A recording that stores data in memory.
def drain_as_bytes()
Drains the MemoryRecording and returns the data as bytes.
This will flush the current sink before returning.
def num_msgs()
The number of pending messages in the MemoryRecording.
Note: counting the messages will flush the batcher in order to get a deterministic count.
def get_data_recording(recording=None)
Returns the most appropriate recording to log data to, in the current context, if any.
- If
recording
is specified, returns that one; - Otherwise, falls back to the currently active thread-local recording, if there is one;
- Otherwise, falls back to the currently active global recording, if there is one;
- Otherwise, returns None.
PARAMETER | DESCRIPTION |
---|---|
recording
|
Specifies the
TYPE:
|
RETURNS | DESCRIPTION |
---|---|
Optional[RecordingStream]
|
The most appropriate recording to log data to, in the current context, if any. |
def get_global_data_recording()
Returns the currently active global recording, if any.
RETURNS | DESCRIPTION |
---|---|
Optional[RecordingStream]
|
The currently active global recording, if any. |
def get_recording_id(recording=None)
Get the recording ID that this recording is logging to, as a UUIDv4, if any.
The default recording_id is based on multiprocessing.current_process().authkey
which means that all processes spawned with multiprocessing
will have the same default recording_id.
If you are not using multiprocessing
and still want several different Python
processes to log to the same Rerun instance (and be part of the same recording),
you will need to manually assign them all the same recording_id.
Any random UUIDv4 will work, or copy the recording id for the parent process.
PARAMETER | DESCRIPTION |
---|---|
recording
|
Specifies the
TYPE:
|
RETURNS | DESCRIPTION |
---|---|
str
|
The recording ID that this recording is logging to. |
def get_thread_local_data_recording()
Returns the currently active thread-local recording, if any.
RETURNS | DESCRIPTION |
---|---|
Optional[RecordingStream]
|
The currently active thread-local recording, if any. |
def is_enabled(recording=None)
Is this Rerun recording enabled.
If false, all calls to the recording are ignored.
The default can be set in rerun.init
, but is otherwise True
.
This can be controlled with the environment variable RERUN
(e.g. RERUN=on
or RERUN=off
).
def new_recording(application_id, *, recording_id=None, make_default=False, make_thread_default=False, spawn=False, default_enabled=True)
Creates a new recording with a user-chosen application id (name) that can be used to log data.
If you only need a single global recording, rerun.init
might be simpler.
Note that unless setting spawn=True
new recording streams always begin connected to a buffered sink.
To send the data to a viewer or file you will likely want to call rerun.connect
or rerun.save
explicitly.
Warning
If you don't specify a recording_id
, it will default to a random value that is generated once
at the start of the process.
That value will be kept around for the whole lifetime of the process, and even inherited by all
its subprocesses, if any.
This makes it trivial to log data to the same recording in a multiprocess setup, but it also means that the following code will not create two distinct recordings:
rr.init("my_app")
rr.init("my_app")
To create distinct recordings from the same process, specify distinct recording IDs:
from uuid import uuid4
rec = rr.new_recording(application_id="test", recording_id=uuid4())
rec = rr.new_recording(application_id="test", recording_id=uuid4())
PARAMETER | DESCRIPTION |
---|---|
application_id
|
Your Rerun recordings will be categorized by this application id, so try to pick a unique one for each application that uses the Rerun SDK. For example, if you have one application doing object detection
and another doing camera calibration, you could have
TYPE:
|
recording_id
|
Set the recording ID that this process is logging to, as a UUIDv4. The default recording_id is based on If you are not using
TYPE:
|
make_default
|
If true (not the default), the newly initialized recording will replace the current active one (if any) in the global scope.
TYPE:
|
make_thread_default
|
If true (not the default), the newly initialized recording will replace the current active one (if any) in the thread-local scope.
TYPE:
|
spawn
|
Spawn a Rerun Viewer and stream logging data to it.
Short for calling
TYPE:
|
default_enabled
|
Should Rerun logging be on by default?
Can be overridden with the RERUN env-var, e.g.
TYPE:
|
RETURNS | DESCRIPTION |
---|---|
RecordingStream
|
A handle to the |
Examples:
Using a recording stream object directly.
from uuid import uuid4
stream = rr.new_recording("my_app", recording_id=uuid4())
stream.connect()
stream.log("hello", rr.TextLog("Hello world"))
Setting up a new global recording explicitly.
from uuid import uuid4
rr.new_recording("my_app", make_default=True, recording_id=uuid4())
rr.connect()
rr.log("hello", rr.TextLog("Hello world"))
def set_global_data_recording(recording)
Replaces the currently active global recording with the specified one.
PARAMETER | DESCRIPTION |
---|---|
recording
|
The newly active global recording.
TYPE:
|
def set_thread_local_data_recording(recording)
Replaces the currently active thread-local recording with the specified one.
PARAMETER | DESCRIPTION |
---|---|
recording
|
The newly active thread-local recording.
TYPE:
|
def start_web_viewer_server(port=0)
Start an HTTP server that hosts the rerun web viewer.
This only provides the web-server that makes the viewer available and does not otherwise provide a rerun websocket server or facilitate any routing of data.
This is generally only necessary for application such as running a jupyter notebook in a context where app.rerun.io is unavailable, or does not have the matching resources for your build (such as when running from source.)
PARAMETER | DESCRIPTION |
---|---|
port
|
Port to serve assets on. Defaults to 0 (random port).
TYPE:
|
def escape_entity_path_part(part)
Escape an individual part of an entity path.
For instance, escape_entity_path_path("my image!")
will return "my\ image\!"
.
See https://www.rerun.io/docs/concepts/entity-path for more on entity paths.
PARAMETER | DESCRIPTION |
---|---|
part
|
An unescaped string
TYPE:
|
RETURNS | DESCRIPTION |
---|---|
str
|
The escaped entity path.
TYPE:
|
def new_entity_path(entity_path)
Construct an entity path, defined by a list of (unescaped) parts.
If any part if not a string, it will be converted to a string using str()
.
For instance, new_entity_path(["world", 42, "my image!"])
will return "world/42/my\ image\!"
.
See https://www.rerun.io/docs/concepts/entity-path for more on entity paths.
PARAMETER | DESCRIPTION |
---|---|
entity_path
|
A list of strings to escape and join with slash. |
RETURNS | DESCRIPTION |
---|---|
str
|
The escaped entity path.
TYPE:
|
def thread_local_stream(application_id)
Create a thread-local recording stream and use it when executing the decorated function.
This can be helpful for decorating a function that represents a job or a task that you want to to produce its own isolated recording.
Example
@rr.thread_local_stream("rerun_example_job")
def job(name: str) -> None:
rr.save(f"job_{name}.rrd")
for i in range(5):
time.sleep(0.2)
rr.log("hello", rr.TextLog(f"Hello {i) from Job {name}"))
threading.Thread(target=job, args=("A",)).start()
threading.Thread(target=job, args=("B",)).start()
PARAMETER | DESCRIPTION |
---|---|
application_id
|
The application ID that this recording is associated with.
TYPE:
|
def recording_stream_generator_ctx(func)
Decorator to manage recording stream context for generator functions.
This is only necessary if you need to implement a generator which yields while holding an open recording stream context which it created. This decorator will ensure that the recording stream context is suspended and then properly resumed upon re-entering the generator.
See: https://github.com/rerun-io/rerun/issues/6238 for context on why this is necessary.
There are plenty of things that can go wrong when mixing context managers with generators, so don't use this decorator unless you're sure you need it.
If you can plumb through RecordingStream
objects and use those directly instead of relying on
the context manager, that will always be more robust.
Example
@rr.recording_stream.recording_stream_generator_ctx
def my_generator(name: str) -> Iterator[None]:
with rr.new_recording(name):
rr.save(f"{name}.rrd")
for i in range(10):
rr.log("stream", rr.TextLog(f"{name} {i}"))
yield i
for i in my_generator("foo"):
pass