Skip to content
This repository has been archived by the owner on Jan 3, 2025. It is now read-only.

Commit

Permalink
Adds Prometheus metrics (#60)
Browse files Browse the repository at this point in the history
Added Prometheus Metrics
  • Loading branch information
FinnIckler authored Jun 6, 2023
1 parent be7757c commit c2c9cb0
Show file tree
Hide file tree
Showing 23 changed files with 179 additions and 65 deletions.
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ gem 'kredis'
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]

# Exposes Metrics
gem 'prometheus_exporter'

# vault for secrets management
gem 'vault-rails'

Expand Down
4 changes: 4 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ GEM
parallel (1.23.0)
parser (3.2.2.1)
ast (~> 2.4.1)
prometheus_exporter (2.0.8)
webrick
puma (5.6.5)
nio4r (~> 2.0)
racc (1.6.2)
Expand Down Expand Up @@ -204,6 +206,7 @@ GEM
vault-rails (0.8.0)
activesupport (>= 5.0)
vault (~> 0.17)
webrick (1.8.1)
websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
Expand All @@ -221,6 +224,7 @@ DEPENDENCIES
hiredis
jbuilder
kredis
prometheus_exporter
puma (~> 5.0)
rack-cors
rails (~> 7.0.4, >= 7.0.4.3)
Expand Down
18 changes: 0 additions & 18 deletions app/controllers/metrics_controller.rb

This file was deleted.

19 changes: 14 additions & 5 deletions app/controllers/registration_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def create
event_ids = params[:event_ids]

unless validate_request(competitor_id, competition_id) && !event_ids.empty?
Metrics.registration_validation_errors_counter.increment
return render json: { status: 'User cannot register for competition' }, status: :forbidden
end

Expand Down Expand Up @@ -42,6 +43,7 @@ def update
status = params[:status]

unless validate_request(competitor_id, competition_id, status)
Metrics.registration_validation_errors_counter.increment
return render json: { status: 'User cannot register, wrong format' }, status: :forbidden
end

Expand All @@ -67,7 +69,8 @@ def update
})
render json: { status: 'ok' }
rescue Aws::DynamoDB::Errors::ServiceError => e
puts e # TODO: Expose this as a metric
puts e
Metrics.registration_dynamodb_errors_counter.increment
render json: { status: 'Failed to update registration data' }, status: :internal_server_error
end
end
Expand All @@ -77,6 +80,7 @@ def delete
competition_id = params[:competition_id]

unless validate_request(competitor_id, competition_id)
Metrics.registration_validation_errors_counter.increment
return render json: { status: 'User cannot register, wrong format' }, status: :forbidden
end

Expand All @@ -97,7 +101,8 @@ def delete
render json: { status: 'ok' }
rescue Aws::DynamoDB::Errors::ServiceError => e
# Render an error response
puts e # TODO: Expose this as a metric
puts e
Metrics.registration_dynamodb_errors_counter.increment
render json: { status: "Error deleting item from DynamoDB: #{e.message}" },
status: :internal_server_error
end
Expand All @@ -106,8 +111,14 @@ def delete
def list
competition_id = params[:competition_id]
registrations = get_registrations(competition_id)

# Render a success response
render json: registrations
rescue Aws::DynamoDB::Errors::ServiceError => e
# Render an error response
puts e
Metrics.registration_dynamodb_errors_counter.increment
render json: { status: "Error getting registrations" },
status: :internal_server_error
end

private
Expand All @@ -133,9 +144,7 @@ def can_user_register?(competitor_id, competition_id)

