defmodule Elixometer.Updater do @moduledoc false @max_messages 1000 @default_formatter Elixometer.Utils import Elixometer, only: [ensure_registered: 2, add_counter: 2, add_counter: 1] use GenServer def init(_args) do config = Application.get_env(:elixometer, __MODULE__, []) max_messages = Keyword.get(config, :max_messages, @max_messages) formatter = Keyword.get(config, :formatter, @default_formatter) {:ok, pobox} = :pobox.start_link(self(), max_messages, :queue) Process.register(pobox, :elixometer_pobox) activate_pobox() {:ok, %{formatter: formatter}} end def start_link(args) do GenServer.start_link(__MODULE__, args, name: __MODULE__) end def timer(name, units, elapsed) do :pobox.post(:elixometer_pobox, {:timer, name, units, elapsed}) end def gauge(name, value) do :pobox.post(:elixometer_pobox, {:gauge, name, value}) end def counter(name, delta, reset_seconds) do :pobox.post(:elixometer_pobox, {:counter, name, delta, reset_seconds}) end def spiral(name, delta, opts) do :pobox.post(:elixometer_pobox, {:spiral, name, delta, opts}) end def histogram(name, delta, reset_seconds, truncate) do :pobox.post(:elixometer_pobox, {:histogram, name, delta, reset_seconds, truncate}) end def handle_info({:mail, _pid, messages, _, _}, %{formatter: formatter} = state) do Enum.each(messages, fn message -> do_update(message, formatter) end) activate_pobox() {:noreply, state} end def do_update({:histogram, name, delta, aggregate_seconds, truncate}, formatter) do monitor = do_format(formatter, :histograms, name) ensure_registered(monitor, fn -> :exometer.new( monitor, :histogram, time_span: :timer.seconds(aggregate_seconds), truncate: truncate ) end) :exometer.update(monitor, delta) end def do_update({:spiral, name, delta, opts}, formatter) do monitor = do_format(formatter, :spirals, name) ensure_registered(monitor, fn -> :exometer.new(monitor, :spiral, opts) end) :exometer.update(monitor, delta) end def do_update({:counter, name, delta, reset_seconds}, formatter) do monitor = do_format(formatter, :counters, name) ensure_registered(monitor, fn -> :exometer.new(monitor, :counter, []) if is_nil(reset_seconds) do add_counter(monitor) else add_counter(monitor, reset_seconds * 1000) end end) :exometer.update(monitor, delta) end def do_update({:gauge, name, value}, formatter) do monitor = do_format(formatter, :gauges, name) ensure_registered(monitor, fn -> :exometer.new(monitor, :gauge, []) end) :exometer.update(monitor, value) end def do_update({:timer, name, units, elapsed_us}, formatter) do timer = do_format(formatter, :timers, name) ensure_registered(timer, fn -> :exometer.new(timer, :histogram, []) end) elapsed_time = cond do units in [:nanosecond, :nanoseconds] -> elapsed_us * 1000 units in [:micros, :microsecond, :microseconds] -> elapsed_us units in [:millis, :millisecond, :milliseconds] -> div(elapsed_us, 1000) units in [:second, :seconds] -> div(elapsed_us, 1_000_000) end :exometer.update(timer, elapsed_time) end defp activate_pobox do :pobox.active(:elixometer_pobox, fn msg, _ -> {{:ok, msg}, :nostate} end, :nostate) end defp do_format(formatter, metric, name) when is_function(formatter) do formatter.(metric, name) end defp do_format(formatter, metric, name) do formatter.format(metric, name) end end