Class: LcpRuby::Metrics::Collector

Inherits:
Object
  • Object
show all
Defined in:
lib/lcp_ruby/metrics/collector.rb

Overview

Base class for custom metric collectors. Host apps define collectors in app/lcp_metrics/ that inherit from this.

Example:

class ErpMetrics < LcpRuby::Metrics::Collector
  gauge :erp_active_orders, "Number of active orders"
  cache_ttl 60

  def collect
    set(:erp_active_orders, Order.active.count)
  end

  subscribe "payment.processed" do |event|
    increment(:payment_total, labels: { status: event.payload[:status] })
  end
end

Constant Summary collapse

COLLECT_TIMEOUT =

seconds

5

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.activate_subscriptions!(instance) ⇒ Object

Activates AS::Notifications subscriptions for this collector instance. Returns an array of subscription objects (for later unsubscribe).



76
77
78
79
80
81
82
83
# File 'lib/lcp_ruby/metrics/collector.rb', line 76

def activate_subscriptions!(instance)
  subscription_defs.map do |defn|
    ActiveSupport::Notifications.subscribe(defn[:event]) do |*args|
      event = ActiveSupport::Notifications::Event.new(*args)
      instance.instance_exec(event, &defn[:block])
    end
  end
end

.cache_ttl(seconds) ⇒ Object



39
40
41
# File 'lib/lcp_ruby/metrics/collector.rb', line 39

def cache_ttl(seconds)
  @cache_ttl = seconds
end

.counter(name, description, labels: []) ⇒ Object



27
28
29
# File 'lib/lcp_ruby/metrics/collector.rb', line 27

def counter(name, description, labels: [])
  metric_defs << { name: name, type: :counter, description: description, labels: labels }
end

.gauge(name, description, labels: []) ⇒ Object



23
24
25
# File 'lib/lcp_ruby/metrics/collector.rb', line 23

def gauge(name, description, labels: [])
  metric_defs << { name: name, type: :gauge, description: description, labels: labels }
end

.get_cache_ttlObject



43
44
45
# File 'lib/lcp_ruby/metrics/collector.rb', line 43

def get_cache_ttl
  @cache_ttl || 30
end

.histogram(name, description, labels: [], buckets: nil) ⇒ Object



31
32
33
# File 'lib/lcp_ruby/metrics/collector.rb', line 31

def histogram(name, description, labels: [], buckets: nil)
  metric_defs << { name: name, type: :histogram, description: description, labels: labels, buckets: buckets }
end

.metric_defsObject



47
48
49
# File 'lib/lcp_ruby/metrics/collector.rb', line 47

def metric_defs
  @metric_defs ||= []
end

.register_metrics!Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/lcp_ruby/metrics/collector.rb', line 55

def register_metrics!
  return unless PrometheusCheck.available?

  registry = ::Prometheus::Client.registry
  metric_defs.each do |defn|
    next if registry.exist?(defn[:name])

    case defn[:type]
    when :gauge
      registry.gauge(defn[:name], docstring: defn[:description], labels: defn[:labels])
    when :counter
      registry.counter(defn[:name], docstring: defn[:description], labels: defn[:labels])
    when :histogram
      buckets = defn[:buckets] || LcpRuby.configuration.metrics_histogram_buckets
      registry.histogram(defn[:name], docstring: defn[:description], labels: defn[:labels], buckets: buckets)
    end
  end
end

.subscribe(event_name, &block) ⇒ Object



35
36
37
# File 'lib/lcp_ruby/metrics/collector.rb', line 35

def subscribe(event_name, &block)
  subscription_defs << { event: event_name, block: block }
end

.subscription_defsObject



51
52
53
# File 'lib/lcp_ruby/metrics/collector.rb', line 51

def subscription_defs
  @subscription_defs ||= []
end

Instance Method Details

#collectObject



86
87
88
# File 'lib/lcp_ruby/metrics/collector.rb', line 86

def collect
  # Override in subclass
end

#safe_collectObject



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/lcp_ruby/metrics/collector.rb', line 90

def safe_collect
  ttl = self.class.get_cache_ttl

  if @last_collected_at && (Time.now - @last_collected_at) < ttl
    return
  end

  Timeout.timeout(COLLECT_TIMEOUT) { collect }
  @last_collected_at = Time.now
rescue Timeout::Error => e
  LcpRuby.record_error(e, subsystem: "collector", collector: self.class.name, timeout_seconds: COLLECT_TIMEOUT)
rescue StandardError => e
  raise unless defined?(Rails) && Rails.env.production?
  LcpRuby.record_error(e, subsystem: "collector", collector: self.class.name)
end