Setting logger metadata in absinthe middleware

I have an app getting a lot of timeouts while resolving GraphQL queries, and I’m trying to identify the culprit query or queries. My logs don’t have much context right now, so I’m looking to add the current object and field identifiers to the logger metadata.

My best idea so far is to have a middleware put/delete the appropriate metadata. Here’s what I have so far:

defmodule MyAppWeb.GraphQL.Schema do
  use Absinthe.Schema
  alias MyAppWeb.GraphQL.Middleware.LoggerMetadata


  def middleware(middleware, field, object) do
    Enum.flat_map(middleware, fn
      current_middleware = {{Absinthe.Resolution, :call}, _resolver} ->
          {{LoggerMetadata, :put}, {object.identifier, field.identifier}},
          {{LoggerMetadata, :delete}, nil}

      current_middleware ->
defmodule MyAppWeb.GraphQL.Middleware.LoggerMetadata do
  @spec put(Absinthe.Resolution.t(), {atom(), atom()}) :: Absinthe.Resolution.t()
  def put(resolution, {object_id, field_id}) do
    Logger.metadata(graphql_object: object_id, graphql_field: field_id)


  @spec delete(Absinthe.Resolution.t(), none()) :: Absinthe.Resolution.t()
  def delete(resolution, _) do
    Logger.metadata(graphql_object: nil, graphql_field: nil)


This works, but the Enum.flat_map/2 solution seems brittle to me: if the behavior of the resolve macro ever changed, this would no longer work.

Is there a better way about this? I’d love to just be able to change the behavior of the resolve macro to return all three middlewares, but I’m assuming that’s not possible (the source code has been hard to follow). I don’t want to create a custom resolve macro; it seems like too much of a chore to enforce its use over the regular resolve macro.

Hey @brettbeatty. This seems like a great time to use Telemetry: