Extends Absinthe to automatically create OpenCensus spans. Designed to work with whatever is producing spans upstream, e.g. Opencensus.Plug.
- Take the dependency
- Set up the pipeline
- Set up the middleware
- Adjust your schema
- Check it's all working
If you're using Absinthe.Plug, add opencensus_absinthe to your deps
in mix.exs using a tighter version constraint than:
{:absinthe_plug, ">= 0.0.0"},
{:opencensus_absinthe, ">= 0.0.0"},Add a :pipeline to your t:Absinthe.Plug.opts/0 to have it call
Opencensus.Absinthe.Plug.traced_pipeline/2. If you're using
Phoenix.Router.forward/4, for example:
forward(
path,
Absinthe.Plug,
# ... existing config ...
pipeline: {Opencensus.Absinthe.Plug, :traced_pipeline}
)If you already have a pipeline, you can define your own and call both to
insert their phases. To work with ApolloTracing, for example:
def your_custom_pipeline(config, pipeline_opts \\ []) do
config
|> Absinthe.Plug.default_pipeline(pipeline_opts)
|> ApolloTracing.Pipeline.add_phases()
|> Opencensus.Absinthe.add_phases()
endWorst case, you'll need to copy the code from the current pipeline target
and add a call to Opencensus.Absinthe.add_phases/1 as above.
Your middleware callback needs to run its output through
the matching function in Opencensus.Absinthe.Middleware to add the
middleware to only the fields that need it:
def middleware(middleware, field, object) do
Opencensus.Absinthe.middleware(middleware, field, object)
endIf you've already got some middleware, like above, you might need to copy some code around to get the job done:
def middleware(middleware, field, object) do
([ApolloTracing.Middleware.Tracing, ApolloTracing.Middleware.Caching] ++ middleware)
|> Opencensus.Absinthe.middleware(field, object)
endUntil Absinthe merge and publish their telemetry support (see below) and
you upgrade, you'll also need to set :trace in the metadata for any
field for which you want tracing to happen:
query do
@desc "List all the things"
field :things, list_of(:thing), meta: [trace: true] do
resolve(&Resolvers.Account.all_things/2)
end
Once you're on a telemetry-capable Absinthe, you'll get tracing for every
field containing a resolve.
Check your installation with iex -S mix phx.server, assuming Phoenix, and:
iex> :oc_reporter.register(:oc_reporter_stdout)
:ok
Fire off a few requests and check the {span, <<NAME> lines on standard
output.
-
If you see names matching your GraphQL route, e.g.
<</api>>, you set upopencensus_plugproperly. -
If you see
<<"Absinthe.Blueprint">>, the pipeline is working. -
If you see
<<"YourProject.Schema:thefield">>, the middleware is working and you've either:-
Added
meta: [trace: true]to yourfield :thefieldas above, or -
Upgraded to a telemetry-capable Absinthe.
-
Each Absinthe query runs in the process of its caller. If you hook up
opencensus_plug, or something else that'll take trace
details off the wire, the process dictionary will have an :oc_span_ctx_key
key used by opencensus to keep track of spans in flight.
This package adds new phases to your Absinthe Pipeline to start new spans for each resolution and call, using both methods available:
opencensusprovides two methods for tracking [trace and span] context, the process dictionary and a variable holding a ctx record.
Specifically, this package:
-
Starts a new span registered in the process dictionary for each query, and
-
Without any use of the process dictionary, starts a new span for each field, using the query span as the parent.
The latter is necessary because the fields don't necessarily start and stop
without overlap. Naïve use of :ocp.with_child_span and :ocp.finish_span
will yield incorrect traces.
Dependency management:
mix deps.getto get your dependenciesmix deps.compileto compile themmix licensesto check their license declarations, recursively
Finding problems:
mix compileto compile your codemix credoto suggest more idiomatic style for itmix dialyzerto find problems static typing might spot... slowlymix testto run unit testsmix test.watchto run the tests again whenever you change somethingmix coverallsto check test coverage
Documentation:
mix docsto generate documentation for this projectmix helpto find out what else you can do withmix
Obvious next steps include stronger tests and many minor tweaks:
- Rename the outer span according to the schema
- Set some attributes on the outer span
- Trim the path from references so it starts with the closest
lib - Set the span status on completion
- Retire
lib/opencensus/absinthe/logger.exwhen possible
The biggest looming change would be telemetry integration:
absinthe-graphql/absinthe#663 to add telemetry to
Absinthe could give us start and stop calls from within the calling process
suitable for calling :ocp.with_child_span and :ocp.finish_span to
maintain the main trace. In turn, that'd mean we didn't need the pipeline.
#663 won't help us generate spans for fields, because there's no way to
pass state back through :telemetry.execute. That said, it'll automatically
set :absinthe_telemetry in the field metadata if query is present.
Rather than push back on the telemetry support to make it better support tracing, we could integrate this capability directly with Absinthe if:
- The community deploy a lot of
opencensus - It proves to be as lightweight and stable as
telemetry - Its impact when not hooked up is minimal or zero
We could then retire this module except for users with older versions.