defmodule Methex do use Application @etstable :__methex__cache__ @def_ttl 60 # See http://elixir-lang.org/docs/stable/elixir/Application.html # for more information on OTP Applications def start(_type, _args) do import Supervisor.Spec, warn: false if (:ets.info(@etstable) != :undefined), do: raise("ets table #{inspect @etstable} already exist") true = (@etstable == :ets.new(@etstable, [:public, :named_table, :ordered_set, {:write_concurrency, true}, {:read_concurrency, true}, :protected])) case Logger.add_backend({Methex.Logger, nil}) do {:ok, pid} when is_pid(pid) -> :ok {:error, :already_present} -> :ok some -> raise("can not start Methex.Logger backend, got #{inspect some}") end children = [ # Define workers and child supervisors to be supervised # worker(Methex.Worker, [arg1, arg2, arg3]) ] # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html # for other strategies and supported options opts = [strategy: :one_for_one, name: Methex.Supervisor] Supervisor.start_link(children, opts) end @spec put(number, String.t, pos_integer) :: :ok def put(num, name, ttl \\ @def_ttl) def put(num, name, ttl) when is_number(num) and is_binary(name) and is_integer(ttl) and (ttl > 0) do case :ets.lookup(@etstable, name) do [{^name, ^ttl}] -> :ok = :folsom_metrics.notify(name, num) [] -> :ok = :folsom_metrics.new_histogram(name, :slide, ttl) true = :ets.insert(@etstable, {name, ttl}) :ok = :folsom_metrics.notify(name, num) [{^name, _}] -> :ok = :folsom_metrics.delete_metric(name) :ok = :folsom_metrics.new_histogram(name, :slide, ttl) true = :ets.insert(@etstable, {name, ttl}) :ok = :folsom_metrics.notify(name, num) end end @spec get(String.t) :: [number] def get(name) when is_binary(name) do case :folsom_metrics.get_metric_value(name) do [] -> [] lst = [_|_] -> lst end end @spec get_average(String.t) :: float def get_average(name) when is_binary(name) do case get(name) do [] -> 0.0 lst = [_|_] -> Enum.sum(lst) / length(lst) end end @spec get_freq(String.t) :: float def get_freq(name) when is_binary(name) do case :ets.lookup(@etstable, name) do [{^name, ttl}] -> ((get(name) |> length) / ttl) [] -> 0.0 end end @spec keys :: [String.t] def keys, do: :ets.foldl(fn({k,_}, acc) -> [k|acc] end, [], @etstable) @spec all_freq :: %{String.t => float} def all_freq, do: :ets.foldl(fn({name,_}, acc) -> Map.put(acc,"freq_#{name}", get_freq(name)) end, %{}, @etstable) @spec all_average :: %{String.t => float} def all_average, do: :ets.foldl(fn({name,_}, acc) -> Map.put(acc, "average_#{name}", get_average(name)) end, %{}, @etstable) @spec all :: %{String.t => float} def all, do: :ets.foldl(fn({name,_}, acc) -> Map.put(acc,"freq_#{name}", get_freq(name)) |> Map.put("average_#{name}", get_average(name)) end, %{}, @etstable) defmacro tc(body, name, ttl \\ @def_ttl) do quote location: :keep do ( {time, res} = :timer.tc(fn() -> unquote(body) end) Methex.put(div(time,1000), unquote(name), unquote(ttl)) res ) end end end