Skip to content

Commit

Permalink
Merge pull request #295 from DataDog/delner/add_graphql_configuration
Browse files Browse the repository at this point in the history
Add GraphQL configuration
  • Loading branch information
delner authored Feb 7, 2018
2 parents 0b99c2e + bd10fbf commit 815a66a
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 8 deletions.
1 change: 1 addition & 0 deletions Appraisals
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ if RUBY_VERSION >= '2.2.2' && RUBY_PLATFORM != 'java'
appraise 'contrib' do
gem 'elasticsearch-transport'
gem 'mongo', '< 2.5'
gem 'graphql'
gem 'grape'
gem 'rack'
gem 'rack-test'
Expand Down
19 changes: 11 additions & 8 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ namespace :spec do
:rack,
:faraday,
:grape,
:graphql,
:aws,
:sucker_punch,
:mongodb,
Expand Down Expand Up @@ -108,18 +109,19 @@ namespace :test do
end

[
:aws,
:elasticsearch,
:http,
:redis,
:sinatra,
:sidekiq,
:rack,
:faraday,
:grape,
:aws,
:sucker_punch,
:http,
:mongodb,
:resque
:resque,
:rack,
:redis,
:resque,
:sidekiq,
:sinatra,
:sucker_punch
].each do |contrib|
Rake::TestTask.new(contrib) do |t|
t.libs << %w[test lib]
Expand Down Expand Up @@ -230,6 +232,7 @@ task :ci do
# RSpec
sh 'rvm $MRI_VERSIONS --verbose do appraisal contrib rake spec:active_record'
sh 'rvm $MRI_VERSIONS --verbose do appraisal contrib rake spec:dalli'
sh 'rvm $MRI_VERSIONS --verbose do appraisal contrib rake spec:graphql'
sh 'rvm $MRI_VERSIONS --verbose do appraisal contrib rake spec:racecar'
sh 'rvm $MRI_OLD_VERSIONS --verbose do appraisal contrib-old rake spec:dalli'
sh 'rvm $MRI_OLD_VERSIONS --verbose do appraisal contrib-old rake spec:active_record'
Expand Down
52 changes: 52 additions & 0 deletions docs/GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ For further details and options, check our integrations list.
* [Ruby on Rails](#Ruby_on_Rails)
* [Sinatra](#Sinatra)
* [Rack](#Rack)
* [GraphQL](#GraphQL)
* [Grape](#Grape)
* [Active Record](#Active_Record)
* [Elastic Search](#Elastic_Search)
Expand Down Expand Up @@ -144,6 +145,57 @@ Where `options` is an optional `Hash` that accepts the following parameters:

## Other libraries

### GraphQL

*Version 1.7.9+ supported*

The GraphQL integration activates instrumentation for GraphQL queries. To activate your integration, use the ``Datadog.configure`` method:

# Inside Rails initializer or equivalent
Datadog.configure do |c|
c.use :graphql,
service_name: 'graphql',
schemas: [YourSchema]
end

# Then run a GraphQL query
YourSchema.execute(query, variables: {}, context: {}, operation_name: nil)

The `use :graphql` method accepts the following parameters:

| Key | Description | Default |
| --- | --- | --- |
| ``service_name`` | Service name used for `graphql` instrumentation | ``ruby-graphql`` |
| ``schemas`` | Required. Array of `GraphQL::Schema` objects which to trace. Tracing will be added to all the schemas listed, using the options provided to this configuration. If you do not provide any, then tracing will not be activated. | ``[]`` |
| ``tracer`` | A ``Datadog::Tracer`` instance used to instrument the application. Usually you don't need to set that. | ``Datadog.tracer`` |

##### Manually configuring GraphQL schemas

If you prefer to individually configure the tracer settings for a schema (e.g. you have multiple schemas with different service names),
in the schema definition, you can add the following [using the GraphQL API](http://graphql-ruby.org/queries/tracing.html):

```
YourSchema = GraphQL::Schema.define do
use(
GraphQL::Tracing::DataDogTracing,
service: 'graphql'
)
end
```

Or you can modify an already defined schema:

```
YourSchema.define do
use(
GraphQL::Tracing::DataDogTracing,
service: 'graphql'
)
end
```

Do *not* `use :graphql` in `Datadog.configure` if you choose to configure manually, as to avoid double tracing. These two means of configuring GraphQL tracing are considered mutually exclusive.

### Grape

The Grape integration adds the instrumentation to Grape endpoints and filters. This integration can work side by side
Expand Down
1 change: 1 addition & 0 deletions gemfiles/contrib.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ source "https://rubygems.org"
gem "pry-nav", git: "https://github.com/nixme/pry-nav.git", branch: "master"
gem "elasticsearch-transport"
gem "mongo", "< 2.5"
gem "graphql"
gem "grape"
gem "rack"
gem "rack-test"
Expand Down
1 change: 1 addition & 0 deletions lib/ddtrace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def configure(target = configuration, opts = {})
require 'ddtrace/contrib/elasticsearch/patcher'
require 'ddtrace/contrib/faraday/patcher'
require 'ddtrace/contrib/grape/patcher'
require 'ddtrace/contrib/graphql/patcher'
require 'ddtrace/contrib/redis/patcher'
require 'ddtrace/contrib/http/patcher'
require 'ddtrace/contrib/aws/patcher'
Expand Down
57 changes: 57 additions & 0 deletions lib/ddtrace/contrib/graphql/patcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
require 'ddtrace/ext/app_types'
require 'ddtrace/ext/http'

module Datadog
module Contrib
module GraphQL
# Provides instrumentation for `graphql` through the GraphQL tracing framework
module Patcher
include Base
register_as :graphql

option :tracer, default: Datadog.tracer
option :service_name, default: 'ruby-graphql', depends_on: [:tracer] do |value|
get_option(:tracer).set_service_info(value, 'ruby-graphql', Ext::AppTypes::WEB)
value
end
option :schemas

class << self
def patch
return patched? if patched? || !compatible? || get_option(:schemas).nil?

get_option(:schemas).each { |s| patch_schema!(s) }

@patched = true
end

def patch_schema!(schema)
tracer = get_option(:tracer)
service_name = get_option(:service_name)

schema.define do
use(
::GraphQL::Tracing::DataDogTracing,
tracer: tracer,
service: service_name
)
end
end

def patched?
return @patched if defined?(@patched)
@patched = false
end

private

def compatible?
defined?(::GraphQL) \
&& defined?(::GraphQL::Tracing::DataDogTracing) \
&& Gem.loaded_specs['graphql'].version >= Gem::Version.new('1.7.9')
end
end
end
end
end
end
54 changes: 54 additions & 0 deletions spec/ddtrace/contrib/graphql/test_types.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
LogHelpers.without_warnings do
require 'graphql'
end

RSpec.shared_context 'GraphQL test schema' do
let(:schema) do
qt = query_type

::GraphQL::Schema.define do
query(qt)
end
end

let(:query_type_name) { 'Query' }
let(:query_type) do
qtn = query_type_name
ot = object_type
oc = object_class

::GraphQL::ObjectType.define do
name qtn
field ot.name.downcase.to_sym do
type ot
argument :id, !types.ID
description 'Find an object by ID'
resolve ->(_obj, args, _ctx) { oc.new(args['id']) }
end
end
end

let(:object_type_name) { 'Foo' }
let(:object_type) do
otn = object_type_name

::GraphQL::ObjectType.define do
name otn
field :id, !types.ID
field :name, types.String
field :created_at, !types.String
field :updated_at, !types.String
end
end

let(:object_class) do
Class.new do
attr_accessor :id, :name

def initialize(id, name = 'bar')
@id = id
@name = name
end
end
end
end
73 changes: 73 additions & 0 deletions spec/ddtrace/contrib/graphql/tracer_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
require 'spec_helper'
require 'ddtrace/contrib/graphql/test_types'

require 'ddtrace'

# rubocop:disable Metrics/BlockLength
RSpec.describe 'GraphQL patcher' do
include_context 'GraphQL test schema'

# GraphQL generates tons of warnings.
# This suppresses those warnings.
around(:each) do |example|
without_warnings do
example.run
end
end

let(:tracer) { ::Datadog::Tracer.new(writer: FauxWriter.new) }

def pop_spans
tracer.writer.spans(:keep)
end

let(:all_spans) { pop_spans }
let(:root_span) { all_spans.find { |s| s.parent.nil? } }

before(:each) do
Datadog.configure do |c|
c.use :graphql,
service_name: 'graphql-test',
tracer: tracer,
schemas: [schema]
end
end

describe 'query trace' do
subject(:result) { schema.execute(query, variables: {}, context: {}, operation_name: nil) }

let(:query) { '{ foo(id: 1) { name } }' }
let(:variables) { {} }
let(:context) { {} }
let(:operation_name) { nil }

it do
# Expect no errors
expect(result.to_h['errors']).to be nil

# Expect nine spans
expect(all_spans).to have(9).items

# List of valid resource names
# (If this is too brittle, revist later.)
valid_resource_names = [
'Query.foo',
'analyze.graphql',
'execute.graphql',
'lex.graphql',
'parse.graphql',
'validate.graphql'
]

# Expect root span to be 'execute.graphql'
expect(root_span.name).to eq('execute.graphql')
expect(root_span.resource).to eq('execute.graphql')

# Expect each span to be properly named
all_spans.each do |span|
expect(span.service).to eq('graphql-test')
expect(valid_resource_names).to include(span.resource.to_s)
end
end
end
end
2 changes: 2 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# require 'support/rails_active_record_helpers'
# require 'support/configuration_helpers'
require 'support/synchronization_helpers'
require 'support/log_helpers'

WebMock.allow_net_connect!
WebMock.disable!
Expand All @@ -25,6 +26,7 @@
# config.include RailsActiveRecordHelpers
# config.include ConfigurationHelpers
config.include SynchronizationHelpers
config.include LogHelpers

config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
Expand Down
15 changes: 15 additions & 0 deletions spec/support/log_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module LogHelpers
def without_warnings(&block)
LogHelpers.without_warnings(&block)
end

def self.without_warnings
v = $VERBOSE
$VERBOSE = nil
begin
yield
ensure
$VERBOSE = v
end
end
end

0 comments on commit 815a66a

Please sign in to comment.