├── .github └── workflows │ ├── build-release.yml │ └── test.yml ├── .gitignore ├── .rspec ├── Appraisals ├── CHANGELOG.md ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin ├── console └── setup ├── gemfiles ├── .bundle │ └── config ├── graphql_1.10.gemfile ├── graphql_1.10.gemfile.lock ├── graphql_1.11.gemfile ├── graphql_1.11.gemfile.lock ├── graphql_1.12.gemfile ├── graphql_1.12.gemfile.lock ├── graphql_1.13.gemfile ├── graphql_1.13.gemfile.lock ├── graphql_2.0.gemfile ├── graphql_2.0.gemfile.lock ├── graphql_2.1.gemfile ├── graphql_2.1.gemfile.lock ├── graphql_2.2.gemfile └── graphql_2.2.gemfile.lock ├── grafana-dashboard.json ├── lib └── yabeda │ ├── graphql.rb │ └── graphql │ ├── instrumentation.rb │ ├── version.rb │ └── yabeda_tracing.rb ├── spec ├── spec_helper.rb ├── support │ └── graphql_schema.rb └── yabeda │ └── graphql_spec.rb ├── yabeda-graphql-logo.png └── yabeda-graphql.gemspec /.github/workflows/build-release.yml: -------------------------------------------------------------------------------- 1 | name: Build and release gem to RubyGems 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | with: 14 | fetch-depth: 0 # Fetch current tag as annotated. See https://github.com/actions/checkout/issues/290 15 | - uses: ruby/setup-ruby@v1 16 | with: 17 | ruby-version: 2.7 18 | - name: "Extract data from tag: version, message, body" 19 | id: tag 20 | run: | 21 | git fetch --tags --force # Really fetch annotated tag. See https://github.com/actions/checkout/issues/290#issuecomment-680260080 22 | echo ::set-output name=version::${GITHUB_REF#refs/tags/v} 23 | echo ::set-output name=subject::$(git for-each-ref $GITHUB_REF --format='%(contents:subject)') 24 | BODY="$(git for-each-ref $GITHUB_REF --format='%(contents:body)')" 25 | # Extract changelog entries between this and previous version headers 26 | escaped_version=$(echo ${GITHUB_REF#refs/tags/v} | sed -e 's/[]\/$*.^[]/\\&/g') 27 | changelog=$(awk "BEGIN{inrelease=0} /## ${escaped_version}/{inrelease=1;next} /## [0-9]+\.[0-9]+\.[0-9]+/{inrelease=0;exit} {if (inrelease) print}" CHANGELOG.md) 28 | # Multiline body for release. See https://github.community/t/set-output-truncates-multiline-strings/16852/5 29 | BODY="${BODY}"$'\n'"${changelog}" 30 | BODY="${BODY//'%'/'%25'}" 31 | BODY="${BODY//$'\n'/'%0A'}" 32 | BODY="${BODY//$'\r'/'%0D'}" 33 | echo "::set-output name=body::$BODY" 34 | # Add pre-release option if tag name has any suffix after vMAJOR.MINOR.PATCH 35 | if [[ ${GITHUB_REF#refs/tags/} =~ ^v[0-9]+\.[0-9]+\.[0-9]+.+ ]]; then 36 | echo ::set-output name=prerelease::true 37 | fi 38 | - name: Build gem 39 | run: gem build 40 | - name: Calculate checksums 41 | run: sha256sum yabeda-graphql-${{ steps.tag.outputs.version }}.gem > SHA256SUM 42 | - name: Check version 43 | run: ls -l yabeda-graphql-${{ steps.tag.outputs.version }}.gem 44 | - name: Create Release 45 | id: create_release 46 | uses: actions/create-release@v1 47 | env: 48 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 49 | with: 50 | tag_name: ${{ github.ref }} 51 | release_name: ${{ steps.tag.outputs.subject }} 52 | body: ${{ steps.tag.outputs.body }} 53 | draft: false 54 | prerelease: ${{ steps.tag.outputs.prerelease }} 55 | - name: Upload built gem as release asset 56 | uses: actions/upload-release-asset@v1 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 59 | with: 60 | upload_url: ${{ steps.create_release.outputs.upload_url }} 61 | asset_path: yabeda-graphql-${{ steps.tag.outputs.version }}.gem 62 | asset_name: yabeda-graphql-${{ steps.tag.outputs.version }}.gem 63 | asset_content_type: application/x-tar 64 | - name: Upload checksums as release asset 65 | uses: actions/upload-release-asset@v1 66 | env: 67 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 68 | with: 69 | upload_url: ${{ steps.create_release.outputs.upload_url }} 70 | asset_path: SHA256SUM 71 | asset_name: SHA256SUM 72 | asset_content_type: text/plain 73 | - name: Publish to GitHub packages 74 | env: 75 | GEM_HOST_API_KEY: Bearer ${{ secrets.GITHUB_TOKEN }} 76 | run: | 77 | gem push yabeda-graphql-${{ steps.tag.outputs.version }}.gem --host https://rubygems.pkg.github.com/${{ github.repository_owner }} 78 | - name: Publish to RubyGems 79 | env: 80 | GEM_HOST_API_KEY: "${{ secrets.RUBYGEMS_API_KEY }}" 81 | run: | 82 | gem push yabeda-graphql-${{ steps.tag.outputs.version }}.gem 83 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - '**' 8 | tags-ignore: 9 | - 'v*' 10 | 11 | jobs: 12 | test: 13 | name: "GraphQL-Ruby ${{ matrix.graphql-ruby }} on Ruby ${{ matrix.ruby }}" 14 | runs-on: ubuntu-latest 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | include: 19 | - ruby: "3.3" 20 | graphql-ruby: "2.2" 21 | - ruby: "3.2" 22 | graphql-ruby: "2.1" 23 | - ruby: "3.1" 24 | graphql-ruby: "2.0" 25 | - ruby: "3.0" 26 | graphql-ruby: "1.13" 27 | - ruby: "2.7" 28 | graphql-ruby: "1.12" 29 | - ruby: "2.6" 30 | graphql-ruby: "1.11" 31 | - ruby: "2.5" 32 | graphql-ruby: "1.10" 33 | env: 34 | BUNDLE_GEMFILE: gemfiles/graphql_${{ matrix.graphql-ruby }}.gemfile 35 | steps: 36 | - uses: actions/checkout@v4 37 | - uses: ruby/setup-ruby@v1 38 | with: 39 | ruby-version: ${{ matrix.ruby }} 40 | bundler-cache: true 41 | - name: Run RSpec 42 | run: bundle exec rspec 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | 10 | # rspec failure tracking 11 | .rspec_status 12 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | --require spec_helper 4 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | appraise "graphql-1.10" do 2 | gem "graphql", "~> 1.10.0" 3 | end 4 | 5 | appraise "graphql-1.11" do 6 | gem "graphql", "~> 1.11.0" 7 | end 8 | 9 | appraise "graphql-1.12" do 10 | gem "graphql", "~> 1.12.0" 11 | end 12 | 13 | appraise "graphql-1.13" do 14 | gem "graphql", "~> 1.13.0" 15 | end 16 | 17 | appraise "graphql-2.0" do 18 | gem "graphql", "~> 2.0.0" 19 | end 20 | 21 | appraise "graphql-2.1" do 22 | gem "graphql", "~> 2.1.0" 23 | end 24 | 25 | appraise "graphql-2.2" do 26 | gem "graphql", "~> 2.2.0" 27 | end 28 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 7 | 8 | ## 0.2.3 - 2023-05-29 9 | 10 | ### Fixed 11 | 12 | - Compatibility with GraphQL-Ruby 2.0 tracing API. [#5](https://github.com/yabeda-rb/yabeda-graphql/pull/5) by [@skateman] 13 | 14 | ## 0.2.2 - 2022-04-04 15 | 16 | ### Fixed 17 | 18 | - GraphQL-Ruby 2.0 compatibility (allow it in gemspec). [@Envek] 19 | 20 | ## 0.2.1 - 2021-02-09 21 | 22 | ### Fixed 23 | 24 | - Ruby 3.0 compatibility. [@Envek] 25 | 26 | ## 0.2.0 - 2020-03-21 27 | 28 | ### Changed 29 | 30 | - Method of hooking gem into schema. BREAKING CHANGE! [@Envek] 31 | - Sum durations of lazy and non-lazy resolvings for every field. [@Envek] 32 | 33 | ## 0.1.0 - 2020-03-17 34 | 35 | - Initial release of yabeda-graphql gem. [@Envek] 36 | 37 | [@Envek]: https://github.com/Envek "Andrey Novikov" 38 | [@skateman]: https://github.com/skateman "Halász Dávid" 39 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # Specify your gem's dependencies in yabeda-graphql.gemspec 4 | gemspec 5 | 6 | group :development, :test do 7 | gem "pry" 8 | gem "pry-inline" 9 | gem "pry-byebug", platform: :mri 10 | gem "graphql-batch" 11 | end 12 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | yabeda-graphql (0.2.3) 5 | graphql (>= 1.9, < 3) 6 | yabeda (~> 0.2) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | anyway_config (2.6.3) 12 | ruby-next-core (~> 1.0) 13 | appraisal (2.5.0) 14 | bundler 15 | rake 16 | thor (>= 0.14.0) 17 | byebug (11.1.3) 18 | coderay (1.1.3) 19 | concurrent-ruby (1.2.3) 20 | diff-lcs (1.5.1) 21 | dry-initializer (3.1.1) 22 | graphql (2.2.8) 23 | graphql-batch (0.5.4) 24 | graphql (>= 1.12.18, < 3) 25 | promise.rb (~> 0.7.2) 26 | method_source (1.0.0) 27 | promise.rb (0.7.4) 28 | pry (0.14.2) 29 | coderay (~> 1.1) 30 | method_source (~> 1.0) 31 | pry-byebug (3.10.1) 32 | byebug (~> 11.0) 33 | pry (>= 0.13, < 0.15) 34 | pry-inline (1.0.7) 35 | pry (> 0.10.0) 36 | unicode (~> 0.4.4) 37 | rake (13.1.0) 38 | rspec (3.13.0) 39 | rspec-core (~> 3.13.0) 40 | rspec-expectations (~> 3.13.0) 41 | rspec-mocks (~> 3.13.0) 42 | rspec-core (3.13.0) 43 | rspec-support (~> 3.13.0) 44 | rspec-expectations (3.13.0) 45 | diff-lcs (>= 1.2.0, < 2.0) 46 | rspec-support (~> 3.13.0) 47 | rspec-mocks (3.13.0) 48 | diff-lcs (>= 1.2.0, < 2.0) 49 | rspec-support (~> 3.13.0) 50 | rspec-support (3.13.0) 51 | ruby-next-core (1.0.1) 52 | thor (1.3.0) 53 | unicode (0.4.4.4) 54 | yabeda (0.12.0) 55 | anyway_config (>= 1.0, < 3) 56 | concurrent-ruby 57 | dry-initializer 58 | 59 | PLATFORMS 60 | ruby 61 | 62 | DEPENDENCIES 63 | appraisal 64 | bundler 65 | graphql-batch 66 | pry 67 | pry-byebug 68 | pry-inline 69 | rake (~> 13.0) 70 | rspec (~> 3.0) 71 | yabeda-graphql! 72 | 73 | BUNDLED WITH 74 | 2.5.6 75 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Andrey Novikov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![Yabeda::GraphQL](./yabeda-graphql-logo.png) 2 | 3 | [![Gem Version](https://badge.fury.io/rb/yabeda-graphql.svg)](https://badge.fury.io/rb/yabeda-graphql) 4 | [![Tests](https://github.com/yabeda-rb/yabeda-graphql/actions/workflows/test.yml/badge.svg)](https://github.com/yabeda-rb/yabeda-graphql/actions/workflows/test.yml) 5 | 6 | Built-in metrics for [GraphQL-Ruby] monitoring out of the box! Part of the [yabeda] suite. 7 | 8 | Get sample Grafana dashboard from [Grafana.com #14774](https://grafana.com/grafana/dashboards/14774) or from [`grafana-dashboard.json`](./grafana-dashboard.json) file. 9 | 10 | ## Installation 11 | 12 | 1. Add the gem to your Gemfile: 13 | 14 | ```ruby 15 | gem 'yabeda-graphql' 16 | 17 | # Then add monitoring system adapter, e.g.: 18 | # gem 'yabeda-prometheus' 19 | 20 | # If you're using Rails don't forget to add plugin for it: 21 | # gem 'yabeda-rails' 22 | # But if not then you should run `Yabeda.configure!` manually when your app is ready. 23 | ``` 24 | 25 | And then execute: 26 | 27 | ```sh 28 | bundle install 29 | ``` 30 | 31 | 2. Hook it to your schema: 32 | 33 | ```ruby 34 | class YourAppSchema < GraphQL::Schema 35 | use Yabeda::GraphQL 36 | end 37 | ``` 38 | 39 | 3. And deploy it! 40 | 41 | **And that is it!** GraphQL metrics are being collected! 42 | 43 | ## Metrics 44 | 45 | - **Fields resolved count**: `graphql_fields_request_count` (segmented by `type` name, `field` name and `deprecated` flag) 46 | - **Field resolve runtime**: `graphql_field_resolve_runtime` (segmented by `type` name, `field` name and `deprecated` flag) 47 | - **Queries executed count** (root query fields): `query_fields_count` (segmented by query `name` and `deprecated` flag) 48 | - **Mutations executed** (root mutation fields): `mutation_fields_count` (segmented by query `name` and `deprecated` flag) 49 | 50 | That is it for now, but more will come later. 51 | 52 | ## Development 53 | 54 | After checking out the repo, run `bin/setup` to install dependencies. 55 | 56 | Then, run fololowing commands to run the tests against all supported versions of [GraphQL-Ruby]: 57 | 58 | ```sh 59 | bundle exec appraisal rspec 60 | ``` 61 | 62 | You can also run `bin/console` for an interactive prompt that will allow you to experiment. 63 | 64 | To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). 65 | 66 | ## Contributing 67 | 68 | Bug reports and pull requests are welcome on GitHub at https://github.com/yabeda-rb/yabeda-graphql. 69 | 70 | ### Releasing 71 | 72 | 1. Bump version number in `lib/yabeda/graphql/version.rb` 73 | 74 | In case of pre-releases keep in mind [rubygems/rubygems#3086](https://github.com/rubygems/rubygems/issues/3086) and check version with command like `Gem::Version.new(Yabeda::GraphQL::VERSION).to_s` 75 | 76 | 2. Fill `CHANGELOG.md` with missing changes, add header with version and date. 77 | 78 | 3. Make a commit: 79 | 80 | ```sh 81 | git add lib/yabeda/graphql/version.rb CHANGELOG.md 82 | version=$(ruby -r ./lib/yabeda/graphql/version.rb -e "puts Gem::Version.new(Yabeda::GraphQL::VERSION)") 83 | git commit --message="${version}: " --edit 84 | ``` 85 | 86 | 4. Create annotated tag: 87 | 88 | ```sh 89 | git tag v${version} --annotate --message="${version}: " --edit --sign 90 | ``` 91 | 92 | 5. Fill version name into subject line and (optionally) some description (list of changes will be taken from changelog and appended automatically) 93 | 94 | 6. Push it: 95 | 96 | ```sh 97 | git push --follow-tags 98 | ``` 99 | 100 | 7. GitHub Actions will create a new release, build and push gem into RubyGems! You're done! 101 | 102 | ## License 103 | 104 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 105 | 106 | [yabeda]: https://github.com/yabeda-rb/yabeda "Extendable framework for collecting and exporting metrics from your Ruby application" 107 | [GraphQL-Ruby]: https://graphql-ruby.org/ "Ruby implementation of GraphQL" 108 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task :default => :spec 7 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "yabeda/graphql" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | require "pry" 10 | Pry.start 11 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /gemfiles/.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_RETRY: "1" 3 | -------------------------------------------------------------------------------- /gemfiles/graphql_1.10.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "graphql", "~> 1.10.0" 6 | 7 | group :development, :test do 8 | gem "pry" 9 | gem "pry-inline" 10 | gem "pry-byebug", platform: :mri 11 | gem "graphql-batch" 12 | end 13 | 14 | gemspec path: "../" 15 | -------------------------------------------------------------------------------- /gemfiles/graphql_1.10.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | yabeda-graphql (0.2.3) 5 | graphql (>= 1.9, < 3) 6 | yabeda (~> 0.2) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | anyway_config (2.6.3) 12 | ruby-next-core (~> 1.0) 13 | appraisal (2.5.0) 14 | bundler 15 | rake 16 | thor (>= 0.14.0) 17 | byebug (11.1.3) 18 | coderay (1.1.3) 19 | concurrent-ruby (1.2.3) 20 | diff-lcs (1.5.1) 21 | dry-initializer (3.0.4) 22 | graphql (1.10.14) 23 | graphql-batch (0.5.2) 24 | graphql (>= 1.10, < 3) 25 | promise.rb (~> 0.7.2) 26 | method_source (1.0.0) 27 | promise.rb (0.7.4) 28 | pry (0.14.2) 29 | coderay (~> 1.1) 30 | method_source (~> 1.0) 31 | pry-byebug (3.8.0) 32 | byebug (~> 11.0) 33 | pry (~> 0.10) 34 | pry-inline (1.0.7) 35 | pry (> 0.10.0) 36 | unicode (~> 0.4.4) 37 | rake (13.1.0) 38 | rspec (3.13.0) 39 | rspec-core (~> 3.13.0) 40 | rspec-expectations (~> 3.13.0) 41 | rspec-mocks (~> 3.13.0) 42 | rspec-core (3.13.0) 43 | rspec-support (~> 3.13.0) 44 | rspec-expectations (3.13.0) 45 | diff-lcs (>= 1.2.0, < 2.0) 46 | rspec-support (~> 3.13.0) 47 | rspec-mocks (3.13.0) 48 | diff-lcs (>= 1.2.0, < 2.0) 49 | rspec-support (~> 3.13.0) 50 | rspec-support (3.13.0) 51 | ruby-next-core (1.0.1) 52 | thor (1.2.2) 53 | unicode (0.4.4.4) 54 | yabeda (0.12.0) 55 | anyway_config (>= 1.0, < 3) 56 | concurrent-ruby 57 | dry-initializer 58 | 59 | PLATFORMS 60 | ruby 61 | 62 | DEPENDENCIES 63 | appraisal 64 | bundler 65 | graphql (~> 1.10.0) 66 | graphql-batch 67 | pry 68 | pry-byebug 69 | pry-inline 70 | rake (~> 13.0) 71 | rspec (~> 3.0) 72 | yabeda-graphql! 73 | 74 | BUNDLED WITH 75 | 2.3.10 76 | -------------------------------------------------------------------------------- /gemfiles/graphql_1.11.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "graphql", "~> 1.11.0" 6 | 7 | group :development, :test do 8 | gem "pry" 9 | gem "pry-inline" 10 | gem "pry-byebug", platform: :mri 11 | gem "graphql-batch" 12 | end 13 | 14 | gemspec path: "../" 15 | -------------------------------------------------------------------------------- /gemfiles/graphql_1.11.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | yabeda-graphql (0.2.3) 5 | graphql (>= 1.9, < 3) 6 | yabeda (~> 0.2) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | anyway_config (2.6.3) 12 | ruby-next-core (~> 1.0) 13 | appraisal (2.5.0) 14 | bundler 15 | rake 16 | thor (>= 0.14.0) 17 | byebug (11.1.3) 18 | coderay (1.1.3) 19 | concurrent-ruby (1.2.3) 20 | diff-lcs (1.5.1) 21 | dry-initializer (3.0.4) 22 | graphql (1.11.10) 23 | graphql-batch (0.5.2) 24 | graphql (>= 1.10, < 3) 25 | promise.rb (~> 0.7.2) 26 | method_source (1.0.0) 27 | promise.rb (0.7.4) 28 | pry (0.14.2) 29 | coderay (~> 1.1) 30 | method_source (~> 1.0) 31 | pry-byebug (3.8.0) 32 | byebug (~> 11.0) 33 | pry (~> 0.10) 34 | pry-inline (1.0.7) 35 | pry (> 0.10.0) 36 | unicode (~> 0.4.4) 37 | rake (13.1.0) 38 | rspec (3.13.0) 39 | rspec-core (~> 3.13.0) 40 | rspec-expectations (~> 3.13.0) 41 | rspec-mocks (~> 3.13.0) 42 | rspec-core (3.13.0) 43 | rspec-support (~> 3.13.0) 44 | rspec-expectations (3.13.0) 45 | diff-lcs (>= 1.2.0, < 2.0) 46 | rspec-support (~> 3.13.0) 47 | rspec-mocks (3.13.0) 48 | diff-lcs (>= 1.2.0, < 2.0) 49 | rspec-support (~> 3.13.0) 50 | rspec-support (3.13.0) 51 | ruby-next-core (1.0.1) 52 | thor (1.3.0) 53 | unicode (0.4.4.4) 54 | yabeda (0.12.0) 55 | anyway_config (>= 1.0, < 3) 56 | concurrent-ruby 57 | dry-initializer 58 | 59 | PLATFORMS 60 | ruby 61 | 62 | DEPENDENCIES 63 | appraisal 64 | bundler 65 | graphql (~> 1.11.0) 66 | graphql-batch 67 | pry 68 | pry-byebug 69 | pry-inline 70 | rake (~> 13.0) 71 | rspec (~> 3.0) 72 | yabeda-graphql! 73 | 74 | BUNDLED WITH 75 | 2.4.22 76 | -------------------------------------------------------------------------------- /gemfiles/graphql_1.12.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "graphql", "~> 1.12.0" 6 | 7 | group :development, :test do 8 | gem "pry" 9 | gem "pry-inline" 10 | gem "pry-byebug", platform: :mri 11 | gem "graphql-batch" 12 | end 13 | 14 | gemspec path: "../" 15 | -------------------------------------------------------------------------------- /gemfiles/graphql_1.12.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | yabeda-graphql (0.2.3) 5 | graphql (>= 1.9, < 3) 6 | yabeda (~> 0.2) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | anyway_config (2.6.3) 12 | ruby-next-core (~> 1.0) 13 | appraisal (2.5.0) 14 | bundler 15 | rake 16 | thor (>= 0.14.0) 17 | byebug (11.1.3) 18 | coderay (1.1.3) 19 | concurrent-ruby (1.2.3) 20 | diff-lcs (1.5.1) 21 | dry-initializer (3.1.1) 22 | graphql (1.12.24) 23 | graphql-batch (0.5.4) 24 | graphql (>= 1.12.18, < 3) 25 | promise.rb (~> 0.7.2) 26 | method_source (1.0.0) 27 | promise.rb (0.7.4) 28 | pry (0.14.2) 29 | coderay (~> 1.1) 30 | method_source (~> 1.0) 31 | pry-byebug (3.10.1) 32 | byebug (~> 11.0) 33 | pry (>= 0.13, < 0.15) 34 | pry-inline (1.0.7) 35 | pry (> 0.10.0) 36 | unicode (~> 0.4.4) 37 | rake (13.1.0) 38 | rspec (3.13.0) 39 | rspec-core (~> 3.13.0) 40 | rspec-expectations (~> 3.13.0) 41 | rspec-mocks (~> 3.13.0) 42 | rspec-core (3.13.0) 43 | rspec-support (~> 3.13.0) 44 | rspec-expectations (3.13.0) 45 | diff-lcs (>= 1.2.0, < 2.0) 46 | rspec-support (~> 3.13.0) 47 | rspec-mocks (3.13.0) 48 | diff-lcs (>= 1.2.0, < 2.0) 49 | rspec-support (~> 3.13.0) 50 | rspec-support (3.13.0) 51 | ruby-next-core (1.0.1) 52 | thor (1.3.0) 53 | unicode (0.4.4.4) 54 | yabeda (0.12.0) 55 | anyway_config (>= 1.0, < 3) 56 | concurrent-ruby 57 | dry-initializer 58 | 59 | PLATFORMS 60 | ruby 61 | 62 | DEPENDENCIES 63 | appraisal 64 | bundler 65 | graphql (~> 1.12.0) 66 | graphql-batch 67 | pry 68 | pry-byebug 69 | pry-inline 70 | rake (~> 13.0) 71 | rspec (~> 3.0) 72 | yabeda-graphql! 73 | 74 | BUNDLED WITH 75 | 2.4.22 76 | -------------------------------------------------------------------------------- /gemfiles/graphql_1.13.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "graphql", "~> 1.13.0" 6 | 7 | group :development, :test do 8 | gem "pry" 9 | gem "pry-inline" 10 | gem "pry-byebug", platform: :mri 11 | gem "graphql-batch" 12 | end 13 | 14 | gemspec path: "../" 15 | -------------------------------------------------------------------------------- /gemfiles/graphql_1.13.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | yabeda-graphql (0.2.3) 5 | graphql (>= 1.9, < 3) 6 | yabeda (~> 0.2) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | anyway_config (2.6.3) 12 | ruby-next-core (~> 1.0) 13 | appraisal (2.5.0) 14 | bundler 15 | rake 16 | thor (>= 0.14.0) 17 | byebug (11.1.3) 18 | coderay (1.1.3) 19 | concurrent-ruby (1.2.3) 20 | diff-lcs (1.5.1) 21 | dry-initializer (3.1.1) 22 | graphql (1.13.21) 23 | graphql-batch (0.5.4) 24 | graphql (>= 1.12.18, < 3) 25 | promise.rb (~> 0.7.2) 26 | method_source (1.0.0) 27 | promise.rb (0.7.4) 28 | pry (0.14.2) 29 | coderay (~> 1.1) 30 | method_source (~> 1.0) 31 | pry-byebug (3.10.1) 32 | byebug (~> 11.0) 33 | pry (>= 0.13, < 0.15) 34 | pry-inline (1.0.7) 35 | pry (> 0.10.0) 36 | unicode (~> 0.4.4) 37 | rake (13.1.0) 38 | rspec (3.13.0) 39 | rspec-core (~> 3.13.0) 40 | rspec-expectations (~> 3.13.0) 41 | rspec-mocks (~> 3.13.0) 42 | rspec-core (3.13.0) 43 | rspec-support (~> 3.13.0) 44 | rspec-expectations (3.13.0) 45 | diff-lcs (>= 1.2.0, < 2.0) 46 | rspec-support (~> 3.13.0) 47 | rspec-mocks (3.13.0) 48 | diff-lcs (>= 1.2.0, < 2.0) 49 | rspec-support (~> 3.13.0) 50 | rspec-support (3.13.0) 51 | ruby-next-core (1.0.1) 52 | thor (1.3.0) 53 | unicode (0.4.4.4) 54 | yabeda (0.12.0) 55 | anyway_config (>= 1.0, < 3) 56 | concurrent-ruby 57 | dry-initializer 58 | 59 | PLATFORMS 60 | x86_64-linux 61 | 62 | DEPENDENCIES 63 | appraisal 64 | bundler 65 | graphql (~> 1.13.0) 66 | graphql-batch 67 | pry 68 | pry-byebug 69 | pry-inline 70 | rake (~> 13.0) 71 | rspec (~> 3.0) 72 | yabeda-graphql! 73 | 74 | BUNDLED WITH 75 | 2.5.6 76 | -------------------------------------------------------------------------------- /gemfiles/graphql_2.0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "graphql", "~> 2.0.0" 6 | 7 | group :development, :test do 8 | gem "pry" 9 | gem "pry-inline" 10 | gem "pry-byebug", platform: :mri 11 | gem "graphql-batch" 12 | end 13 | 14 | gemspec path: "../" 15 | -------------------------------------------------------------------------------- /gemfiles/graphql_2.0.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | yabeda-graphql (0.2.3) 5 | graphql (>= 1.9, < 3) 6 | yabeda (~> 0.2) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | anyway_config (2.6.3) 12 | ruby-next-core (~> 1.0) 13 | appraisal (2.5.0) 14 | bundler 15 | rake 16 | thor (>= 0.14.0) 17 | byebug (11.1.3) 18 | coderay (1.1.3) 19 | concurrent-ruby (1.2.3) 20 | diff-lcs (1.5.1) 21 | dry-initializer (3.1.1) 22 | graphql (2.0.28) 23 | graphql-batch (0.5.4) 24 | graphql (>= 1.12.18, < 3) 25 | promise.rb (~> 0.7.2) 26 | method_source (1.0.0) 27 | promise.rb (0.7.4) 28 | pry (0.14.2) 29 | coderay (~> 1.1) 30 | method_source (~> 1.0) 31 | pry-byebug (3.10.1) 32 | byebug (~> 11.0) 33 | pry (>= 0.13, < 0.15) 34 | pry-inline (1.0.7) 35 | pry (> 0.10.0) 36 | unicode (~> 0.4.4) 37 | rake (13.1.0) 38 | rspec (3.13.0) 39 | rspec-core (~> 3.13.0) 40 | rspec-expectations (~> 3.13.0) 41 | rspec-mocks (~> 3.13.0) 42 | rspec-core (3.13.0) 43 | rspec-support (~> 3.13.0) 44 | rspec-expectations (3.13.0) 45 | diff-lcs (>= 1.2.0, < 2.0) 46 | rspec-support (~> 3.13.0) 47 | rspec-mocks (3.13.0) 48 | diff-lcs (>= 1.2.0, < 2.0) 49 | rspec-support (~> 3.13.0) 50 | rspec-support (3.13.0) 51 | ruby-next-core (1.0.1) 52 | thor (1.3.0) 53 | unicode (0.4.4.4) 54 | yabeda (0.12.0) 55 | anyway_config (>= 1.0, < 3) 56 | concurrent-ruby 57 | dry-initializer 58 | 59 | PLATFORMS 60 | x86_64-linux 61 | 62 | DEPENDENCIES 63 | appraisal 64 | bundler 65 | graphql (~> 2.0.0) 66 | graphql-batch 67 | pry 68 | pry-byebug 69 | pry-inline 70 | rake (~> 13.0) 71 | rspec (~> 3.0) 72 | yabeda-graphql! 73 | 74 | BUNDLED WITH 75 | 2.5.6 76 | -------------------------------------------------------------------------------- /gemfiles/graphql_2.1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "graphql", "~> 2.1.0" 6 | 7 | group :development, :test do 8 | gem "pry" 9 | gem "pry-inline" 10 | gem "pry-byebug", platform: :mri 11 | gem "graphql-batch" 12 | end 13 | 14 | gemspec path: "../" 15 | -------------------------------------------------------------------------------- /gemfiles/graphql_2.1.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | yabeda-graphql (0.2.3) 5 | graphql (>= 1.9, < 3) 6 | yabeda (~> 0.2) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | anyway_config (2.6.3) 12 | ruby-next-core (~> 1.0) 13 | appraisal (2.5.0) 14 | bundler 15 | rake 16 | thor (>= 0.14.0) 17 | byebug (11.1.3) 18 | coderay (1.1.3) 19 | concurrent-ruby (1.2.3) 20 | diff-lcs (1.5.1) 21 | dry-initializer (3.1.1) 22 | graphql (2.1.11) 23 | racc (~> 1.4) 24 | graphql-batch (0.5.4) 25 | graphql (>= 1.12.18, < 3) 26 | promise.rb (~> 0.7.2) 27 | method_source (1.0.0) 28 | promise.rb (0.7.4) 29 | pry (0.14.2) 30 | coderay (~> 1.1) 31 | method_source (~> 1.0) 32 | pry-byebug (3.10.1) 33 | byebug (~> 11.0) 34 | pry (>= 0.13, < 0.15) 35 | pry-inline (1.0.7) 36 | pry (> 0.10.0) 37 | unicode (~> 0.4.4) 38 | racc (1.7.3) 39 | rake (13.1.0) 40 | rspec (3.13.0) 41 | rspec-core (~> 3.13.0) 42 | rspec-expectations (~> 3.13.0) 43 | rspec-mocks (~> 3.13.0) 44 | rspec-core (3.13.0) 45 | rspec-support (~> 3.13.0) 46 | rspec-expectations (3.13.0) 47 | diff-lcs (>= 1.2.0, < 2.0) 48 | rspec-support (~> 3.13.0) 49 | rspec-mocks (3.13.0) 50 | diff-lcs (>= 1.2.0, < 2.0) 51 | rspec-support (~> 3.13.0) 52 | rspec-support (3.13.0) 53 | ruby-next-core (1.0.1) 54 | thor (1.3.0) 55 | unicode (0.4.4.4) 56 | yabeda (0.12.0) 57 | anyway_config (>= 1.0, < 3) 58 | concurrent-ruby 59 | dry-initializer 60 | 61 | PLATFORMS 62 | ruby 63 | x86_64-linux 64 | 65 | DEPENDENCIES 66 | appraisal 67 | bundler 68 | graphql (~> 2.1.0) 69 | graphql-batch 70 | pry 71 | pry-byebug 72 | pry-inline 73 | rake (~> 13.0) 74 | rspec (~> 3.0) 75 | yabeda-graphql! 76 | 77 | BUNDLED WITH 78 | 2.5.6 79 | -------------------------------------------------------------------------------- /gemfiles/graphql_2.2.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "graphql", "~> 2.2.0" 6 | 7 | group :development, :test do 8 | gem "pry" 9 | gem "pry-inline" 10 | gem "pry-byebug", platform: :mri 11 | gem "graphql-batch" 12 | end 13 | 14 | gemspec path: "../" 15 | -------------------------------------------------------------------------------- /gemfiles/graphql_2.2.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | yabeda-graphql (0.2.3) 5 | graphql (>= 1.9, < 3) 6 | yabeda (~> 0.2) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | anyway_config (2.6.3) 12 | ruby-next-core (~> 1.0) 13 | appraisal (2.5.0) 14 | bundler 15 | rake 16 | thor (>= 0.14.0) 17 | byebug (11.1.3) 18 | coderay (1.1.3) 19 | concurrent-ruby (1.2.3) 20 | diff-lcs (1.5.1) 21 | dry-initializer (3.1.1) 22 | graphql (2.2.8) 23 | graphql-batch (0.5.4) 24 | graphql (>= 1.12.18, < 3) 25 | promise.rb (~> 0.7.2) 26 | method_source (1.0.0) 27 | promise.rb (0.7.4) 28 | pry (0.14.2) 29 | coderay (~> 1.1) 30 | method_source (~> 1.0) 31 | pry-byebug (3.10.1) 32 | byebug (~> 11.0) 33 | pry (>= 0.13, < 0.15) 34 | pry-inline (1.0.7) 35 | pry (> 0.10.0) 36 | unicode (~> 0.4.4) 37 | rake (13.1.0) 38 | rspec (3.13.0) 39 | rspec-core (~> 3.13.0) 40 | rspec-expectations (~> 3.13.0) 41 | rspec-mocks (~> 3.13.0) 42 | rspec-core (3.13.0) 43 | rspec-support (~> 3.13.0) 44 | rspec-expectations (3.13.0) 45 | diff-lcs (>= 1.2.0, < 2.0) 46 | rspec-support (~> 3.13.0) 47 | rspec-mocks (3.13.0) 48 | diff-lcs (>= 1.2.0, < 2.0) 49 | rspec-support (~> 3.13.0) 50 | rspec-support (3.13.0) 51 | ruby-next-core (1.0.1) 52 | thor (1.3.0) 53 | unicode (0.4.4.4) 54 | yabeda (0.12.0) 55 | anyway_config (>= 1.0, < 3) 56 | concurrent-ruby 57 | dry-initializer 58 | 59 | PLATFORMS 60 | ruby 61 | x86_64-linux 62 | 63 | DEPENDENCIES 64 | appraisal 65 | bundler 66 | graphql (~> 2.2.0) 67 | graphql-batch 68 | pry 69 | pry-byebug 70 | pry-inline 71 | rake (~> 13.0) 72 | rspec (~> 3.0) 73 | yabeda-graphql! 74 | 75 | BUNDLED WITH 76 | 2.5.6 77 | -------------------------------------------------------------------------------- /grafana-dashboard.json: -------------------------------------------------------------------------------- 1 | { 2 | "__inputs": [ 3 | { 4 | "name": "DS_PROMETHEUS", 5 | "label": "Prometheus", 6 | "description": "", 7 | "type": "datasource", 8 | "pluginId": "prometheus", 9 | "pluginName": "Prometheus" 10 | } 11 | ], 12 | "__requires": [ 13 | { 14 | "type": "grafana", 15 | "id": "grafana", 16 | "name": "Grafana", 17 | "version": "6.6.0" 18 | }, 19 | { 20 | "type": "panel", 21 | "id": "graph", 22 | "name": "Graph", 23 | "version": "" 24 | }, 25 | { 26 | "type": "datasource", 27 | "id": "prometheus", 28 | "name": "Prometheus", 29 | "version": "1.0.0" 30 | } 31 | ], 32 | "annotations": { 33 | "list": [ 34 | { 35 | "builtIn": 1, 36 | "datasource": "-- Grafana --", 37 | "enable": true, 38 | "hide": true, 39 | "iconColor": "rgba(0, 211, 255, 1)", 40 | "name": "Annotations & Alerts", 41 | "type": "dashboard" 42 | } 43 | ] 44 | }, 45 | "description": "Example dashboard for metrics provided by yabeda-graphql gem for monitoring of APIs powered by GraphQL-Ruby.", 46 | "editable": true, 47 | "gnetId": null, 48 | "graphTooltip": 0, 49 | "id": null, 50 | "links": [ 51 | { 52 | "asDropdown": false, 53 | "icon": "doc", 54 | "includeVars": false, 55 | "keepTime": false, 56 | "tags": [], 57 | "targetBlank": true, 58 | "title": "yabeda-graphql", 59 | "tooltip": "Open yabeda-graphql page on GitHub", 60 | "type": "link", 61 | "url": "https://github.com/yabeda-rb/yabeda-graphql/" 62 | } 63 | ], 64 | "panels": [ 65 | { 66 | "aliasColors": {}, 67 | "bars": false, 68 | "dashLength": 10, 69 | "dashes": false, 70 | "datasource": null, 71 | "fieldConfig": { 72 | "defaults": {}, 73 | "overrides": [] 74 | }, 75 | "fill": 1, 76 | "fillGradient": 0, 77 | "gridPos": { 78 | "h": 9, 79 | "w": 24, 80 | "x": 0, 81 | "y": 0 82 | }, 83 | "hiddenSeries": false, 84 | "id": 2, 85 | "legend": { 86 | "alignAsTable": true, 87 | "avg": false, 88 | "current": true, 89 | "hideEmpty": true, 90 | "hideZero": true, 91 | "max": true, 92 | "min": false, 93 | "rightSide": true, 94 | "show": true, 95 | "sort": "max", 96 | "sortDesc": true, 97 | "total": false, 98 | "values": true 99 | }, 100 | "lines": true, 101 | "linewidth": 1, 102 | "nullPointMode": "null", 103 | "options": { 104 | "alertThreshold": true 105 | }, 106 | "percentage": false, 107 | "pluginVersion": "7.5.3", 108 | "pointradius": 2, 109 | "points": false, 110 | "renderer": "flot", 111 | "seriesOverrides": [], 112 | "spaceLength": 10, 113 | "stack": false, 114 | "steppedLine": false, 115 | "targets": [ 116 | { 117 | "exemplar": true, 118 | "expr": "histogram_quantile(0.95, sum(rate(graphql_field_resolve_runtime_seconds_bucket[1m])) by (type,field,le))", 119 | "interval": "", 120 | "legendFormat": "{{type}}.{{field}}", 121 | "queryType": "randomWalk", 122 | "refId": "A" 123 | } 124 | ], 125 | "thresholds": [], 126 | "timeFrom": null, 127 | "timeRegions": [], 128 | "timeShift": null, 129 | "title": "Field resolve time (95th percentile)", 130 | "tooltip": { 131 | "shared": true, 132 | "sort": 2, 133 | "value_type": "individual" 134 | }, 135 | "type": "graph", 136 | "xaxis": { 137 | "buckets": null, 138 | "mode": "time", 139 | "name": null, 140 | "show": true, 141 | "values": [] 142 | }, 143 | "yaxes": [ 144 | { 145 | "format": "s", 146 | "label": null, 147 | "logBase": 1, 148 | "max": null, 149 | "min": null, 150 | "show": true 151 | }, 152 | { 153 | "format": "short", 154 | "label": null, 155 | "logBase": 1, 156 | "max": null, 157 | "min": null, 158 | "show": false 159 | } 160 | ], 161 | "yaxis": { 162 | "align": false, 163 | "alignLevel": null 164 | } 165 | }, 166 | { 167 | "aliasColors": {}, 168 | "bars": false, 169 | "dashLength": 10, 170 | "dashes": false, 171 | "datasource": null, 172 | "fieldConfig": { 173 | "defaults": {}, 174 | "overrides": [] 175 | }, 176 | "fill": 1, 177 | "fillGradient": 0, 178 | "gridPos": { 179 | "h": 8, 180 | "w": 9, 181 | "x": 0, 182 | "y": 9 183 | }, 184 | "hiddenSeries": false, 185 | "id": 6, 186 | "legend": { 187 | "alignAsTable": true, 188 | "avg": false, 189 | "current": true, 190 | "hideEmpty": true, 191 | "max": true, 192 | "min": false, 193 | "rightSide": true, 194 | "show": true, 195 | "sort": "current", 196 | "sortDesc": true, 197 | "total": false, 198 | "values": true 199 | }, 200 | "lines": true, 201 | "linewidth": 1, 202 | "nullPointMode": "null", 203 | "options": { 204 | "alertThreshold": true 205 | }, 206 | "percentage": false, 207 | "pluginVersion": "7.5.3", 208 | "pointradius": 2, 209 | "points": false, 210 | "renderer": "flot", 211 | "seriesOverrides": [], 212 | "spaceLength": 10, 213 | "stack": false, 214 | "steppedLine": false, 215 | "targets": [ 216 | { 217 | "exemplar": true, 218 | "expr": "sum(rate(graphql_query_fields_count[1m])) by (name)", 219 | "interval": "", 220 | "legendFormat": "{{name}}", 221 | "refId": "A" 222 | } 223 | ], 224 | "thresholds": [], 225 | "timeFrom": null, 226 | "timeRegions": [], 227 | "timeShift": null, 228 | "title": "Queries throughput", 229 | "tooltip": { 230 | "shared": true, 231 | "sort": 2, 232 | "value_type": "individual" 233 | }, 234 | "type": "graph", 235 | "xaxis": { 236 | "buckets": null, 237 | "mode": "time", 238 | "name": null, 239 | "show": true, 240 | "values": [] 241 | }, 242 | "yaxes": [ 243 | { 244 | "decimals": 0, 245 | "format": "reqps", 246 | "label": null, 247 | "logBase": 1, 248 | "max": null, 249 | "min": "0", 250 | "show": true 251 | }, 252 | { 253 | "format": "short", 254 | "label": null, 255 | "logBase": 1, 256 | "max": null, 257 | "min": null, 258 | "show": false 259 | } 260 | ], 261 | "yaxis": { 262 | "align": false, 263 | "alignLevel": null 264 | } 265 | }, 266 | { 267 | "aliasColors": {}, 268 | "bars": false, 269 | "dashLength": 10, 270 | "dashes": false, 271 | "datasource": null, 272 | "fieldConfig": { 273 | "defaults": {}, 274 | "overrides": [] 275 | }, 276 | "fill": 1, 277 | "fillGradient": 0, 278 | "gridPos": { 279 | "h": 8, 280 | "w": 9, 281 | "x": 9, 282 | "y": 9 283 | }, 284 | "hiddenSeries": false, 285 | "id": 8, 286 | "legend": { 287 | "alignAsTable": true, 288 | "avg": false, 289 | "current": true, 290 | "hideEmpty": true, 291 | "max": true, 292 | "min": false, 293 | "rightSide": true, 294 | "show": true, 295 | "sort": "current", 296 | "sortDesc": true, 297 | "total": false, 298 | "values": true 299 | }, 300 | "lines": true, 301 | "linewidth": 1, 302 | "nullPointMode": "null", 303 | "options": { 304 | "alertThreshold": true 305 | }, 306 | "percentage": false, 307 | "pluginVersion": "7.5.3", 308 | "pointradius": 2, 309 | "points": false, 310 | "renderer": "flot", 311 | "seriesOverrides": [], 312 | "spaceLength": 10, 313 | "stack": false, 314 | "steppedLine": false, 315 | "targets": [ 316 | { 317 | "exemplar": true, 318 | "expr": "sum(rate(graphql_mutation_fields_count[1m])) by (name)", 319 | "interval": "", 320 | "legendFormat": "{{name}}", 321 | "refId": "A" 322 | } 323 | ], 324 | "thresholds": [], 325 | "timeFrom": null, 326 | "timeRegions": [], 327 | "timeShift": null, 328 | "title": "Mutations throughput", 329 | "tooltip": { 330 | "shared": true, 331 | "sort": 2, 332 | "value_type": "individual" 333 | }, 334 | "type": "graph", 335 | "xaxis": { 336 | "buckets": null, 337 | "mode": "time", 338 | "name": null, 339 | "show": true, 340 | "values": [] 341 | }, 342 | "yaxes": [ 343 | { 344 | "decimals": 0, 345 | "format": "reqps", 346 | "label": null, 347 | "logBase": 1, 348 | "max": null, 349 | "min": "0", 350 | "show": true 351 | }, 352 | { 353 | "format": "short", 354 | "label": null, 355 | "logBase": 1, 356 | "max": null, 357 | "min": null, 358 | "show": false 359 | } 360 | ], 361 | "yaxis": { 362 | "align": false, 363 | "alignLevel": null 364 | } 365 | }, 366 | { 367 | "aliasColors": {}, 368 | "bars": false, 369 | "dashLength": 10, 370 | "dashes": false, 371 | "datasource": null, 372 | "decimals": 0, 373 | "fieldConfig": { 374 | "defaults": {}, 375 | "overrides": [] 376 | }, 377 | "fill": 1, 378 | "fillGradient": 0, 379 | "gridPos": { 380 | "h": 8, 381 | "w": 6, 382 | "x": 18, 383 | "y": 9 384 | }, 385 | "hiddenSeries": false, 386 | "id": 4, 387 | "legend": { 388 | "alignAsTable": true, 389 | "avg": false, 390 | "current": false, 391 | "hideEmpty": true, 392 | "hideZero": false, 393 | "max": false, 394 | "min": false, 395 | "rightSide": true, 396 | "show": true, 397 | "total": false, 398 | "values": false 399 | }, 400 | "lines": true, 401 | "linewidth": 1, 402 | "nullPointMode": "null", 403 | "options": { 404 | "alertThreshold": true 405 | }, 406 | "percentage": false, 407 | "pluginVersion": "7.5.3", 408 | "pointradius": 2, 409 | "points": false, 410 | "renderer": "flot", 411 | "seriesOverrides": [], 412 | "spaceLength": 10, 413 | "stack": false, 414 | "steppedLine": false, 415 | "targets": [ 416 | { 417 | "exemplar": true, 418 | "expr": "sum(rate(graphql_fields_request_count{deprecated=\"true\"}[1m])) by (type, field)", 419 | "interval": "", 420 | "legendFormat": "{{type}}.{{field}}", 421 | "refId": "A" 422 | } 423 | ], 424 | "thresholds": [], 425 | "timeFrom": null, 426 | "timeRegions": [], 427 | "timeShift": null, 428 | "title": "Deprecated fields usage", 429 | "tooltip": { 430 | "shared": true, 431 | "sort": 0, 432 | "value_type": "individual" 433 | }, 434 | "type": "graph", 435 | "xaxis": { 436 | "buckets": null, 437 | "mode": "time", 438 | "name": null, 439 | "show": true, 440 | "values": [] 441 | }, 442 | "yaxes": [ 443 | { 444 | "decimals": 0, 445 | "format": "reqps", 446 | "label": null, 447 | "logBase": 1, 448 | "max": null, 449 | "min": "0", 450 | "show": true 451 | }, 452 | { 453 | "format": "short", 454 | "label": null, 455 | "logBase": 1, 456 | "max": null, 457 | "min": null, 458 | "show": false 459 | } 460 | ], 461 | "yaxis": { 462 | "align": false, 463 | "alignLevel": null 464 | } 465 | } 466 | ], 467 | "refresh": "10s", 468 | "schemaVersion": 27, 469 | "style": "dark", 470 | "tags": [], 471 | "templating": { 472 | "list": [] 473 | }, 474 | "time": { 475 | "from": "now-6h", 476 | "to": "now" 477 | }, 478 | "timepicker": {}, 479 | "timezone": "", 480 | "title": "GraphQL API", 481 | "uid": "yabeda-graphql", 482 | "version": 1 483 | } 484 | -------------------------------------------------------------------------------- /lib/yabeda/graphql.rb: -------------------------------------------------------------------------------- 1 | require "yabeda" 2 | require "yabeda/graphql/version" 3 | require "yabeda/graphql/yabeda_tracing" 4 | require "yabeda/graphql/instrumentation" 5 | 6 | module Yabeda 7 | module GraphQL 8 | class Error < StandardError; end 9 | 10 | REQUEST_BUCKETS = [ 11 | 0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 12 | ].freeze 13 | 14 | Yabeda.configure do 15 | group :graphql 16 | 17 | histogram :field_resolve_runtime, comment: "A histogram of field resolving time", 18 | unit: :seconds, per: :field, 19 | tags: %i[type field deprecated], 20 | buckets: REQUEST_BUCKETS 21 | 22 | counter :fields_request_count, comment: "A counter for specific fields requests", 23 | tags: %i[type field deprecated] 24 | 25 | counter :query_fields_count, comment: "A counter for query root fields", 26 | tags: %i[name deprecated] 27 | 28 | counter :mutation_fields_count, comment: "A counter for mutation root fields", 29 | tags: %i[name deprecated] 30 | end 31 | 32 | def self.use(schema) 33 | schema.instrument(:query, Instrumentation.new) 34 | schema.use YabedaTracing, trace_scalars: true 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/yabeda/graphql/instrumentation.rb: -------------------------------------------------------------------------------- 1 | module Yabeda 2 | module GraphQL 3 | class Instrumentation 4 | def before_query(query) 5 | reset_cache!(query) 6 | end 7 | 8 | def after_query(query) 9 | cache(query).each do |_path, options| 10 | Yabeda.graphql.field_resolve_runtime.measure(options[:tags], options[:duration]) 11 | Yabeda.graphql.fields_request_count.increment(options[:tags]) 12 | end 13 | end 14 | 15 | private 16 | 17 | def cache(query) 18 | query.context.namespace(Yabeda::GraphQL)[:field_call_cache] 19 | end 20 | 21 | def reset_cache!(query) 22 | query.context.namespace(Yabeda::GraphQL)[:field_call_cache] = 23 | Hash.new { |h,k| h[k] = { tags: {}, duration: 0.0 } } 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/yabeda/graphql/version.rb: -------------------------------------------------------------------------------- 1 | module Yabeda 2 | module GraphQL 3 | VERSION = "0.2.3" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/yabeda/graphql/yabeda_tracing.rb: -------------------------------------------------------------------------------- 1 | require "graphql/tracing/platform_tracing" 2 | 3 | module Yabeda 4 | module GraphQL 5 | class YabedaTracing < ::GraphQL::Tracing::PlatformTracing 6 | 7 | self.platform_keys = { 8 | 'lex' => "graphql.lex", 9 | 'parse' => "graphql.parse", 10 | 'validate' => "graphql.validate", 11 | 'analyze_query' => "graphql.analyze", 12 | 'analyze_multiplex' => "graphql.analyze", 13 | 'execute_multiplex' => "graphql.execute", 14 | 'execute_query' => "graphql.execute", 15 | 'execute_query_lazy' => "graphql.execute", 16 | 'execute_field' => "graphql.execute", 17 | 'execute_field_lazy' => "graphql.execute" 18 | } 19 | 20 | def platform_trace(platform_key, key, data, &block) 21 | start = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC 22 | result = block.call 23 | duration = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start 24 | 25 | case key 26 | when "execute_field", "execute_field_lazy" 27 | field, path, query = extract_field_trace_data(data) 28 | 29 | tags = extract_field_tags(field) 30 | if path.length == 1 31 | return result if key == "execute_field" && query.schema.lazy?(result) 32 | 33 | if query.query? 34 | instrument_query_execution(tags) 35 | elsif query.mutation? 36 | instrument_mutation_execution(tags) 37 | elsif query.subscription? 38 | # Not implemented yet 39 | end 40 | else 41 | instrument_field_execution(query, path, tags, duration) 42 | end 43 | end 44 | 45 | result 46 | end 47 | 48 | # See https://graphql-ruby.org/api-doc/1.10.5/GraphQL/Tracing 49 | def extract_field_trace_data(data) 50 | if data[:context] # Legacy non-interpreter mode 51 | [data[:context].field, data[:context].path, data[:context].query] 52 | else # Interpreter mode 53 | data.values_at(:field, :path, :query) 54 | end 55 | end 56 | 57 | def extract_field_tags(field) 58 | owner = field.respond_to?(:owner) ? field.owner : field.metadata[:type_class].owner 59 | { 60 | type: owner.graphql_name, 61 | field: field.graphql_name, 62 | deprecated: !field.deprecation_reason.nil?, 63 | } 64 | end 65 | 66 | def instrument_field_execution(query, path, tags, duration) 67 | cache(query)[path][:tags] = tags 68 | cache(query)[path][:duration] += duration 69 | end 70 | 71 | def instrument_mutation_execution(tags) 72 | tags = { name: tags[:field], deprecated: tags[:deprecated] } 73 | Yabeda.graphql.mutation_fields_count.increment(tags) 74 | end 75 | 76 | def instrument_query_execution(tags) 77 | tags = { name: tags[:field], deprecated: tags[:deprecated] } 78 | Yabeda.graphql.query_fields_count.increment(tags) 79 | end 80 | 81 | def cache(query) 82 | query.context.namespace(Yabeda::GraphQL)[:field_call_cache] 83 | end 84 | 85 | def platform_field_key(type, field) 86 | "#{type.graphql_name}.#{field.graphql_name}" 87 | end 88 | 89 | # We don't use these yet, but graphql-ruby require us to declare them 90 | 91 | def platform_authorized_key(type) 92 | "#{type.graphql_name}.authorized" 93 | end 94 | 95 | def platform_resolve_type_key(type) 96 | "#{type.graphql_name}.resolve_type" 97 | end 98 | end 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require "bundler/setup" 2 | require "yabeda/graphql" 3 | require "pry" 4 | 5 | require_relative "support/graphql_schema" 6 | 7 | RSpec.configure do |config| 8 | # Enable flags like --only-failures and --next-failure 9 | config.example_status_persistence_file_path = ".rspec_status" 10 | 11 | # Disable RSpec exposing methods globally on `Module` and `main` 12 | config.disable_monkey_patching! 13 | 14 | config.expect_with :rspec do |c| 15 | c.syntax = :expect 16 | end 17 | 18 | config.mock_with :rspec 19 | 20 | Kernel.srand config.seed 21 | config.order = :random 22 | 23 | config.before(:all) do 24 | Yabeda.configure! 25 | end 26 | end 27 | 28 | # Add sum of all observed values to histograms to check in tests 29 | module SummingHistogram 30 | def measure(tags, value) 31 | all_tags = ::Yabeda::Tags.build(tags) 32 | sums[all_tags] += value 33 | super 34 | end 35 | 36 | def sums 37 | @sums ||= Concurrent::Hash.new { |h, k| h[k] = 0.0 } 38 | end 39 | end 40 | 41 | Yabeda::Histogram.prepend(SummingHistogram) 42 | -------------------------------------------------------------------------------- /spec/support/graphql_schema.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "graphql" 3 | require "graphql/batch" 4 | 5 | class PriceLoader < GraphQL::Batch::Loader 6 | def perform(ids) 7 | sleep 0.01 8 | ids.each { |id| fulfill(id, { currency: "USD", amount: 1.0 }) } 9 | end 10 | end 11 | 12 | class PriceType < GraphQL::Schema::Object 13 | field :currency, String, null: false, hash_key: :currency 14 | field :amount, Float, null: false, hash_key: :amount 15 | end 16 | 17 | class ProductType < GraphQL::Schema::Object 18 | field :id, ID, null: false 19 | field :title, String, null: true 20 | field :shmitle, String, null: true, method: :title, deprecation_reason: "Just shit" 21 | field :price, PriceType, null: true 22 | 23 | def price 24 | PriceLoader.load(object.id) 25 | end 26 | end 27 | 28 | class UserType < GraphQL::Schema::Object 29 | field :id, ID, null: false 30 | field :name, String, null: true 31 | end 32 | 33 | class Product < OpenStruct 34 | def title 35 | sleep 0.001 36 | super 37 | end 38 | end 39 | 40 | class User < OpenStruct 41 | def name 42 | super 43 | end 44 | end 45 | 46 | class QueryType < GraphQL::Schema::Object 47 | field :products, [ProductType], null: false 48 | field :users, [UserType], null: false 49 | 50 | def products 51 | [ 52 | Product.new(id: 1, title: "Foo", price: { currency: "USD", amount: 1.0 }), 53 | Product.new(id: 2, title: "Foo", price: { currency: "RUB", amount: 75.0 }), 54 | ] 55 | end 56 | 57 | def users 58 | [User.new(id: 1, name: "Andrey")] 59 | end 60 | end 61 | 62 | class CreateProduct < GraphQL::Schema::RelayClassicMutation 63 | argument :title, String, required: true 64 | 65 | field :product, ProductType, null: true 66 | field :errors, [String], null: false 67 | 68 | def resolve(title:) 69 | { product: { id: 42, title: title } } 70 | end 71 | end 72 | 73 | 74 | class MutationType < GraphQL::Schema::Object 75 | field :create_product, mutation: CreateProduct 76 | end 77 | 78 | class SubscriptionType < GraphQL::Schema::Object 79 | field :product_created, ProductType, null: false 80 | field :product_updated, ProductType, null: false 81 | 82 | # See https://github.com/rmosolgo/graphql-ruby/issues/1567 83 | def product_created; end 84 | def product_updated; end 85 | end 86 | 87 | class YabedaSchema < GraphQL::Schema 88 | use Yabeda::GraphQL 89 | 90 | query QueryType 91 | mutation MutationType 92 | subscription SubscriptionType 93 | 94 | use GraphQL::Batch 95 | end 96 | -------------------------------------------------------------------------------- /spec/yabeda/graphql_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe Yabeda::GraphQL do 2 | it "has a version number" do 3 | expect(Yabeda::GraphQL::VERSION).not_to be nil 4 | end 5 | 6 | subject do 7 | YabedaSchema.execute( 8 | query: query, 9 | context: {}, 10 | variables: {}, 11 | ) 12 | end 13 | 14 | describe "queries" do 15 | let(:query) do 16 | <<~GRAPHQL 17 | query { 18 | products { 19 | id title shmitle price { amount currency } 20 | } 21 | users { 22 | id name 23 | } 24 | } 25 | GRAPHQL 26 | end 27 | 28 | it "measures field executions" do 29 | Yabeda.graphql.field_resolve_runtime.sums.clear # This is a hack 30 | subject 31 | expect(Yabeda.graphql.field_resolve_runtime.sums).to match( 32 | { type: "Product", field: "id", deprecated: false } => kind_of(Numeric), 33 | { type: "Product", field: "title", deprecated: false } => be > 0.002, 34 | { type: "Product", field: "shmitle", deprecated: true } => kind_of(Numeric), 35 | { type: "Product", field: "price", deprecated: false } => be > 0.01, 36 | { type: "Price", field: "amount", deprecated: false } => kind_of(Numeric), 37 | { type: "Price", field: "currency", deprecated: false } => kind_of(Numeric), 38 | { type: "User", field: "id", deprecated: false } => kind_of(Numeric), 39 | { type: "User", field: "name", deprecated: false } => kind_of(Numeric), 40 | ) 41 | end 42 | 43 | it "increment counters" do 44 | Yabeda.graphql.fields_request_count.values.clear # This is a hack 45 | subject 46 | expect(Yabeda.graphql.fields_request_count.values).to match( 47 | 48 | { type: "Product", field: "id", deprecated: false } => 2, 49 | { type: "Product", field: "title", deprecated: false } => 2, 50 | { type: "Product", field: "shmitle", deprecated: true } => 2, 51 | { type: "Product", field: "price", deprecated: false } => 2, 52 | { type: "Price", field: "amount", deprecated: false } => 2, 53 | { type: "Price", field: "currency", deprecated: false } => 2, 54 | { type: "User", field: "id", deprecated: false } => 1, 55 | { type: "User", field: "name", deprecated: false } => 1, 56 | ) 57 | end 58 | 59 | it "counts query executions" do 60 | Yabeda.graphql.query_fields_count.values.clear # This is a hack 61 | subject 62 | expect(Yabeda.graphql.query_fields_count.values).to match( 63 | { name: "products", deprecated: false } => 1, 64 | { name: "users", deprecated: false } => 1, 65 | ) 66 | end 67 | end 68 | 69 | describe "mutations" do 70 | let(:query) do 71 | <<~GRAPHQL 72 | mutation { 73 | createProduct(input: { title: "Boo" }) { 74 | product { id title } 75 | } 76 | } 77 | GRAPHQL 78 | end 79 | 80 | it "measures response field executions" do 81 | Yabeda.graphql.field_resolve_runtime.sums.clear # This is a hack 82 | subject 83 | expect(Yabeda.graphql.field_resolve_runtime.sums).to match( 84 | { type: "Product", field: "id", deprecated: false } => kind_of(Numeric), 85 | { type: "Product", field: "title", deprecated: false } => kind_of(Numeric), 86 | { type: "CreateProduct", field: "product", deprecated: false } => kind_of(Numeric), 87 | ) 88 | end 89 | 90 | it "counts mutation executions" do 91 | Yabeda.graphql.mutation_fields_count.values.clear # This is a hack 92 | subject 93 | expect(Yabeda.graphql.mutation_fields_count.values).to match( 94 | { name: "createProduct", deprecated: false } => 1, 95 | ) 96 | end 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /yabeda-graphql-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yabeda-rb/yabeda-graphql/9df8e8d70bcc7086df0a831e08bdc6b9b7f8f772/yabeda-graphql-logo.png -------------------------------------------------------------------------------- /yabeda-graphql.gemspec: -------------------------------------------------------------------------------- 1 | require_relative 'lib/yabeda/graphql/version' 2 | 3 | Gem::Specification.new do |spec| 4 | spec.name = "yabeda-graphql" 5 | spec.version = Yabeda::GraphQL::VERSION 6 | spec.authors = ["Andrey Novikov"] 7 | spec.email = ["envek@envek.name"] 8 | 9 | spec.summary = %q{Collects metrics to monitor execution of your GraphQL queries} 10 | spec.description = %q{Extends Yabeda metrics with graphql-ruby metrics: queries, mutations, fields…} 11 | spec.homepage = "http://github.com/yabeda-rb/yabeda-graphql" 12 | spec.license = "MIT" 13 | spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0") 14 | 15 | spec.metadata["homepage_uri"] = spec.homepage 16 | 17 | # Specify which files should be added to the gem when it is released. 18 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 19 | spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do 20 | `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 21 | end 22 | spec.bindir = "exe" 23 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 24 | spec.require_paths = ["lib"] 25 | 26 | spec.add_runtime_dependency "yabeda", "~> 0.2" 27 | spec.add_runtime_dependency "graphql", ">= 1.9", "< 3" 28 | 29 | spec.add_development_dependency "bundler" 30 | spec.add_development_dependency "rake", "~> 13.0" 31 | spec.add_development_dependency "rspec", "~> 3.0" 32 | spec.add_development_dependency "appraisal" 33 | end 34 | --------------------------------------------------------------------------------