Last active
August 28, 2017 14:46
-
-
Save frekw/cb3ed4a33a63b34559ea347335afa74a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Instrumenter do | |
alias Absinthe.Resolution | |
@behaviour Absinthe.Middleware | |
@callback measurement(metric :: atom, result :: any, time :: non_neg_integer) :: none | |
@callback field(field :: string) :: none | |
defmacro __using__(opts) do | |
backend = Keyword.get(opts, :adapter, Instrumenter.Dummy) | |
quote do | |
def instrument([], _field, _obj), do: [] | |
def instrument(mw, %{__reference__: %{module: Absinthe.Type.BuiltIns.Introspection}}, _obj), do: mw | |
def instrument(mw, %{identifier: id} = field, _obj) do | |
# TODO: Inject potential args. | |
[{{Instrumenter}, unquote(backend)} | mw] | |
end | |
def install(schema) do | |
instrumented? = fn %{middleware: mw} -> | |
mw | |
|> Enum.any?(fn | |
{{Instrumenter}, _} -> true | |
_ -> false | |
end) | |
end | |
for %{fields: fields} = object <- Absinthe.Schema.types(schema), | |
{k, %Absinthe.Type.Field{name: name, identifier: id} = field} <- fields, | |
instrumented?.(field) do | |
unquote(backend).field(name) | |
end | |
end | |
end | |
end | |
def call(%Resolution{state: :unresolved} = res, backend, _config) do | |
now = :os.timestamp() | |
%{res | middleware: res.middleware ++ [{{Instrumenter, :after_resolve}, start_at: now, backend: backend, field: res.definition.name, parent: res.parent_type.name}]} | |
end | |
def after_resolve(%Resolution{state: :resolved} = res, [start_at: start_at, backend: backend, field: field, parent: _]) do | |
diff = :timer.now_diff(:os.timestamp(), start_at) | |
result = case res.errors do | |
[] -> {:ok, res.value} | |
errors -> {:error, errors} | |
end | |
backend.measurement(field, result, diff) | |
res | |
end | |
end | |
defmodule Instrumenter.Dummy do | |
@behaviour Instrumenter | |
require Logger | |
def measurement(metric, {status, _result}, time) do | |
case status do | |
:error -> Logger.warn("#{metric} failed (took: #{inspect time})") | |
:ok -> Logger.info("#{metric} took: #{inspect time}") | |
end | |
end | |
def field(name) do | |
Logger.info("install field #{name}") | |
end | |
end | |
defmodule App.Instrumentation do | |
use Instrumenter | |
end | |
# during application boot | |
App.Instrumentation.install(App.Schema) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment