Logger, Telemetry & Observability

Elixir's Logger is integrated with Erlang's :logger (handlers, filters, JSON formatters like logger_json).

42.1 Logger

Elixir's Logger is integrated with Erlang's :logger (handlers, filters, JSON formatters like logger_json).

require Logger
Logger.debug("details")
Logger.info("User #{id} signed in", user_id: id)        # metadata
Logger.warning("disk almost full")
Logger.error("failed: #{inspect(reason)}")
Logger.info(fn -> expensive_message() end)              # lazy
Logger.metadata(request_id: "abc")
config :logger, level: :info
config :logger, :default_formatter, format: "$time $metadata[$level] $message\n", metadata: [:request_id]

42.2 Telemetry

A standard for emitting metrics/events across libraries:

Ecosystem: telemetry_metrics, telemetry_poller, Prometheus exporters (prom_ex), OpenTelemetry (opentelemetry_exporter), Phoenix LiveDashboard.

:telemetry.execute([:my_app, :request, :stop], %{duration: 120}, %{path: "/"})

:telemetry.attach("log-handler", [:my_app, :request, :stop],
  fn _event, measurements, metadata, _config ->
    Logger.info("#{metadata.path} took #{measurements.duration}ms")
  end, nil)

:telemetry.span([:my_app, :work], %{}, fn -> {do_work(), %{}} end)

42.3 Runtime introspection

Tracing: :dbg, recon_trace, Extrace. Remote shell into a running release: bin/my_app remote.

:observer.start()                       # GUI: processes, ETS, apps, load
:recon.proc_count(:memory, 5)           # top processes by memory (recon library)
:recon.proc_window(:reductions, 5, 1000)
Process.info(pid, [:current_stacktrace, :message_queue_len, :memory])
:sys.get_state(pid)                     # inspect a GenServer's state
:sys.trace(pid, true)                   # trace its messages
:erlang.memory()
:erlang.statistics(:run_queue)

42.4 Debugging

IO.inspect/2, dbg/1, IEx.pry/0 (with iex -S mix), break!/2 in IEx, :debugger.start(), ElixirLS debugger in VS Code.