module Metrics extend ActiveSupport::Concern class_methods do def increment_counter(metric_name, amount = 1) cache_key = "metrics:#{metric_name}" current_value = Rails.cache.read(cache_key) || 0 Rails.cache.write(cache_key, current_value + amount) end def decrement_counter(metric_name, amount = 1) cache_key = "metrics:#{metric_name}" current_value = Rails.cache.read(cache_key) || 0 new_value = [current_value - amount, 0].max Rails.cache.write(cache_key, new_value) end def get_counter(metric_name) cache_key = "metrics:#{metric_name}" Rails.cache.read(cache_key) || 0 end def reset_counter(metric_name) cache_key = "metrics:#{metric_name}" Rails.cache.delete(cache_key) end def set_gauge(metric_name, value) cache_key = "metrics:gauge:#{metric_name}" Rails.cache.write(cache_key, value) end def get_gauge(metric_name) cache_key = "metrics:gauge:#{metric_name}" Rails.cache.read(cache_key) end # Record timing metrics def record_timing(metric_name, duration_ms) cache_key = "metrics:timing:#{metric_name}" timings = Rails.cache.read(cache_key) || [] timings << duration_ms # Keep only last 100 measurements timings = timings.last(100) Rails.cache.write(cache_key, timings) end def get_timing_stats(metric_name) cache_key = "metrics:timing:#{metric_name}" timings = Rails.cache.read(cache_key) || [] return nil if timings.empty? { count: timings.size, avg: timings.sum / timings.size.to_f, min: timings.min, max: timings.max, p95: percentile(timings, 95), p99: percentile(timings, 99) } end private def percentile(array, percent) return nil if array.empty? sorted = array.sort k = (percent / 100.0) * (sorted.length - 1) f = k.floor c = k.ceil if f == c sorted[k] else sorted[f] * (c - k) + sorted[c] * (k - f) end end end end