diff --git a/Rakefile b/Rakefile index 0010d0a1931..1ff97ac14ec 100644 --- a/Rakefile +++ b/Rakefile @@ -84,6 +84,7 @@ desc 'Run RSpec' # rubocop:disable Metrics/BlockLength namespace :spec do task all: [:main, :benchmark, + :graphql, :graphql_unified_trace_patcher, :graphql_trace_patcher, :graphql_tracing_patcher, :rails, :railsredis, :railsredis_activesupport, :railsactivejob, :elasticsearch, :http, :redis, :sidekiq, :sinatra, :hanami, :hanami_autoinstrument, :profiling, :crashtracking] @@ -101,6 +102,27 @@ namespace :spec do t.rspec_opts = args.to_a.join(' ') end + RSpec::Core::RakeTask.new(:graphql) do |t, args| + t.pattern = 'spec/datadog/tracing/contrib/graphql/**/*_spec.rb' + t.exclude_pattern = 'spec/datadog/tracing/contrib/graphql/{unified_trace,trace,tracing}_patcher_spec.rb' + t.rspec_opts = args.to_a.join(' ') + end + + RSpec::Core::RakeTask.new(:graphql_unified_trace_patcher) do |t, args| + t.pattern = 'spec/datadog/tracing/contrib/graphql/unified_trace_patcher_spec.rb' + t.rspec_opts = args.to_a.join(' ') + end + + RSpec::Core::RakeTask.new(:graphql_trace_patcher) do |t, args| + t.pattern = 'spec/datadog/tracing/contrib/graphql/trace_patcher_spec.rb' + t.rspec_opts = args.to_a.join(' ') + end + + RSpec::Core::RakeTask.new(:graphql_tracing_patcher) do |t, args| + t.pattern = 'spec/datadog/tracing/contrib/graphql/tracing_patcher_spec.rb' + t.rspec_opts = args.to_a.join(' ') + end + desc '' # "Explicitly hiding from `rake -T`" RSpec::Core::RakeTask.new(:opentelemetry) do |t, args| t.pattern = 'spec/datadog/opentelemetry/**/*_spec.rb,spec/datadog/opentelemetry_spec.rb' @@ -223,7 +245,6 @@ namespace :spec do :excon, :faraday, :grape, - :graphql, :grpc, :http, :httpclient, diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index a9bb30c26a9..8a1196fa138 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -845,8 +845,14 @@ To activate your integration, use the `Datadog.configure` method: ```ruby # Inside Rails initializer or equivalent +# For graphql >= v2.2 Datadog.configure do |c| - c.tracing.instrument :graphql, schemas: [YourSchema], **options + c.tracing.instrument :graphql, with_unified_tracer: true, **options +end + +# For graphql < v2.2 +Datadog.configure do |c| + c.tracing.instrument :graphql, **options end # Then run a GraphQL query @@ -859,31 +865,38 @@ The `instrument :graphql` method accepts the following parameters. Additional op | ------------------------ | -------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | | `enabled` | `DD_TRACE_GRAPHQL_ENABLED` | `Bool` | Whether the integration should create spans. | `true` | | `schemas` | | `Array` | Array of `GraphQL::Schema` objects (that support class-based schema only) to trace. If you do not provide any, then tracing will applied to all the schemas. | `[]` | -| `with_unified_tracer` | | `Bool` | Enable to instrument with `UnifiedTrace` tracer, enabling support for API Catalog. `with_deprecated_tracer` has priority over this. Default is `false`, using `GraphQL::Tracing::DataDogTrace` (Added in v2.2) | `false` | -| `with_deprecated_tracer` | | `Bool` | Enable to instrument with deprecated `GraphQL::Tracing::DataDogTracing`. This has priority over `with_unified_tracer`. Default is `false`, using `GraphQL::Tracing::DataDogTrace` | `false` | +| `with_unified_tracer` | | `Bool` | (Recommended) Enable to instrument with `UnifiedTrace` tracer for `graphql` >= v2.2, **enabling support for API Catalog**. `with_deprecated_tracer` has priority over this. Default is `false`, using `GraphQL::Tracing::DataDogTrace` instead | `false` | +| `with_deprecated_tracer` | | `Bool` | Enable to instrument with deprecated `GraphQL::Tracing::DataDogTracing`. This has priority over `with_unified_tracer`. Default is `false`, using `GraphQL::Tracing::DataDogTrace` instead | `false` | | `service_name` | | `String` | Service name used for graphql instrumentation | `'ruby-graphql'` | +Once an instrumentation strategy is selected (`with_unified_tracer: true`, `with_deprecated_tracer: true`, or *no option set* which defaults to `GraphQL::Tracing::DataDogTrace`), it is not possible to change the instrumentation strategy in the same Ruby process. +This is especially important for [auto instrumented applications](#rails-or-hanami-applications) because an automatic initial instrumentation is always applied at startup, thus such applications will always instrument GraphQL with the default strategy (`GraphQL::Tracing::DataDogTrace`). + **Manually configuring GraphQL schemas** -If you prefer to individually configure the tracer settings for a schema (e.g. you have multiple schemas), in the schema definition, you can add the following [using the GraphQL API](http://graphql-ruby.org/queries/tracing.html): +If you prefer, you can individually configure the tracer settings per schema (e.g. you have multiple schemas with distinct instrumentation options). + +Do _NOT_ `c.tracing.instrument :graphql` in `Datadog.configure` if you choose to configure schema settings manually, as to avoid double tracing. These two means of configuring GraphQL tracing are mutually exclusive. + +To instrument each schema individually, you add the following [using the GraphQL API](http://graphql-ruby.org/queries/tracing.html): -With `GraphQL::Tracing::DataDogTrace` +For `graphql` >= v2.2: ```ruby class YourSchema < GraphQL::Schema - trace_with GraphQL::Tracing::DataDogTrace + trace_with Datadog::Tracing::Contrib::GraphQL::UnifiedTrace end ``` -With `UnifiedTracer` (Added in v2.2) +For `graphql` < v2.2: ```ruby class YourSchema < GraphQL::Schema - trace_with Datadog::Tracing::Contrib::GraphQL::UnifiedTrace + trace_with GraphQL::Tracing::DataDogTrace end ``` -or with `GraphQL::Tracing::DataDogTracing` (deprecated) +Using the deprecated tracer GraphQL (`GraphQL::Tracing::DataDogTracing`): ```ruby class YourSchema < GraphQL::Schema @@ -893,8 +906,6 @@ end **Note**: This integration does not support define-style schemas. Only class-based schemas are supported. -Do _NOT_ `instrument :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. - **Adding custom tags to Datadog spans** You can add custom tags to Datadog spans by implementing the `prepare_span` method in a subclass, then manually configuring your schema. diff --git a/lib/datadog/tracing/contrib/graphql/patcher.rb b/lib/datadog/tracing/contrib/graphql/patcher.rb index b473b999ff5..36c1e736be5 100644 --- a/lib/datadog/tracing/contrib/graphql/patcher.rb +++ b/lib/datadog/tracing/contrib/graphql/patcher.rb @@ -21,13 +21,18 @@ def target_version end def patch + # DEV-3.0: We should remove as many patching options as possible, given the alternatives do not + # DEV-3.0: provide any benefit to the recommended `with_unified_tracer` patching method. + # DEV-3.0: `with_deprecated_tracer` is likely safe to remove. + # DEV-3.0: `with_unified_tracer: false` should be removed if possible. + # DEV-3.0: `with_unified_tracer: true` should be the default and hopefully not even necessary as an option. if configuration[:with_deprecated_tracer] - TracingPatcher.patch!(schemas, trace_options) + TracingPatcher.patch!(schemas) elsif Integration.trace_supported? if configuration[:with_unified_tracer] - UnifiedTracePatcher.patch!(schemas, trace_options) + UnifiedTracePatcher.patch!(schemas) else - TracePatcher.patch!(schemas, trace_options) + TracePatcher.patch!(schemas) end else Datadog.logger.warn( @@ -35,18 +40,10 @@ def patch 'or Datadog::Tracing::Contrib::GraphQL::UnifiedTrace.'\ 'Falling back to GraphQL::Tracing::DataDogTracing.' ) - TracingPatcher.patch!(schemas, trace_options) + TracingPatcher.patch!(schemas) end end - def trace_options - { - service: configuration[:service_name], - analytics_enabled: Contrib::Analytics.enabled?(configuration[:analytics_enabled]), - analytics_sample_rate: configuration[:analytics_sample_rate] - } - end - def configuration Datadog.configuration.tracing[:graphql] end diff --git a/lib/datadog/tracing/contrib/graphql/trace_patcher.rb b/lib/datadog/tracing/contrib/graphql/trace_patcher.rb index ea4d90797e7..dfc00dc0d01 100644 --- a/lib/datadog/tracing/contrib/graphql/trace_patcher.rb +++ b/lib/datadog/tracing/contrib/graphql/trace_patcher.rb @@ -8,12 +8,12 @@ module GraphQL module TracePatcher module_function - def patch!(schemas, options) + def patch!(schemas) if schemas.empty? - ::GraphQL::Schema.trace_with(::GraphQL::Tracing::DataDogTrace, **options) + ::GraphQL::Schema.trace_with(::GraphQL::Tracing::DataDogTrace) else schemas.each do |schema| - schema.trace_with(::GraphQL::Tracing::DataDogTrace, **options) + schema.trace_with(::GraphQL::Tracing::DataDogTrace) end end end diff --git a/lib/datadog/tracing/contrib/graphql/tracing_patcher.rb b/lib/datadog/tracing/contrib/graphql/tracing_patcher.rb index 9a662c0c30c..d43de7f3d79 100644 --- a/lib/datadog/tracing/contrib/graphql/tracing_patcher.rb +++ b/lib/datadog/tracing/contrib/graphql/tracing_patcher.rb @@ -8,13 +8,13 @@ module GraphQL module TracingPatcher module_function - def patch!(schemas, options) + def patch!(schemas) if schemas.empty? - ::GraphQL::Schema.tracer(::GraphQL::Tracing::DataDogTracing.new(**options)) + ::GraphQL::Schema.tracer(::GraphQL::Tracing::DataDogTracing.new) else schemas.each do |schema| if schema.respond_to? :use - schema.use(::GraphQL::Tracing::DataDogTracing, **options) + schema.use(::GraphQL::Tracing::DataDogTracing) else Datadog.logger.warn("Unable to patch #{schema}: Please migrate to class-based schema.") end diff --git a/lib/datadog/tracing/contrib/graphql/unified_trace.rb b/lib/datadog/tracing/contrib/graphql/unified_trace.rb index 2bb739351b4..12ea44b5940 100644 --- a/lib/datadog/tracing/contrib/graphql/unified_trace.rb +++ b/lib/datadog/tracing/contrib/graphql/unified_trace.rb @@ -11,14 +11,7 @@ module GraphQL # which is required to use features such as API Catalog. # DEV-3.0: This tracer should be the default one in the next major version. module UnifiedTrace - # @param analytics_enabled [Boolean] Deprecated - # @param analytics_sample_rate [Float] Deprecated - # @param service [String|nil] The service name to be set on the spans - def initialize(*args, analytics_enabled: false, analytics_sample_rate: 1.0, service: nil, **kwargs) - @analytics_enabled = analytics_enabled - @analytics_sample_rate = analytics_sample_rate - - @service_name = service + def initialize(*args, **kwargs) @has_prepare_span = respond_to?(:prepare_span) super end @@ -139,7 +132,18 @@ def platform_resolve_type_key(type, *args, **kwargs) private def trace(callable, trace_key, resource, **kwargs) - Tracing.trace("graphql.#{trace_key}", resource: resource, service: @service_name, type: 'graphql') do |span| + config = Datadog.configuration.tracing[:graphql] + + Tracing.trace( + "graphql.#{trace_key}", + type: 'graphql', + resource: resource, + service: config[:service_name] + ) do |span| + if Contrib::Analytics.enabled?(config[:analytics_enabled]) + Contrib::Analytics.set_sample_rate(span, config[:analytics_sample_rate]) + end + yield(span) if block_given? prepare_span(trace_key, kwargs, span) if @has_prepare_span diff --git a/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb b/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb index addea2950e2..9959f8733e0 100644 --- a/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +++ b/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb @@ -12,12 +12,15 @@ module GraphQL module UnifiedTracePatcher module_function - def patch!(schemas, options) + # TODO: `GraphQL::Schema.trace_with` and `YOUR_SCHEMA.trace_with` don't mix. + # TODO: They create duplicate spans when combined. + # TODO: We should measure how frequently users use `YOUR_SCHEMA.trace_with`, and hopefully we can remove it. + def patch!(schemas) if schemas.empty? - ::GraphQL::Schema.trace_with(UnifiedTrace, **options) + ::GraphQL::Schema.trace_with(UnifiedTrace) else schemas.each do |schema| - schema.trace_with(UnifiedTrace, **options) + schema.trace_with(UnifiedTrace) end end end diff --git a/spec/datadog/tracing/contrib/graphql/patcher_spec.rb b/spec/datadog/tracing/contrib/graphql/patcher_spec.rb index d828918fd1f..535778ee17c 100644 --- a/spec/datadog/tracing/contrib/graphql/patcher_spec.rb +++ b/spec/datadog/tracing/contrib/graphql/patcher_spec.rb @@ -7,9 +7,14 @@ require 'datadog' RSpec.describe Datadog::Tracing::Contrib::GraphQL::Patcher do + before(:context) { load_test_schema } + after(:context) do + unload_test_schema + remove_patch!(:graphql) + end + around do |example| remove_patch!(:graphql) - Datadog.configuration.reset! Datadog.configuration.tracing[:graphql].reset! without_warnings do @@ -17,7 +22,6 @@ end remove_patch!(:graphql) - Datadog.configuration.reset! Datadog.configuration.tracing[:graphql].reset! end @@ -26,10 +30,7 @@ context 'with default configuration' do it 'patches GraphQL' do allow(Datadog::Tracing::Contrib::GraphQL::Integration).to receive(:trace_supported?).and_return(true) - expect(Datadog::Tracing::Contrib::GraphQL::TracePatcher).to receive(:patch!).with( - [], - hash_including(:analytics_enabled, :analytics_sample_rate, :service) - ) + expect(Datadog::Tracing::Contrib::GraphQL::TracePatcher).to receive(:patch!).with([]) Datadog.configure do |c| c.tracing.instrument :graphql @@ -40,10 +41,7 @@ context 'with with_deprecated_tracer enabled' do it do allow(Datadog::Tracing::Contrib::GraphQL::Integration).to receive(:trace_supported?).and_return(true) - expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with( - [], - hash_including(:analytics_enabled, :analytics_sample_rate, :service) - ) + expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with([]) Datadog.configure do |c| c.tracing.instrument :graphql, with_deprecated_tracer: true @@ -54,10 +52,7 @@ context 'with with_deprecated_tracer disabled' do it do allow(Datadog::Tracing::Contrib::GraphQL::Integration).to receive(:trace_supported?).and_return(true) - expect(Datadog::Tracing::Contrib::GraphQL::TracePatcher).to receive(:patch!).with( - [], - hash_including(:analytics_enabled, :analytics_sample_rate, :service) - ) + expect(Datadog::Tracing::Contrib::GraphQL::TracePatcher).to receive(:patch!).with([]) Datadog.configure do |c| c.tracing.instrument :graphql, with_deprecated_tracer: false @@ -68,10 +63,7 @@ context 'with with_unified_tracer enabled' do it do allow(Datadog::Tracing::Contrib::GraphQL::Integration).to receive(:trace_supported?).and_return(true) - expect(Datadog::Tracing::Contrib::GraphQL::UnifiedTracePatcher).to receive(:patch!).with( - [], - hash_including(:analytics_enabled, :analytics_sample_rate, :service) - ) + expect(Datadog::Tracing::Contrib::GraphQL::UnifiedTracePatcher).to receive(:patch!).with([]) Datadog.configure do |c| c.tracing.instrument :graphql, with_unified_tracer: true @@ -82,10 +74,7 @@ context 'with with_unified_tracer disabled' do it do allow(Datadog::Tracing::Contrib::GraphQL::Integration).to receive(:trace_supported?).and_return(true) - expect(Datadog::Tracing::Contrib::GraphQL::TracePatcher).to receive(:patch!).with( - [], - hash_including(:analytics_enabled, :analytics_sample_rate, :service) - ) + expect(Datadog::Tracing::Contrib::GraphQL::TracePatcher).to receive(:patch!).with([]) Datadog.configure do |c| c.tracing.instrument :graphql, with_unified_tracer: false @@ -96,10 +85,7 @@ context 'with with_unified_tracer enabled and with_deprecated_tracer enabled' do it do allow(Datadog::Tracing::Contrib::GraphQL::Integration).to receive(:trace_supported?).and_return(true) - expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with( - [], - hash_including(:analytics_enabled, :analytics_sample_rate, :service) - ) + expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with([]) Datadog.configure do |c| c.tracing.instrument :graphql, with_unified_tracer: true, with_deprecated_tracer: true @@ -110,10 +96,7 @@ context 'with given schema' do it do allow(Datadog::Tracing::Contrib::GraphQL::Integration).to receive(:trace_supported?).and_return(true) - expect(Datadog::Tracing::Contrib::GraphQL::TracePatcher).to receive(:patch!).with( - [TestGraphQLSchema], - hash_including(:analytics_enabled, :analytics_sample_rate, :service) - ) + expect(Datadog::Tracing::Contrib::GraphQL::TracePatcher).to receive(:patch!).with([TestGraphQLSchema]) Datadog.configure do |c| c.tracing.instrument :graphql, schemas: [TestGraphQLSchema] @@ -126,10 +109,7 @@ context 'with default configuration' do it 'patches GraphQL' do allow(Datadog::Tracing::Contrib::GraphQL::Integration).to receive(:trace_supported?).and_return(false) - expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with( - [], - hash_including(:analytics_enabled, :analytics_sample_rate, :service) - ) + expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with([]) expect_any_instance_of(Datadog::Core::Logger).to receive(:warn) .with(/Falling back to GraphQL::Tracing::DataDogTracing/) @@ -142,10 +122,7 @@ context 'with with_deprecated_tracer enabled' do it do allow(Datadog::Tracing::Contrib::GraphQL::Integration).to receive(:trace_supported?).and_return(false) - expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with( - [], - hash_including(:analytics_enabled, :analytics_sample_rate, :service) - ) + expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with([]) expect_any_instance_of(Datadog::Core::Logger).not_to receive(:warn) Datadog.configure do |c| @@ -157,10 +134,7 @@ context 'with with_deprecated_tracer disabled' do it do allow(Datadog::Tracing::Contrib::GraphQL::Integration).to receive(:trace_supported?).and_return(false) - expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with( - [], - hash_including(:analytics_enabled, :analytics_sample_rate, :service) - ) + expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with([]) expect_any_instance_of(Datadog::Core::Logger).to receive(:warn) .with(/Falling back to GraphQL::Tracing::DataDogTracing/) @@ -173,10 +147,7 @@ context 'with with_unified_tracer enabled' do it do allow(Datadog::Tracing::Contrib::GraphQL::Integration).to receive(:trace_supported?).and_return(false) - expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with( - [], - hash_including(:analytics_enabled, :analytics_sample_rate, :service) - ) + expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with([]) expect_any_instance_of(Datadog::Core::Logger).to receive(:warn) .with(/Falling back to GraphQL::Tracing::DataDogTracing/) @@ -189,10 +160,7 @@ context 'with with_unified_tracer disabled' do it do allow(Datadog::Tracing::Contrib::GraphQL::Integration).to receive(:trace_supported?).and_return(false) - expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with( - [], - hash_including(:analytics_enabled, :analytics_sample_rate, :service) - ) + expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with([]) expect_any_instance_of(Datadog::Core::Logger).to receive(:warn) .with(/Falling back to GraphQL::Tracing::DataDogTracing/) @@ -205,10 +173,7 @@ context 'with with_unified_tracer enabled and with_deprecated_tracer enabled' do it do allow(Datadog::Tracing::Contrib::GraphQL::Integration).to receive(:trace_supported?).and_return(false) - expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with( - [], - hash_including(:analytics_enabled, :analytics_sample_rate, :service) - ) + expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with([]) Datadog.configure do |c| c.tracing.instrument :graphql, with_unified_tracer: true, with_deprecated_tracer: true @@ -219,10 +184,7 @@ context 'with given schema' do it do allow(Datadog::Tracing::Contrib::GraphQL::Integration).to receive(:trace_supported?).and_return(false) - expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with( - [TestGraphQLSchema], - hash_including(:analytics_enabled, :analytics_sample_rate, :service) - ) + expect(Datadog::Tracing::Contrib::GraphQL::TracingPatcher).to receive(:patch!).with([TestGraphQLSchema]) expect_any_instance_of(Datadog::Core::Logger).to receive(:warn) .with(/Falling back to GraphQL::Tracing::DataDogTracing/) diff --git a/spec/datadog/tracing/contrib/graphql/test_schema_examples.rb b/spec/datadog/tracing/contrib/graphql/test_schema_examples.rb index beb8f84e584..edd63741291 100644 --- a/spec/datadog/tracing/contrib/graphql/test_schema_examples.rb +++ b/spec/datadog/tracing/contrib/graphql/test_schema_examples.rb @@ -2,38 +2,42 @@ require_relative 'test_helpers' -class TestUserType < ::GraphQL::Schema::Object - field :id, ::GraphQL::Types::ID, null: false - field :name, ::GraphQL::Types::String, null: true - field :created_at, ::GraphQL::Types::String, null: false - field :updated_at, ::GraphQL::Types::String, null: false -end +def load_test_schema(prefix: '') + # rubocop:disable Security/Eval + # rubocop:disable Style/DocumentDynamicEvalDefinition + eval <<-RUBY, binding, __FILE__, __LINE__ + 1 + class #{prefix}TestUserType < ::GraphQL::Schema::Object + field :id, ::GraphQL::Types::ID, null: false + field :name, ::GraphQL::Types::String, null: true + field :created_at, ::GraphQL::Types::String, null: false + field :updated_at, ::GraphQL::Types::String, null: false + end -class TestGraphQLQuery < ::GraphQL::Schema::Object - field :user, TestUserType, null: false, description: 'Find an user by ID' do - argument :id, ::GraphQL::Types::ID, required: true - end + class #{prefix}TestGraphQLQuery < ::GraphQL::Schema::Object + field :user, #{prefix}TestUserType, null: false, description: 'Find user' do + argument :id, ::GraphQL::Types::ID, required: true + end - def user(id:) - OpenStruct.new(id: id, name: 'Bits') - end + def user(id:) + OpenStruct.new(id: id, name: 'Bits') + end + end + + class #{prefix}TestGraphQLSchema < ::GraphQL::Schema + query(#{prefix}TestGraphQLQuery) + end + RUBY + # rubocop:enable Style/DocumentDynamicEvalDefinition + # rubocop:enable Security/Eval end -class TestGraphQLSchema < ::GraphQL::Schema - query(TestGraphQLQuery) +def unload_test_schema(prefix: '') + Object.send(:remove_const, :"#{prefix}TestUserType") + Object.send(:remove_const, :"#{prefix}TestGraphQLQuery") + Object.send(:remove_const, :"#{prefix}TestGraphQLSchema") end RSpec.shared_examples 'graphql default instrumentation' do - around do |example| - Datadog::GraphQLTestHelpers.reset_schema_cache!(::GraphQL::Schema) - Datadog::GraphQLTestHelpers.reset_schema_cache!(TestGraphQLSchema) - - example.run - - Datadog::GraphQLTestHelpers.reset_schema_cache!(::GraphQL::Schema) - Datadog::GraphQLTestHelpers.reset_schema_cache!(TestGraphQLSchema) - end - describe 'query trace' do subject(:result) { TestGraphQLSchema.execute('{ user(id: 1) { name } }') } @@ -70,27 +74,17 @@ class TestGraphQLSchema < ::GraphQL::Schema end end -RSpec.shared_examples 'graphql instrumentation with unified naming convention trace' do - around do |example| - Datadog::GraphQLTestHelpers.reset_schema_cache!(::GraphQL::Schema) - Datadog::GraphQLTestHelpers.reset_schema_cache!(TestGraphQLSchema) - - example.run - - Datadog::GraphQLTestHelpers.reset_schema_cache!(::GraphQL::Schema) - Datadog::GraphQLTestHelpers.reset_schema_cache!(TestGraphQLSchema) - end - +RSpec.shared_examples 'graphql instrumentation with unified naming convention trace' do |prefix: ''| describe 'query trace' do - subject(:result) do - TestGraphQLSchema.execute(query: 'query Users($var: ID!){ user(id: $var) { name } }', variables: { var: 1 }) - end + subject(:result) { schema.execute(query: 'query Users($var: ID!){ user(id: $var) { name } }', variables: { var: 1 }) } + let(:schema) { Object.const_get("#{prefix}TestGraphQLSchema") } + let(:service) { defined?(super) ? super() : tracer.default_service } matrix = [ ['graphql.analyze', 'query Users($var: ID!){ user(id: $var) { name } }'], ['graphql.analyze_multiplex', 'Users'], - ['graphql.authorized', 'TestGraphQLQuery.authorized'], - ['graphql.authorized', 'TestUser.authorized'], + ['graphql.authorized', "#{prefix}TestGraphQLQuery.authorized"], + ['graphql.authorized', "#{prefix}TestUser.authorized"], ['graphql.execute', 'Users'], ['graphql.execute_lazy', 'Users'], ['graphql.execute_multiplex', 'Users'], @@ -98,8 +92,8 @@ class TestGraphQLSchema < ::GraphQL::Schema ['graphql.lex', 'query Users($var: ID!){ user(id: $var) { name } }'] end, ['graphql.parse', 'query Users($var: ID!){ user(id: $var) { name } }'], - ['graphql.resolve', 'TestGraphQLQuery.user'], - ['graphql.resolve', 'TestUser.name'], + ['graphql.resolve', "#{prefix}TestGraphQLQuery.user"], + ['graphql.resolve', "#{prefix}TestUser.name"], # New Ruby-based parser doesn't emit a "lex" event. (graphql/c_parser still does.) ['graphql.validate', 'Users'] ].compact @@ -117,7 +111,7 @@ class TestGraphQLSchema < ::GraphQL::Schema expect(span.name).to eq(name) expect(span.resource).to eq(resource) - expect(span.service).to eq(tracer.default_service) + expect(span.service).to eq(service) expect(span.type).to eq('graphql') if spans_with_source.include?(name) diff --git a/spec/datadog/tracing/contrib/graphql/trace_patcher_spec.rb b/spec/datadog/tracing/contrib/graphql/trace_patcher_spec.rb index a3898903d8e..2476537d199 100644 --- a/spec/datadog/tracing/contrib/graphql/trace_patcher_spec.rb +++ b/spec/datadog/tracing/contrib/graphql/trace_patcher_spec.rb @@ -6,11 +6,19 @@ RSpec.describe Datadog::Tracing::Contrib::GraphQL::TracePatcher, skip: Gem::Version.new(::GraphQL::VERSION) < Gem::Version.new('2.0.19') do + before(:context) { load_test_schema } + after(:context) do + unload_test_schema + remove_patch!(:graphql) + end + describe '#patch!' do context 'with empty schema configuration' do it_behaves_like 'graphql default instrumentation' do before do - described_class.patch!([], {}) + Datadog.configure do |c| + c.tracing.instrument :graphql + end end end end @@ -18,7 +26,9 @@ context 'with specified schemas configuration' do it_behaves_like 'graphql default instrumentation' do before do - described_class.patch!([TestGraphQLSchema], {}) + Datadog.configure do |c| + c.tracing.instrument :graphql, schemas: [TestGraphQLSchema] + end end end end diff --git a/spec/datadog/tracing/contrib/graphql/tracing_patcher_spec.rb b/spec/datadog/tracing/contrib/graphql/tracing_patcher_spec.rb index db036f6e4ac..1ad23c06c10 100644 --- a/spec/datadog/tracing/contrib/graphql/tracing_patcher_spec.rb +++ b/spec/datadog/tracing/contrib/graphql/tracing_patcher_spec.rb @@ -5,11 +5,23 @@ require 'datadog' RSpec.describe Datadog::Tracing::Contrib::GraphQL::TracingPatcher do + before(:context) { load_test_schema } + after(:context) do + unload_test_schema + remove_patch!(:graphql) + end + describe '#patch!' do + before do + Datadog.configuration.tracing[:graphql].reset! + end + context 'with empty schema configuration' do it_behaves_like 'graphql default instrumentation' do before do - described_class.patch!([], {}) + Datadog.configure do |c| + c.tracing.instrument :graphql, with_deprecated_tracer: true + end end end end @@ -17,16 +29,22 @@ context 'with specified schemas configuration' do it_behaves_like 'graphql default instrumentation' do before do - described_class.patch!([TestGraphQLSchema], {}) + Datadog.configure do |c| + c.tracing.instrument :graphql, with_deprecated_tracer: true, schemas: [TestGraphQLSchema] + end end end end context 'when given something else' do + before { remove_patch!(:graphql) } + it do expect_any_instance_of(Datadog::Core::Logger).to receive(:warn).with(/Unable to patch/) - described_class.patch!([OpenStruct.new], {}) + Datadog.configure do |c| + c.tracing.instrument :graphql, with_deprecated_tracer: true, schemas: [OpenStruct.new] + end end end end diff --git a/spec/datadog/tracing/contrib/graphql/unified_trace_patcher_spec.rb b/spec/datadog/tracing/contrib/graphql/unified_trace_patcher_spec.rb index d4a82adb25b..54f6b4c38fd 100644 --- a/spec/datadog/tracing/contrib/graphql/unified_trace_patcher_spec.rb +++ b/spec/datadog/tracing/contrib/graphql/unified_trace_patcher_spec.rb @@ -6,19 +6,45 @@ RSpec.describe Datadog::Tracing::Contrib::GraphQL::UnifiedTracePatcher, skip: Gem::Version.new(::GraphQL::VERSION) < Gem::Version.new('2.0.19') do + before(:context) { load_test_schema } + after(:context) do + unload_test_schema + remove_patch!(:graphql) + end + describe '#patch!' do + before do + Datadog.configuration.tracing[:graphql].reset! + end + context 'with empty schema configuration' do it_behaves_like 'graphql instrumentation with unified naming convention trace' do before do - described_class.patch!([], {}) + Datadog.configure do |c| + c.tracing.instrument :graphql, with_unified_tracer: true + end end end end + context 'with a custom service name' do + it_behaves_like 'graphql instrumentation with unified naming convention trace' do + before do + Datadog.configure do |c| + c.tracing.instrument :graphql, with_unified_tracer: true, service_name: 'my-graphql' + end + end + + let(:service) { 'my-graphql' } + end + end + context 'with specified schemas configuration' do it_behaves_like 'graphql instrumentation with unified naming convention trace' do before do - described_class.patch!([TestGraphQLSchema], {}) + Datadog.configure do |c| + c.tracing.instrument :graphql, with_unified_tracer: true, schemas: [TestGraphQLSchema] + end end end end @@ -28,16 +54,26 @@ # But this should work the same way without the need to require the tracer in the schema. describe '#trace_with' do context 'with schema using trace_with' do - it_behaves_like 'graphql instrumentation with unified naming convention trace' do + it_behaves_like 'graphql instrumentation with unified naming convention trace', prefix: 'TraceWith' do before do + load_test_schema(prefix: 'TraceWith') + + Datadog.configuration.tracing[:graphql].reset! + + # Datadog.configure do |c| + # c.tracing.instrument :graphql, with_unified_tracer: true + # end + # Monkey patch the schema to use the unified tracer # As we're not adding a new method, we cannot use allow(...).to receive(...) # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration - class TestGraphQLSchema + class TraceWithTestGraphQLSchema trace_with Datadog::Tracing::Contrib::GraphQL::UnifiedTrace end # rubocop:enable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration end + + after { unload_test_schema(prefix: 'TraceWith') } end end end