Recording & Replay

View Source

Record terminal sessions to asciinema v2 .cast files. Play them back with pause, seek, and speed controls. If your app crashes mid-recording, the session auto-saves so you can see what happened.

Quick Start

mix raxol.record my_session.cast
mix raxol.replay my_session.cast

Recorder

Raxol.Recording.Recorder captures output and input events with timestamps. The rendering engine and dispatcher call into it automatically while it's running.

{:ok, _} = Raxol.Recording.Recorder.start_link()

# These are typically called by the framework, not by you:
Raxol.Recording.Recorder.record_output(data)
Raxol.Recording.Recorder.record_input(data)

Raxol.Recording.Recorder.active?()  # => true

session = Raxol.Recording.Recorder.get_session()  # peek without stopping
session = Raxol.Recording.Recorder.stop()          # stop and get the session

Player

Raxol.Recording.Player replays a .cast file or session struct:

Raxol.Recording.Player.play("my_session.cast", speed: 2.0, max_delay: 5.0)
Raxol.Recording.Player.play(session, interactive: true)

Keyboard controls during playback:

KeyAction
spacePause / resume
+ / =Speed up (1x -> 2x -> 4x -> 8x)
-Slow down
> / .Skip forward 5s
< / ,Skip backward 5s
0-9Jump to 0%-90%
q / ESCQuit

Options: :speed (default 1.0), :max_delay (default 5.0s cap between events), :interactive (default true).

Asciicast v2 Format

Raxol.Recording.Asciicast reads and writes the standard asciinema v2 format:

alias Raxol.Recording.Asciicast

Asciicast.write!(session, "output.cast")

{:ok, session} = Asciicast.read("output.cast")
session = Asciicast.read!("output.cast")

# String encode/decode
cast_string = Asciicast.encode(session)
session = Asciicast.decode(cast_string)

The format is a JSON header followed by newline-delimited event arrays:

{"version": 2, "width": 80, "height": 24, "timestamp": 1234567890}
[0.5, "o", "Hello"]
[1.2, "o", " World\r\n"]

Upload .cast files to asciinema.org to share them.

Programmatic Usage

{:ok, _} = Raxol.Recording.Recorder.start_link()

# ... run your app ...

session = Raxol.Recording.Recorder.stop()
Raxol.Recording.Asciicast.write!(session, "debug_session.cast")

Raxol.Recording.Player.play("debug_session.cast")

On crash, the current session is saved automatically -- no explicit stop needed for post-mortem.