def validate_request(competitor_id, competition_id, status = 'waiting')
if competitor_id.present? && competitor_id =~ (/^\d{4}[a-zA-Z]{4}\d{2}$/)
puts 'correct competitor_id'
if competition_id =~ (/^[a-zA-Z]+\d{4}$/) && REGISTRATION_STATUS.include?(status)
puts 'correct competition id and status'
can_user_register?(competitor_id, competition_id)
end
else
Expand Down
2 changes: 1 addition & 1 deletion app/helpers/competition_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def self.check_competition(competition_id)
body = JSON.parse res.body
body['registration_open'].present?
else
# The Competition Service is unreachable TODO We should track this as a metric
Metrics.registration_competition_api_error_counter.increment
puts 'network request failed'
false
end
Expand Down
21 changes: 14 additions & 7 deletions app/helpers/competitor_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@
class CompetitorApi
def self.check_competitor(competitor_id)
uri = URI("https://www.worldcubeassociation.org/api/v0/users/#{competitor_id}")
res = Net::HTTP.get_response(uri)
if res.is_a?(Net::HTTPSuccess)
body = JSON.parse res.body
body['user'].present?
else
# The Competitor Service is unreachable TODO We should track this as a metric
puts 'network request failed'
begin
res = Net::HTTP.get_response(uri)
if res.is_a?(Net::HTTPSuccess)
body = JSON.parse res.body
body['user'].present?
else
# The Competitor Service is unreachable
Metrics.registration_competitor_api_error_counter.increment
puts 'network request failed'
false
end
rescue StandardError => _e
puts 'The service does not have internet'
Metrics.registration_competitor_api_error_counter.increment
false
end
end
Expand Down
7 changes: 7 additions & 0 deletions app/helpers/metrics.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Metrics
class << self
attr_accessor :registration_validation_errors_counter, :registration_dynamodb_errors_counter, :registrations_counter, :registration_competition_api_error_counter, :registration_competitor_api_error_counter
end
end
17 changes: 17 additions & 0 deletions app/worker/queue_poller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

require 'json'
require 'aws-sdk-sqs'
require 'prometheus_exporter/client'
require 'prometheus_exporter/instrumentation'
require 'prometheus_exporter/metric'
require_relative 'registration_processor'

class QueuePoller
Expand All @@ -11,6 +14,18 @@ class QueuePoller
MAX_MESSAGES = 10

def self.perform
PrometheusExporter::Client.default = PrometheusExporter::Client.new(host: ENV.fetch("PROMETHEUS_EXPORTER"), port: 9091)
# Instrumentation of the worker process is currently disabled per https://github.com/discourse/prometheus_exporter/issues/282
if ENV.fetch("ENVIRONMENT", "dev") == "staging"
PrometheusExporter::Instrumentation::Process.start(type: "wca-registration-worker-staging", labels: { process: "1" })
@suffix = "-staging"
else
# PrometheusExporter::Instrumentation::Process.start(type: "wca-registration-worker", labels: { process: "1" })
@suffix = ""
end
registrations_counter = PrometheusExporter::Client.default.register("counter", "registrations_counter-#{@suffix}", "The number of Registrations processed")
error_counter = PrometheusExporter::Client.default.register("counter", "worker_error_counter-#{@suffix}", "The number of Errors in the worker")

@sqs ||= if ENV['LOCALSTACK_ENDPOINT']
Aws::SQS::Client.new(endpoint: ENV['LOCALSTACK_ENDPOINT'])
else
Expand All @@ -27,10 +42,12 @@ def self.perform
body = JSON.parse msg.body
begin
RegistrationProcessor.process_message(body)
registrations_counter.increment
rescue StandardError => e
# unexpected error occurred while processing messages,
# log it, and skip delete so it can be re-processed later
puts "Error #{e} when processing message with ID #{msg}"
error_counter.increment
throw :skip_delete
end
end
Expand Down
2 changes: 1 addition & 1 deletion config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module WcaRegistration
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.0

config.autoload_paths << "#{root}/lib/**"
# Only loads a smaller set of middleware suitable for API only apps.
# Middleware like session, flash, cookies can be added back manually.
# Skip views, helpers and assets when generating a new resource.
Expand Down
9 changes: 8 additions & 1 deletion config/initializers/cors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,18 @@
else
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*.worldcubeassociation.org', 'http://test.registration.worldcubeassociation.org.s3-website-us-west-2.amazonaws.com'
origins 'https://www.worldcubeassociation.org', 'https://test-registration.worldcubeassociation.org/', 'http://test.registration.worldcubeassociation.org.s3-website-us-west-2.amazonaws.com'

resource '*',
headers: :any,
methods: %i[get post put patch delete options head]
end
allow do
origins '*'

resource '/api/v1/*',
headers: :any,
methods: %i[get post put patch delete options head]
end
end
end
30 changes: 30 additions & 0 deletions config/initializers/prometheus.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

require 'prometheus_exporter/client'
require 'prometheus_exporter/instrumentation'
require 'prometheus_exporter/metric'

