Skip to main content
GitHub

Running Observer in IEx alongside an Elixir app

2025-01-30 in Posts

I wanted to try out Erlang’s Observer application to dig into the workings of my Elixir/Phoenix dev server. It didn’t immediately work, for two main reasons:

  1. Observer’s graphical interface depends on both the wxWidgets cross-platform GUI library and the OTP application wx, which supplies Erlang bindings for it. My system had neither.
  2. Once I’d sorted out the system problems in 1., I was able to run Observer from a plain IEx prompt, but not from the prompt opened with iex -S mix phx.server. To understand this, I had to learn that Elixir “prunes unused code paths.” More about this later.

Getting past these roadblocks turned out pretty straightforward with the latest Erlang/OTP and a newish Elixir on my M2 Macbook in January 2025. I say turned out, because past discussions on issues around asdf, Erlang and wxWidgets had me ready for a big mess, and it took me a bit to isolate what was relevant to me.

Incidentally, I think everything here also applies to Erlang’s debugger application, which also uses wxWidgets.

Sorting out my system

Install wxWidgets

To begin with, I didn’t even have wxWidgets installed on my computer. I installed it with Homebrew:

brew install wxwidgets

Install Erlang with wx enabled

I was still missing the wx OTP application; it had been disabled in my existing Erlang installation.

I manage runtimes on my Mac with asdf. I suspect that I could have simply uninstalled and reinstalled Erlang after putting wxWidgets on my system, but I included wx explicitly, as follows:

export KERL_CONFIGURE_OPTIONS="--with-wx"
asdf install erlang 27.2

The conservative thing would have been to uninstall the current Erlang and reinstall the same version but with wx enabled, but I went ahead and updated from 26.0.1 to 27.2.

Because I changed versions, I put the correct version in my project’s .tool-versions file with asdf local. Because I changed major versions, I also had to install an Elixir compiled with the right Erlang/OTP, and set the local version of that as well.

Check that wx and Observer work

At this point I was able to fire up IEx with iex and type

iex(1)> :observer.start()

and the Observer GUI popped up. Under the Applications tab were listed just elixir, iex, kernel, and logger.

There’s also the wx demo. Since I’d seen Observer working in IEx, I didn’t need to check wx by itself, but it’s a thing if you do need it or want to try it. Start up the Erlang shell:

erl

And run the demo:

> wx:demo().

A demo GUI should open, proving that wxWidgets and its Erlang bindings are present and working.

The deal with code-path pruning

As of Elixir 1.15, before a Mix project gets compiled, “unused code paths” are pruned, including for applications that ship with Elixir or Erlang, like Observer.

Translation: the Erlang runtime will look for modules in only directories containing the project’s dependencies. This makes compilation faster.

So, a general-purpose IEx prompt has access to all the stuff in your installation, but use iex -S to run a Mix task, and any module that is not used in that Mix project becomes invisible.

Including Observer and its dependencies

You can declare a dependency on an OTP application in mix.exs by including it in an application/0 function, under :extra_applications. My understanding is that this is meant for applications shipped with Erlang/OTP or Elixir—everything else you’d just put in your deps definition.

If you don’t always need a given application in the project, you can instead make it available from the prompt on a one-off basis. Without putting anything into my :extra_applications, under OTP 27, I can start my dev server:

iex -S mix phx.server

Then restore the code path to Observer in IEX with Mix.ensure_application!(app):

iex(1)> Mix.ensure_application!(:observer)

Finally, start Observer:

iex(2)> :observer.start()

Up pops the Observer GUI. A quick check of the Applications tab shows the application supervision tree for my running project.

Other notes

It looks like the way dependencies, and thus “unused code paths” to be pruned, are decided has been streamlined between OTP 26 and OTP 27.

Under Erlang/OTP 26, I had to explicitly require :wx and :runtime_tools as well as :observer in order to run Observer. With OTP 27 I only had to ensure :observer, even after removing the :extra_applications keyword entirely from the application definition in my mix.exs.

I see that :observer_backend is part of :runtime_tools. I’m keeping that in mind in case I need to understand this when I try to look at production nodes from my laptop.

Posts index