Last active
January 5, 2021 00:12
-
-
Save percygrunwald/6dd1d9132855b0372785e75df4a26669 to your computer and use it in GitHub Desktop.
Scout Absinthe (GraphQL) Instrumentation
This file contains 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 ScoutApm.Absinthe.Plug do | |
alias ScoutApm.Internal.Layer | |
require Logger | |
@error_prefix "GraphQL query document could not be parsed" | |
@endpoint_prefix "GraphQL" | |
@default_action_name "unknown" | |
def init(default), do: default | |
def call(conn, _default) do | |
ScoutApm.TrackedRequest.start_layer("Controller", endpoint_name(conn)) | |
conn | |
|> Plug.Conn.register_before_send(&before_send/1) | |
end | |
defp before_send(conn) do | |
endpoint_name = endpoint_name(conn) | |
uri = "#{conn.request_path}/#{action_name(conn)}" | |
ScoutApm.TrackedRequest.stop_layer(fn layer -> | |
layer | |
|> Layer.update_name(endpoint_name) | |
|> Layer.update_uri(uri) | |
end) | |
conn | |
end | |
defp endpoint_name(conn), do: "#{@endpoint_prefix}##{action_name(conn)}" | |
defp action_name(%{params: %{"operationName" => operation_name}} = _conn) when is_binary(operation_name) do | |
operation_name | |
end | |
defp action_name(%{params: %{"query" => query_bin}} = _conn) when is_binary(query_bin) do | |
query_chars = :erlang.binary_to_list(query_bin) | |
with \ | |
{:ok, tokens, _line_count} <- :absinthe_lexer.string(query_chars), | |
{:ok, query_doc} <- :absinthe_parser.parse(tokens) | |
do | |
get_first_field_from_query_doc(query_doc) | |
else | |
{:error, raw_error} -> | |
log_raw_parse_error(raw_error) | |
@default_action_name | |
end | |
end | |
defp action_name(_conn), do: @default_action_name | |
defp get_first_field_from_query_doc(query_doc) do | |
query_doc | |
|> Map.get(:definitions, [%{}]) | |
|> List.first() | |
|> Map.get(:selection_set, %{}) | |
|> Map.get(:selections, [%{}]) | |
|> List.first() | |
|> Map.get(:name, @default_action_name) | |
end | |
defp log_raw_parse_error({_line, :absinthe_parser, msgs}) do | |
message = msgs |> Enum.map(&to_string/1) |> Enum.join("") | |
Logger.error("#{@error_prefix}: #{message}") | |
end | |
defp log_raw_parse_error({_line, :absinthe_lexer, {problem, field}}) do | |
message = "#{problem}: #{field}" | |
Logger.error("#{@error_prefix}: #{message}") | |
end | |
defp log_raw_parse_error(_unknown), do: Logger.error("#{@error_prefix}: unknown error") | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For anyone who is using Absinthe 1.5.x (https://github.com/absinthe-graphql/absinthe/blob/37d72813e1d89b05413e6f58f4563f89a84c94f8/CHANGELOG.md#v150-alpha),
:absinthe_lexer
is no longer used. You'll have to make a slight modification to#action_name/2
. Check out my fork, which has been updated to work with Absinthe 1.5.x: https://gist.github.com/ryancurtin/60c6e147a346f9155befa82ff1a1b199