Skip to content

Instantly share code, notes, and snippets.

@cjbell
Created June 27, 2018 00:04
Show Gist options
  • Save cjbell/3b6d80187db0032a78e8b1a1d17f095f to your computer and use it in GitHub Desktop.
Save cjbell/3b6d80187db0032a78e8b1a1d17f095f to your computer and use it in GitHub Desktop.
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
@cjbell
Copy link
Author

cjbell commented Jun 27, 2018

Usage is like:

@decorate measure()
def my_func() do
  # stuff I want to measure
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment