Observer Web is an easy-to-use tool that integrates into your application, providing enhanced observability. Leveraging OTP distribution, it offers tracing and profiling through the Erlang debugger, along with detailed insights into process/port statuses and Beam VM statistics.
Powered by Phoenix LiveView, it is distributed, lightweight, fully real-time and safe to use in production. This library is part of the DeployEx project.
See the installation guide for details on installing and Configuring.
Colors come from a small set of CSS custom properties in assets/css/tokens.css, one block for light mode and one for dark mode.
They're mapped to Tailwind utilities (bg-primary, bg-base-100, text-muted, bg-success, and so on) in the @theme block of assets/css/app.css, so a class built on a token needs no separate dark: variant - the underlying variable itself switches with the page's dark class.
To change the palette, edit the values in tokens.css and rebuild assets; nothing else needs to change.
Observer Web follows the same support policy as DeployEx: the last two major Erlang/OTP releases, and the last two major Phoenix releases.
The elixir: "~> 1.19" requirement in mix.exs tracks the lowest Elixir version that still runs on the older of those two OTP releases.
CI exercises both ends of that range on every pull request - see .github/workflows/ci.yaml.
| Policy | Currently | |
|---|---|---|
| Erlang/OTP | last 2 major releases | 28, 29 |
| Elixir | lowest version supporting the older OTP release above | ~> 1.19 |
| Phoenix | last 2 major releases | 1.7, 1.8 |
As each project's major version moves forward, these floors move with it - a dropped OTP or Phoenix release is a breaking change for Observer Web too.
mix deps.getmkdir -p priv/static && touch priv/static/app.css && touch priv/static/app.js
cd assets/
npm install
cd ..
mix assets.buildSince Observer Web uses the OTP distribution, it is required to have a name when running.
elixir --sname observer -S mix run --no-halt dev.exsNow you can visit localhost:4000/observer from your browser.
- Start the nodes
Open a new terminal (Terminal 1) and run the application in observer mode:
export PORT=4000
export OBSERVER_WEB_TELEMETRY_MODE=observer
elixir --sname observer --cookie cookie -S mix run --no-halt dev.exsOpen a new terminal (Terminal 2) and run the application in broadcast mode:
export PORT=4001
export OBSERVER_WEB_TELEMETRY_MODE=broadcast
elixir --sname broadcast --cookie cookie -S mix run --no-halt dev.exs- Connect the nodes
Open a new terminal (Terminal 3) and run:
iex --sname hub --cookie cookie -S mix phx.serverAfter the Phoenix server starts, you'll see the Elixir interactive shell prompt. Connect the nodes by executing these commands:
# Connect to the observer node
{:ok, hostname} = :inet.gethostname()
Node.connect(:"observer@#{hostname}")
# Should return: true
# Connect to the broadcast node
{:ok, hostname} = :inet.gethostname()
Node.connect(:"broadcast@#{hostname}")
# Should return: trueYou can close the Terminal 3 if you want, this terminal is only for supporting the node connection.
To verify everything is working properly:
- Visit
localhost:4000/observer/metricsin your browser to confirm the application is running inobservermode. - Visit
localhost:4001/observer/metricsto confirm the application is running inbroadcastmode.
You can now explore the observer mode, checking that the data is persisted even if the other app in
broadcast mode restarts.
To observe any remote node - a Nerves device, a server, or another machine on the network -
rather than another local dev instance, set OBSERVER_WEB_DEV_CONNECT_NODE to that node's full
name before starting the dev server.
dev.exs connects to it automatically on boot, so no interactive shell is needed:
OBSERVER_WEB_DEV_CONNECT_NODE=<remote-node>@<remote-hostname> elixir --name observer@<your-hostname> --cookie cookie -S mix run --no-halt dev.exsA few things this depends on:
- The target's node name must match exactly, including its hostname suffix - a mismatch (e.g. a different domain suffix, or an IP address instead of the registered hostname) silently connects to a nonexistent node and fails.
--name(not--sname) is required whenever either side has a dotted hostname, since Erlang distribution requires both nodes to use the same short-name/long-name mode.--cookiemust match the cookie the remote node is actually running with; check it there viaNode.get_cookie().- Both sides need network access to each other's
epmd(port4369by default).











