# `PhoenixDevToolbar.Plugin`
[🔗](https://github.com/zoten/phoenix_dev_toolbar/blob/0.0.2/lib/phoenix_dev_toolbar/plugin.ex#L1)

> #### Experimental {: .warning}
>
> This API may change in any 0.0.x release. Changes are listed in the CHANGELOG.

The behaviour every toolbar panel implements, the built-ins included ([ADR 05](https://github.com/zoten/phoenix_dev_toolbar/blob/main/adr/05-plugin-behaviour.md)).

A plugin collects events from `:telemetry` inside the host's processes and,
in the panel, folds them into summary badges (`c:summary_init/1`,
`c:summary_add/2`, `c:badges/1`) and shows them in its own tab
(`c:panel/0`, see `PhoenixDevToolbar.Panel`). Register plugins in `config/dev.exs`:

    config :phoenix_dev_toolbar,
      plugins: [:defaults, {MyApp.Toolbar.Jobs, queues: [:default]}]

`:defaults` expands to the built-ins (Ecto, Request, LiveView, Process, System). Listing a
built-in with options overrides it; `{:exclude, Module}` removes one. An
invalid entry is logged and skipped: the host application always boots.

## Example

    defmodule MyApp.Toolbar.Jobs do
      use PhoenixDevToolbar.Plugin, id: :jobs, name: "Jobs"

      alias PhoenixDevToolbar.{Context, Event}

      @impl PhoenixDevToolbar.Plugin
      def init(opts), do: {:ok, %{queues: Keyword.get(opts, :queues, :all)}}

      @impl PhoenixDevToolbar.Plugin
      def telemetry_events(_config), do: [[:oban, :engine, :insert_job, :stop]]

      @impl PhoenixDevToolbar.Plugin
      def collect(_event, {measurements, metadata}, %Context{}) do
        data = %{worker: metadata.job.worker}
        {:record, Event.new(:jobs, :inserted, data, duration: measurements.duration)}
      end
    end

## Where the code runs

`init/1` runs once, when the toolbar attaches its handlers. `collect/3` runs
**inside the host process** that emitted the event (a request, a LiveView,
a Repo caller), and only for processes the toolbar tracks. So it must be
cheap and pure:

  * never call another process synchronously, never do I/O
  * bound everything it keeps: `inspect/2` limits, never whole structs
  * never create atoms from runtime data

A `collect/3` that raises, throws or exits is disabled (quarantined) and the
error is logged once; the other plugins keep working.

The behaviour is unstable before 1.0 (see the note at the top).

# `badge`

```elixir
@type badge() :: %{
  :id =&gt; String.t(),
  :text =&gt; String.t(),
  :level =&gt; :ok | :info | :warn | :error,
  optional(:title) =&gt; String.t()
}
```

A badge of the summary bar, shown even when the panel is collapsed. `id`
becomes the DOM id `pdt-badge-<id>`, unique in the whole panel: start it
with your plugin's id. `title` is the tooltip.

# `config`

```elixir
@type config() :: term()
```

What `init/1` returns: handed to every other callback.

# `badges`
*optional* 

```elixir
@callback badges(summary :: term()) :: [badge()]
```

The summary bar badges for the summary, in display order.

# `collect`

```elixir
@callback collect(
  :telemetry.event_name(),
  {measurements :: map(), metadata :: map()},
  PhoenixDevToolbar.Context.t()
) :: {:record, PhoenixDevToolbar.Event.t()} | :skip
```

Turns one telemetry event into a recorded event, or skips it. Runs inside the
emitting host process: cheap, pure, never raises. `context.config` is the
plugin's config.

# `id`

```elixir
@callback id() :: atom()
```

A unique id, stored in every event the plugin records (`Event.plugin`).

# `init`
*optional* 

```elixir
@callback init(opts :: keyword()) :: {:ok, config()} | {:error, String.t()}
```

Validates the options given in the config and returns the plugin's config.
Runs once, in the toolbar's own process. Defaults to `{:ok, opts}`.

# `name`

```elixir
@callback name() :: String.t()
```

The tab label in the panel.

# `panel`
*optional* 

```elixir
@callback panel() :: module()
```

The plugin's tab: a module that does `use PhoenixDevToolbar.Panel`. A plugin
without it collects events and shows badges only.

# `summary_add`
*optional* 

```elixir
@callback summary_add(summary :: term(), PhoenixDevToolbar.Event.t()) :: term()
```

Folds one of the plugin's events into its summary. Pure and cheap.

# `summary_init`
*optional* 

```elixir
@callback summary_init(config()) :: term()
```

The initial state of the plugin's summary for a page, folded by
`c:summary_add/2` in the panel process, event by event: it survives the
oldest events being trimmed from the buffer.

# `telemetry_events`

```elixir
@callback telemetry_events(config()) :: [:telemetry.event_name()]
```

The `:telemetry` events to attach to.

# `__using__`
*macro* 

Sets the behaviour and defines `id/0`, `name/0` and a default `init/1`.

Options: `:id` (required, an atom) and `:name` (the tab label, defaults to
the id).

---

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