diff --git a/CHANGELOG.md b/CHANGELOG.md index 56e0f600e..7eaff1a37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ Rails users will be able to use `bin/rails generate sentry` to generate their `config/initializers/sentry.rb` file. - Notify users when their custom options are discarded ([#2303](https://github.com/getsentry/sentry-ruby/pull/2303)) +- Add a new `:graphql` patch to automatically enable instrumenting GraphQL spans ([#2308](https://github.com/getsentry/sentry-ruby/pull/2308)) + + Usage: + + ```rb + Sentry.init do |config| + # ... + config.enabled_patches += [:graphql] + end + ``` ### Bug Fixes diff --git a/Gemfile b/Gemfile index 515440efe..70034b740 100644 --- a/Gemfile +++ b/Gemfile @@ -15,6 +15,9 @@ if ruby_version >= Gem::Version.new("2.7.0") end end +# temp pin till https://github.com/socketry/nio4r/issues/315 is fixed +gem "nio4r", "2.7.1" if RUBY_PLATFORM == "java" + # For RSpec gem "rspec", "~> 3.0" gem "rspec-retry" diff --git a/sentry-ruby/Gemfile b/sentry-ruby/Gemfile index be196db7e..9c7524dd1 100644 --- a/sentry-ruby/Gemfile +++ b/sentry-ruby/Gemfile @@ -15,6 +15,8 @@ gem "puma" gem "timecop" gem "stackprof" unless RUBY_PLATFORM == "java" +gem "graphql", ">= 2.2.6" if RUBY_VERSION.to_f >= 2.7 + gem "benchmark-ips" gem "benchmark_driver" gem "benchmark-ipsa" diff --git a/sentry-ruby/lib/sentry-ruby.rb b/sentry-ruby/lib/sentry-ruby.rb index 7e75e9759..d412dec2a 100644 --- a/sentry-ruby/lib/sentry-ruby.rb +++ b/sentry-ruby/lib/sentry-ruby.rb @@ -591,3 +591,4 @@ def utc_now require "sentry/net/http" require "sentry/redis" require "sentry/puma" +require "sentry/graphql" diff --git a/sentry-ruby/lib/sentry/graphql.rb b/sentry-ruby/lib/sentry/graphql.rb new file mode 100644 index 000000000..17bb792c6 --- /dev/null +++ b/sentry-ruby/lib/sentry/graphql.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +Sentry.register_patch(:graphql) do |config| + if defined?(::GraphQL::Schema) && defined?(::GraphQL::Tracing::SentryTrace) && ::GraphQL::Schema.respond_to?(:trace_with) + ::GraphQL::Schema.trace_with(::GraphQL::Tracing::SentryTrace, set_transaction_name: true) + else + config.logger.warn(Sentry::LOGGER_PROGNAME) { 'You tried to enable the GraphQL integration but no GraphQL gem was detected. Make sure you have the `graphql` gem (>= 2.2.6) in your Gemfile.' } + end +end diff --git a/sentry-ruby/spec/sentry/graphql_spec.rb b/sentry-ruby/spec/sentry/graphql_spec.rb new file mode 100644 index 000000000..5820b73d2 --- /dev/null +++ b/sentry-ruby/spec/sentry/graphql_spec.rb @@ -0,0 +1,78 @@ +require 'spec_helper' + +with_graphql = begin + require 'graphql' + true + rescue LoadError + false + end + +RSpec.describe 'GraphQL' do + it 'adds the graphql patch to registered patches' do + expect(Sentry.registered_patches.keys).to include(:graphql) + end + + context 'when patch enabled' do + if with_graphql + describe 'with graphql gem' do + class Thing < GraphQL::Schema::Object + field :str, String + def str; 'blah'; end + end + + class Query < GraphQL::Schema::Object + field :int, Integer, null: false + def int; 1; end + + field :thing, Thing + def thing; :thing; end + end + + class MySchema < GraphQL::Schema + query(Query) + end + + before do + perform_basic_setup do |config| + config.traces_sample_rate = 1.0 + config.enabled_patches << :graphql + end + end + + it 'enables the sentry tracer' do + expect(MySchema.trace_modules_for(:default)).to include(::GraphQL::Tracing::SentryTrace) + end + + it 'adds graphql spans to the transaction' do + transaction = Sentry.start_transaction + Sentry.get_current_scope.set_span(transaction) + MySchema.execute('query foobar { int thing { str } }') + transaction.finish + + expect(last_sentry_event.transaction).to eq('GraphQL/query.foobar') + + execute_span = last_sentry_event.spans.find { |s| s[:op] == 'graphql.execute' } + expect(execute_span[:description]).to eq('query foobar') + expect(execute_span[:data]).to eq({ + 'graphql.document'=>'query foobar { int thing { str } }', + 'graphql.operation.name'=>'foobar', + 'graphql.operation.type'=>'query' + }) + end + end + else + describe 'without graphql gem' do + it 'logs warning' do + string_io = StringIO.new + + perform_basic_setup do |config| + config.enabled_patches << :graphql + config.logger = Logger.new(string_io) + end + + expect(string_io.string).to include('WARN -- sentry: You tried to enable the GraphQL integration but no GraphQL gem was detected. Make sure you have the `graphql` gem (>= 2.2.6) in your Gemfile.') + end + end + end + end +end