Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,6 @@ CLAUDE.md
/app/assets/builds/*
!/app/assets/builds/.keep
.cursorindexingignore
.specstory
.specstory
doc/metrics_and_monitoring_ru.md
doc/prometheus_grafana_setup_ru.md
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ gem 'turbo-rails'
gem 'valvat'
gem 'view_component'
gem 'webpush'
gem 'yabeda-rails'
gem 'yabeda-prometheus'

group :development, :test do
gem 'brakeman'
Expand Down
21 changes: 21 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ GEM
airbrake-ruby (6.2.2)
rbtree3 (~> 0.6)
amazing_print (1.8.1)
anyway_config (2.7.2)
ruby-next-core (~> 1.0)
ast (2.4.3)
attr_required (1.0.2)
base64 (0.3.0)
Expand Down Expand Up @@ -158,6 +160,7 @@ GEM
docile (1.4.1)
domain_name (0.6.20240107)
drb (2.2.3)
dry-initializer (3.2.0)
email_validator (2.2.4)
activemodel
erb (5.0.2)
Expand Down Expand Up @@ -382,6 +385,8 @@ GEM
prettyprint
prettyprint (0.2.0)
prism (1.5.1)
prometheus-client (4.2.5)
base64
propshaft (1.2.1)
actionpack (>= 7.0.0)
activesupport (>= 7.0.0)
Expand Down Expand Up @@ -506,6 +511,7 @@ GEM
rbs (>= 3, < 5)
ruby-lsp-rails (0.4.8)
ruby-lsp (>= 0.26.0, < 0.27.0)
ruby-next-core (1.1.2)
ruby-openai (8.3.0)
event_stream_parser (>= 0.3.0, < 2.0.0)
faraday (>= 1)
Expand Down Expand Up @@ -606,6 +612,19 @@ GEM
websocket-extensions (0.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
yabeda (0.14.0)
anyway_config (>= 1.0, < 3)
concurrent-ruby
dry-initializer
yabeda-prometheus (0.9.1)
prometheus-client (>= 3.0, < 5.0)
rack
yabeda (~> 0.10)
yabeda-rails (0.10.0)
activesupport
anyway_config (>= 1.3, < 3)
railties
yabeda (~> 0.8)
zeitwerk (2.7.3)

PLATFORMS
Expand Down Expand Up @@ -694,6 +713,8 @@ DEPENDENCIES
web-console (>= 3.3.0)
webmock
webpush
yabeda-prometheus
yabeda-rails

BUNDLED WITH
2.6.2
6 changes: 6 additions & 0 deletions app/controllers/auctions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ def index
link_extra: 'data-turbo-action="advance"'
)

increment_home_page_total_views

respond_to do |format|
format.html
format.json
Expand All @@ -34,6 +36,10 @@ def cors_preflight_check

private

def increment_home_page_total_views
Yabeda.auction.home_page_total_views.increment
end

def fetch_auctions_list = Auction.active.search(params, current_user)

def per_page_count
Expand Down
4 changes: 4 additions & 0 deletions app/controllers/email_confirmations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ def create
yield resource if block_given?

if successfully_sent?(resource)
Yabeda.user_business.email_confirmation_resent_total.increment({})
flash[:notice] = t('devise.confirmations.send_instructions')
respond_with({}, location: after_resending_confirmation_instructions_path_for(resource_name))
else
Yabeda.user_business.email_confirmation_failures_total.increment(
reason: resource.errors.full_messages.join(", ").presence || "unknown"
)
flash[:alert] = resource.errors.full_messages.join(', ')
redirect_to root_path, status: :see_other
end
Expand Down
4 changes: 4 additions & 0 deletions app/controllers/invoices_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ def pay_deposit
if response.result?
redirect_to response.instance['oneoff_redirect_link'], allow_other_host: true, format: :html
else
Yabeda.invoice_business.payment_flow_failures_total.increment(
flow: "pay_deposit",
error_type: response.errors['code'] || "unknown"
)
flash.alert = response.errors['message']
redirect_to invoices_path
end
Expand Down
11 changes: 11 additions & 0 deletions app/controllers/offers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,25 @@ def create

respond_to do |format|
if existing_offer
Yabeda.auction_business.offers_failed_total.increment(
reason: "duplicate_offer",
platform: @auction.platform
)
format.html do
redirect_to root_path, notice: t('offers.already_exists')
end
elsif create_predicate
Yabeda.auction_business.offers_created_total.increment(
platform: @auction.platform
)
Rails.logger.info("User #{current_user.id} created offer #{@offer.id} for auction #{@auction.id}")
format.html { redirect_to root_path, notice: t('.created') }
format.json { render :show, status: :created, location: @offer }
else
Yabeda.auction_business.offers_failed_total.increment(
reason: "validation_error",
platform: @auction.platform
)
error_msg = @offer.errors.full_messages.join('; ')
flash[:alert] = error_msg
Rails.logger.error("User #{current_user.id} tried to create offer for auction #{@auction.id} but it failed. Errors: #{error_msg}")
Expand Down
4 changes: 4 additions & 0 deletions app/controllers/phone_confirmations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ def new
def create
user_uuid = @phone_confirmation.user.uuid

Yabeda.phone_business.phone_confirmation_attempts_total.increment({})

respond_to do |format|
if create_predicate
Yabeda.phone_business.phone_confirmation_success_total.increment({})
format.html { redirect_to user_path(user_uuid), notice: t('.confirmed') }
format.json { redirect_to user_path(user_uuid), status: :created, location: @user }
else
Yabeda.phone_business.phone_confirmation_invalid_code_total.increment({})
format.html do
redirect_to new_user_phone_confirmation_path(user_uuid),
notice: t('phone_confirmations.invalid_code')
Expand Down
17 changes: 17 additions & 0 deletions app/jobs/application_job.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
class ApplicationJob < ActiveJob::Base
# Track job execution time and failures with Yabeda metrics
around_perform do |job, block|
labels = {
job: job.class.name,
queue: job.queue_name || "default"
}

start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
block.call
rescue => e
Yabeda.jobs.job_failures_total.increment(labels.merge(exception_class: e.class.name))
raise
ensure
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
Yabeda.jobs.job_duration.measure(labels, duration)
end

rescue_from StandardError do |e|
Rails.logger.info e
Airbrake.notify(e)
Expand Down
12 changes: 12 additions & 0 deletions app/jobs/invoices_overdue_metric_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

# Periodic job to update invoices_overdue_total metric
# Should be scheduled to run periodically (e.g., every hour via cron or whenever)
class InvoicesOverdueMetricJob < ApplicationJob
queue_as :default

def perform
overdue_count = Invoice.overdue.count
Yabeda.invoice_business.invoices_overdue_total.set({}, overdue_count)
end
end
13 changes: 13 additions & 0 deletions app/models/auction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Auction < ApplicationRecord # rubocop:disable Metrics
ENGLISH = '1'.freeze

after_create :find_auction_turns
after_commit :update_active_auctions_metric
validates :domain_name, presence: true

attr_accessor :skip_broadcast, :skip_validation
Expand Down Expand Up @@ -241,4 +242,16 @@ def users_price
def maximum_bids
Money.new(offers.maximum(:cents), Setting.find_by(code: 'auction_currency').retrieve)
end

private

def update_active_auctions_metric
%w[blind english].each do |platform_name|
count = Auction.where(platform: platform_name)
.where("starts_at <= ? AND ends_at >= ?", Time.current, Time.current)
.count

Yabeda.auction_business.active_auctions_total.set({ platform: platform_name }, count)
end
end
end
31 changes: 31 additions & 0 deletions app/models/concerns/user_metrics.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

# User metrics tracking for Yabeda
module UserMetrics
extend ActiveSupport::Concern

included do
after_create :track_user_registration
after_update :track_user_confirmation, if: :saved_change_to_confirmed_at?
end

private

def track_user_registration
provider_name = provider.presence || "password"
Yabeda.user_business.user_registrations_total.increment(provider: provider_name)
end

def track_user_confirmation
return unless confirmed_at.present?

provider_name = provider.presence || "password"
Yabeda.user_business.user_confirmed_total.increment(provider: provider_name)

# Update gauge for TARA unconfirmed users
if provider == User::TARA_PROVIDER
count = User.where(provider: User::TARA_PROVIDER, confirmed_at: nil).count
Yabeda.user_business.user_tara_unconfirmed_total.set({}, count)
end
end
end
9 changes: 9 additions & 0 deletions app/models/invoice.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Invoice < ApplicationRecord
validate :user_id_must_be_the_same_as_on_billing_profile_or_nil

before_create :set_invoice_number
after_create :track_invoice_created

before_update :update_billing_info
before_update :recalculate_vat_rate
Expand Down Expand Up @@ -233,6 +234,7 @@ def mark_as_paid_at(time)
paid!
end

Yabeda.invoice_business.invoices_paid_total.increment(payment_channel: "manual")
ResultStatusUpdateJob.perform_now
end

Expand All @@ -249,6 +251,9 @@ def mark_as_paid_at_with_payment_order(time, payment_order)
result.mark_as_payment_received(time) unless cancelled?
clear_linked_ban
paid!

channel = payment_order.type.demodulize
Yabeda.invoice_business.invoices_paid_total.increment(payment_channel: channel)
else
save!
end
Expand Down Expand Up @@ -320,5 +325,9 @@ def clear_linked_ban
def finally_paid?
due_amount <= 0
end

def track_invoice_created
Yabeda.invoice_business.invoices_created_total.increment(status: status)
end
end
# rubocop:enable Metrics/ClassLength
12 changes: 12 additions & 0 deletions app/models/offer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class Offer < ApplicationRecord

after_create :update_auction_ends_at
after_update :update_auction_ends_at
after_create :track_unique_bidder_metric

def update_auction_ends_at
return if auction.platform == 'blind' || auction.platform.nil?
Expand Down Expand Up @@ -113,4 +114,15 @@ def total

price * (DEFAULT_PRICE_VALUE + (Invoice.find_by(result: result)&.vat_rate || default_vat))
end

private

def track_unique_bidder_metric
return if user_id.nil?

Metrics::UniqueUserBidderTracker.track(user_id)
rescue => e
Rails.logger.error("Failed to track unique bidder metric: #{e.message}")
Sentry.capture_exception(e) if defined?(Sentry)
end
end
6 changes: 6 additions & 0 deletions app/models/phone_confirmation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ def generate_and_send_code
locale = locale_for_sms(user)

send_sms_with_locale(locale, padded_number)
Yabeda.phone_business.phone_confirmation_sms_sent_total.increment({})
rescue => e
Yabeda.phone_business.phone_confirmation_sms_failures_total.increment(
exception_class: e.class.name
)
raise
end

def code
Expand Down
1 change: 1 addition & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
class User < ApplicationRecord
include Bannable
include ReferenceNo
include UserMetrics

PARTICIPANT_ROLE = 'participant'.freeze
ADMINISTATOR_ROLE = 'administrator'.freeze
Expand Down
12 changes: 12 additions & 0 deletions app/services/eis_billing/base_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,26 @@ module EisBilling
module BaseService
def struct_response(response)
if response['error'].present?
increment_failure_counter(error_type: 'provider_error')
wrap(result: false, instance: nil, errors: response['error'])
else
wrap(result: true, instance: response, errors: nil)
end
rescue StandardError => e
increment_failure_counter(error_type: 'exception', exception_class: e.class.name)
wrap(result: false, instance: nil, errors: e)
end

private

def increment_failure_counter(error_type:, exception_class: nil)
Yabeda.eis_billing.payment_failures_total.increment(
service: self.class.name.demodulize,
error_type: error_type,
exception_class: exception_class || 'none'
)
end

def wrap(**kwargs)
result = kwargs[:result]
instance = kwargs[:instance]
Expand Down
Loading
Loading