FSL.Journal (fsl v0.2.0)

Copy Markdown View Source

Records one run, so that it can be drawn afterwards.

A journal holds, in order: every command the machine issued, every state it moved through, and how it ended. FSL.Runner flushes it through a renderer (FSL.Diagram) when the run finishes, which produces a sequence diagram of that particular run.

Turning it on

Off by default, and inert when off: every recording helper returns immediately if no journal has been started, so a production run pays nothing.

config :fsl, :log_sequence, true

An application that keeps its settings in one namespace of its own names it instead, and the flag is read there:

config :fsl, :log_sequence_app, :my_app
config :my_app, :log_sequence, true

A single machine can also turn it on for itself, if its embedding's context has a debug field.

Where it lives

In the process dictionary of the machine's own process — the same process that runs the states, issues the commands and reports the transitions. Two consequences follow, and both are the reason for the choice: a journal is isolated per run without a registry or any message passing, and it disappears with the process that owns it.

Summary

Types

A recorded event, in chronological order once read back via events/0.

Functions

Drop the journal from the current process (used by flush/0 and tests).

True when a journal is active in the current process.

Chronological list of recorded events ([] when disabled).

Render the PlantUML file and clear the journal from the process dictionary.

Metadata stored at start/1 (nil when disabled).

Record an outbound command, e.g. record_command(:sip, "send_INVITE").

Record a state report. state is the target state name, or :succeeded / :failed for a terminal; event is the (already stringified) triggering description and type its category (:sip, :media, …).

Start a journal in the current process with the given metadata.

Types

event()

@type event() ::
  %{kind: :command, type: atom(), name: String.t()}
  | %{
      kind: :transition,
      to: atom() | String.t(),
      event: String.t(),
      type: atom() | nil
    }
  | %{
      kind: :terminal,
      outcome: :succeeded | :failed,
      reason: String.t(),
      type: atom() | nil
    }

A recorded event, in chronological order once read back via events/0.

meta()

@type meta() :: %{scenario: String.t(), pid: String.t(), config: keyword()}

Functions

clear()

@spec clear() :: :ok

Drop the journal from the current process (used by flush/0 and tests).

enabled?()

@spec enabled?() :: boolean()

True when a journal is active in the current process.

events()

@spec events() :: [event()]

Chronological list of recorded events ([] when disabled).

flush()

@spec flush() :: {:ok, String.t()} | :disabled | {:error, term()}

Render the PlantUML file and clear the journal from the process dictionary.

Returns {:ok, path} on success, :disabled when no journal is active, or {:error, reason} if the file could not be written.

meta()

@spec meta() :: meta() | nil

Metadata stored at start/1 (nil when disabled).

record_command(type, name)

@spec record_command(atom(), String.t() | atom()) :: :ok

Record an outbound command, e.g. record_command(:sip, "send_INVITE").

record_transition(state, event, type)

@spec record_transition(atom(), String.t(), atom() | nil) :: :ok

Record a state report. state is the target state name, or :succeeded / :failed for a terminal; event is the (already stringified) triggering description and type its category (:sip, :media, …).

start(meta)

@spec start(meta()) :: :ok

Start a journal in the current process with the given metadata.