require_relative '../../app/helpers/metrics'

PrometheusExporter::Client.default = PrometheusExporter::Client.new(host: ENV.fetch("PROMETHEUS_EXPORTER"), port: 9091)

if ENV.fetch("ENVIRONMENT", "dev") == "staging"
PrometheusExporter::Instrumentation::Process.start(type: "wca-registration-handler-staging", labels: { process: "1" })
@suffix = "-staging"
else
PrometheusExporter::Instrumentation::Process.start(type: "wca-registration-handler", labels: { process: "1" })
@suffix = ""
end

unless Rails.env.test?
require 'prometheus_exporter/middleware'

# This reports stats per request like HTTP status and timings
Rails.application.middleware.unshift PrometheusExporter::Middleware
end

# Create our Metric Counters
Metrics.registration_dynamodb_errors_counter = PrometheusExporter::Client.default.register("counter", "registration_dynamodb_errors_counter#{@suffix}", "The number of times interacting with dynamodb fails")
Metrics.registration_competition_api_error_counter = PrometheusExporter::Client.default.register("counter", "registration_competition_api_error_counter#{@suffix}", "The number of times interacting with the competition API failed")
Metrics.registration_competitor_api_error_counter = PrometheusExporter::Client.default.register("counter", "registration_competitor_api_error_counter#{@suffix}", "The number of times interacting with the competitor API failed")
Metrics.registration_validation_errors_counter = PrometheusExporter::Client.default.register("counter", "registration_validation_errors_counter#{@suffix}", "The number of times validation fails when an attendee tries to register")
1 change: 0 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@
patch '/api/v1/register', to: 'registration#update'
delete '/api/v1/register', to: 'registration#delete'
get '/api/v1/registrations', to: 'registration#list'
get '/metrics', to: 'metrics#index'
end
25 changes: 24 additions & 1 deletion docker-compose.dev.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
version: "3.8"
services:
wca_registration_handler:
container_name: wca_registration_handler
build:
context: .
dockerfile: dockerfile.dev
Expand All @@ -13,6 +14,7 @@ services:
AWS_SECRET_ACCESS_KEY: "fake-access-key"
VAULT_ADDR: "http://wca_vault:8200"
VAULT_DEV_ROOT_TOKEN_ID: aDGdUASDGIUASGDKI
PROMETHEUS_EXPORTER: "prometheus_exporter"
volumes:
- .:/app
- gems_volume_handler:/usr/local/bundle
Expand All @@ -27,8 +29,22 @@ services:
depends_on:
- localstack
- wca_vault
- prometheus_exporter

prometheus_exporter:
container_name: prometheus_exporter
build:
context: .
dockerfile: dockerfile.metrics
tty: true
ports:
- "9091:9091"
networks:
- wca-registration


wca_registration_worker:
container_name: wca_registration_worker
build:
context: .
dockerfile: dockerfile.dev
Expand All @@ -37,6 +53,7 @@ services:
AWS_REGION: "us-east-1"
AWS_ACCESS_KEY_ID: "fake-key"
AWS_SECRET_ACCESS_KEY: "fake-access-key"
PROMETHEUS_EXPORTER: "prometheus_exporter"

volumes:
- .:/app
Expand Down Expand Up @@ -71,6 +88,7 @@ services:
- wca-registration

dynamodb-admin:
container_name: dynamodb-admin
image: aaronshaf/dynamodb-admin
ports:
- "8001:8001"
Expand All @@ -85,6 +103,7 @@ services:
- wca-registration

wca_vault:
container_name: vault
image: hashicorp/vault
environment:
- VAULT_DEV_ROOT_TOKEN_ID=aDGdUASDGIUASGDKI
Expand All @@ -95,6 +114,7 @@ services:

# Frontend
frontend:
container_name: frontend
build:
context: Frontend
tty:
Expand All @@ -106,15 +126,18 @@ services:
- ./Frontend/src:/app/src
depends_on:
- nginx

nginx:
container_name: nginx
image: nginx:latest
ports:
- "3002:80"
networks:
- wca-registration
volumes:
- ./Frontend/frontend_local.conf:/etc/nginx/conf.d/default.conf

depends_on:
- frontend

volumes:
gems_volume_handler:
Expand Down
Loading

0 comments on commit c2c9cb0

Please sign in to comment.