Skip to content

Instantly share code, notes, and snippets.

@cjbell
Created June 27, 2018 00:04
Show Gist options
  • Select an option

  • Save cjbell/3b6d80187db0032a78e8b1a1d17f095f to your computer and use it in GitHub Desktop.

Select an option

Save cjbell/3b6d80187db0032a78e8b1a1d17f095f to your computer and use it in GitHub Desktop.

Revisions

  1. cjbell created this gist Jun 27, 2018.
    74 changes: 74 additions & 0 deletions stats_decorator.ex
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,74 @@
    defmodule Monitoring.Decorator do
    @moduledoc """
    `Decorator` functions for doing monitoring.
    """

    use Decorator.Define, [measure: 0, measure: 1]
    alias Monitoring.Statix

    require Logger

    @doc """
    Decorates a method with `Statix.measure`.
    The name is inferred from the module name and func name. Eg:
    `Core.Accounts.get_account` = `core.accounts.get_account`
    """
    def measure(body, %{module: mod, name: func} = context) do
    quote do
    name = Monitoring.Decorator.context_to_name(unquote(mod), unquote(func))
    context = unquote(extract_context(context))

    name
    |> Statix.measure(fn -> unquote(body) end)
    |> Monitoring.Decorator.log_response(name, context)
    end
    end

    @doc """
    Decorates a method with `Statix.measure`. Name is a required arg.
    """
    def measure(name, body, context) do
    quote do
    name = unquote(name)
    context = unquote(extract_context(context))

    name
    |> Statix.measure(fn -> unquote(body) end)
    |> Monitoring.Decorator.log_response(name, context)
    end
    end

    def context_to_name(mod, func) do
    mod_name(mod) <> "." <> Atom.to_string(func)
    end

    def log_response({:ok, _} = result, name, context) do
    name <> ".succeeded" |> log_and_increment(context)
    result
    end
    def log_response({:error, _} = result, name, context) do
    name <> ".failed" |> log_and_increment(context)
    result
    end
    def log_response(result, name, context) do
    name <> ".calls" |> log_and_increment(context)
    result
    end

    def log_and_increment(name, metadata \\ %{}) do
    event_data = %{String.to_atom(name) => metadata}
    Logger.debug(fn -> "[Service] " <> name end, event: event_data)
    name |> Statix.increment()
    end

    def mod_name(mod) when is_atom(mod), do: Atom.to_string(mod) |> mod_name()
    def mod_name("Elixir." <> mod), do: mod |> mod_name()
    def mod_name(mod), do: mod |> String.downcase()

    def extract_context(%{module: mod, name: name, arity: arity}) do
    quote do
    %{mod: unquote(mod), name: unquote(name), arity: unquote(arity)}
    end
    end
    end