diff --git a/Gemfile b/Gemfile index 09a16dbd..a50c4401 100644 --- a/Gemfile +++ b/Gemfile @@ -12,6 +12,7 @@ gem 'jwt' gem 'lograge' gem 'mitlibraries-theme', git: 'https://github.com/mitlibraries/mitlibraries-theme', tag: 'v1.2' gem 'opensearch-ruby' +gem 'prometheus-client' gem 'puma' gem 'rack-attack' gem 'rack-cors' diff --git a/Gemfile.lock b/Gemfile.lock index ecfe8d54..6dd301ce 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -248,6 +248,7 @@ GEM pathutil (0.16.2) forwardable-extended (~> 2.6) pg (1.5.4) + prometheus-client (4.2.2) psych (5.1.2) stringio public_suffix (5.0.4) @@ -433,6 +434,7 @@ DEPENDENCIES mitlibraries-theme! opensearch-ruby pg + prometheus-client puma rack-attack rack-cors diff --git a/README.md b/README.md index 5b8a2595..0f08a77b 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,7 @@ locally. - `PREFERRED_DOMAIN` - set this to the domain you would like to to use. Any other requests that come to the app will redirect to the root of this domain. This is useful to prevent access to herokuapp.com domains. +- `PROMETHEUS` - If present, enables the Prometheus metrics endpoint and the feature flag to capture metrics - `REQUESTS_PER_PERIOD` - requests allowed before throttling. Default is 100. - `REQUEST_PERIOD` - number of minutes for the period in `REQUESTS_PER_PERIOD`. Default is 1. diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb index 37ea8b30..f5c2e53c 100644 --- a/app/controllers/graphql_controller.rb +++ b/app/controllers/graphql_controller.rb @@ -10,9 +10,12 @@ def execute # current_user: current_user, tracers: [request_tracer] } - result = TimdexSchema.execute(query, variables: variables, - context: context, - operation_name: operation_name) + result = TimdexSchema.execute(query, variables:, + context:, + operation_name:) + + Timdex::GraphqlQueriesTotal.increment if Flipflop.enabled?(:prometheus) + render json: result rescue StandardError => e raise e unless Rails.env.development? diff --git a/app/graphql/timdex_field_usage_analyzer.rb b/app/graphql/timdex_field_usage_analyzer.rb index aca1131b..98af4c5d 100644 --- a/app/graphql/timdex_field_usage_analyzer.rb +++ b/app/graphql/timdex_field_usage_analyzer.rb @@ -8,6 +8,27 @@ def result Rails.logger.info("GraphQL used fields: #{@used_fields.to_a}") Rails.logger.info("GraphQL used deprecated fields: #{@used_deprecated_fields.to_a}") Rails.logger.info("GraphQL used deprecated arguments: #{@used_deprecated_arguments.to_a}") + + if Flipflop.enabled?(:prometheus) + # Increment deprecated field usage counter per field + @used_deprecated_fields.each do |field| + Timdex::GraphqlDeprecatedFieldUsage.increment(labels: { field: }) + end + + # Increment field usage counter per field + @used_fields.each do |field| + next if @used_deprecated_fields.include?(field) + next if field.start_with?('_') + + Timdex::GraphqlFieldUsage.increment(labels: { field: }) + end + + # Increment deprecated argument counter per argument + @used_deprecated_arguments.each do |field| + Timdex::GraphqlDeprecatedArguments.increment(labels: { field: }) + end + end + { used_fields: @used_fields.to_a, used_deprecated_fields: @used_deprecated_fields.to_a, diff --git a/config.ru b/config.ru index ad1fbf29..3e65b446 100644 --- a/config.ru +++ b/config.ru @@ -4,3 +4,13 @@ require_relative 'config/environment' run Rails.application Rails.application.load_server + +if ENV.fetch('PROMETHEUS', false).present? + require 'rack' + require 'prometheus/middleware/collector' + require 'prometheus/middleware/exporter' + + use Rack::Deflater + use Prometheus::Middleware::Collector + use Prometheus::Middleware::Exporter +end diff --git a/config/features.rb b/config/features.rb index 744fcdf2..801d5a23 100644 --- a/config/features.rb +++ b/config/features.rb @@ -2,4 +2,8 @@ # Strategies will be used in the order listed here. strategy :session strategy :default + + feature :prometheus, + default: ENV.fetch('PROMETHEUS', false), + description: "Enable prometheus metrics endpoint" end diff --git a/config/initializers/prometheus_metrics.rb b/config/initializers/prometheus_metrics.rb new file mode 100644 index 00000000..a57ec343 --- /dev/null +++ b/config/initializers/prometheus_metrics.rb @@ -0,0 +1,14 @@ +# flipflip features are available to use in initializers so we cheat a bit here. +return unless ENV.fetch('PROMETHEUS', false).present? + +require 'prometheus/client' +require 'prometheus/client/push' + +prometheus = Prometheus::Client.registry +Timdex::GraphqlQueriesTotal = prometheus.counter(:graphql_queries_total, docstring: 'A counter of GraphQL requests made') + +Timdex::GraphqlFieldUsage = prometheus.counter(:graphql_field_usage, docstring: 'A counter of GraphQL fields requested', labels: [:service, :field], preset_labels: { service: "timdex-api" }) + +Timdex::GraphqlDeprecatedFieldUsage = prometheus.counter(:graphql_deprecated_field_usage, docstring: 'A counter of deprecated GraphQL fields requested', labels: [:service, :field], preset_labels: { service: "timdex-api" }) + +Timdex::GraphqlDeprecatedArguments = prometheus.counter(:graphql_deprecated_argument_usage, docstring: 'A counter of deprecated GraphQL arguments requested', labels: [:service, :field], preset_labels: { service: "timdex-api" })