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.