# `Mob.Event.Trace`
[🔗](https://github.com/genericjam/mob/blob/master/lib/mob/event/trace.ex#L1)

Live tracing of Mob events for IEx debugging.

Subscribe a process to receive every event that reaches a handler: events
delivered through `Mob.Event.dispatch/4`, and native input — taps, changes,
gestures, scroll — which arrives at a screen as a legacy tuple and is traced
by `Mob.Screen.Server` when the screen receives it (see
`Mob.Event.NativeInput`). Tracing is opt-in and costs one `:persistent_term`
read per event when no tracers are registered.

## Usage

    # In IEx connected to the running app:
    Mob.Event.Trace.subscribe()

    # Now every event lands in your mailbox too, tagged
    # {:mob_trace, addr, event, payload}. A native tap on `on_tap: {self(),
    # :save}` arrives as {:mob_trace, %Address{widget: :button, id: :save},
    # :tap, nil}. Pattern-match it, log it, whatever.

    flush()  # see what's in the mailbox

    # Filter on the way out:
    Mob.Event.Trace.subscribe(fn addr -> addr.widget == :list end)

    # From a shell on another node, name the pid to deliver to — `:rpc`
    # runs the call in a short-lived process that would receive nothing:
    :rpc.call(node, Mob.Event.Trace, :subscribe, [self(), nil])

    # Stop tracing:
    Mob.Event.Trace.unsubscribe()   # this process
    Mob.Event.Trace.stop()          # every tracer

Tracers are monitored by `Mob.Diag.Subscribers`, so one that exits stops being
traced to without an `unsubscribe/1`. One whose node disconnects is paused,
filter kept, until that node reconnects.

## What is not traced

Native input the screen never receives: an event for a screen that has
died is dropped by `Mob.Listener` and recorded as an `:undeliverable`
receipt instead. Arbitrary `handle_info/2` messages — timers, PubSub — are
not events and are not traced. A native tag that is not a valid address id
has no canonical address and is not traced either.

## Performance

When no tracers are registered (the default), each event reads an empty list
from `:persistent_term` and returns; native input is not even given an
address. When tracers are registered, each one is `send`ed a copy of the
envelope, payload included. Tracer filter functions run in the dispatching
process, so keep them cheap.

# `broadcast`

```elixir
@spec broadcast(Mob.Event.Address.t(), atom(), term()) :: :ok
```

Called by `Mob.Event.dispatch/4` to deliver to all tracers. Internal API.

Never raises: it runs inside the dispatching screen, and a tracer is a
debugging aid that must not change what it observes.

# `broadcast_input`

```elixir
@spec broadcast_input(tuple(), module()) :: :ok
```

Called by `Mob.Screen.Server` for a native input message it received.
Internal API.

The address is built only when someone is listening, so with no tracers
this is the same empty-list read as `broadcast/3`. Never raises.

# `stop`

```elixir
@spec stop() :: :ok
```

Stop tracing: unsubscribe every tracer.

# `subscribe`

```elixir
@spec subscribe((Mob.Event.Address.t() -&gt; boolean()) | nil) :: :ok
```

Subscribe the current process to receive trace messages.

If `filter` is provided, only events for which `filter.(addr)` returns
truthy are delivered to this subscriber.

Messages arrive shaped `{:mob_trace, addr, event, payload}`.

# `subscribe`

```elixir
@spec subscribe(pid(), (Mob.Event.Address.t() -&gt; boolean()) | nil) :: :ok
```

Subscribe `pid` — which may be on another node — with an optional `filter`.
Subscribing a pid again replaces its filter.

# `unsubscribe`

```elixir
@spec unsubscribe(pid()) :: :ok
```

Unsubscribe `pid` (defaults to the current process).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
