Module: Emrb::Instruments::ClassMethods

Defined in:
lib/emrb/instruments/instruments.rb

Overview

Provides the instrumentation facilities when including/extending Instruments.

Constant Summary collapse

FORBIDDEN_IDENTIFIERS =
instance_methods

Instance Method Summary collapse

Instance Method Details

#block_opts(**opts) ⇒ Object

Internal: Validates the provided options to create a given instrument and performs the merge of all options with presets when they are present.



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/emrb/instruments/instruments.rb', line 285

def block_opts(**opts, &)
  res = block_given? ? yield : opts
  return res if @presets.nil?

  res[:labels] = [] if res[:labels].nil?
  res[:labels].append(*@presets.keys)

  if res[:preset_labels].nil?
    res[:preset_labels] = @presets
    return res
  end

  res[:preset_labels].merge!(**@presets)
  res
end

#check_identifier!(id) ⇒ Object

Internal: Validates whether the given identifier might override one of the class methods. In a positive case, raises CollidingNameError.



303
304
305
# File 'lib/emrb/instruments/instruments.rb', line 303

def check_identifier!(id)
  raise Instruments::CollidingNameError, id if FORBIDDEN_IDENTIFIERS.include? id
end

#counter(identifier, docs = "...") ⇒ Object

Initializes a new Prometheus counter using the provided identifier, documentation, and either optional keyword arguments or a block that returns the keyword arguments. The block takes precedence when supplied.

identifier - The counter identifier. docs - The instrument docstring. Defaults to “…”

Usage example:

class MyApp

class Metrics
  include Emrb::Instruments
  counter :visits, "Number of visits the app has received"
end

def handle_visits
  Metrics.visits.inc
  ...
end

end

For a complete list of options and functionalities for creating and utilizing a Counter, refer to the Prometheus client documentation: github.com/prometheus/client_ruby?tab=readme-ov-file#counter



41
42
43
44
45
46
47
# File 'lib/emrb/instruments/instruments.rb', line 41

def counter(identifier, docs = "...", **, &)
  check_identifier!(identifier)
  opts = block_opts(**, &)
  State.counter(id_for(identifier), docs, **opts).tap do |c|
    define_singleton_method(identifier) { c }
  end
end

#gauge(identifier, docs = "...") ⇒ Object

Initializes a new Prometheus gauge using the provided identifier, documentation, and either optional keyword arguments or a block that returns the keyword arguments. The block takes precedence when supplied.

identifier - The counter identifier. docs - The instrument docstring. Defaults to “…”

Usage example:

class Thermometer

class Metrics
  include Emrb::Instruments
  gauge :current_temperature, "current temperature"
end

def set_current_temp
  temp = < ... >
  Metrics.current_temperature.set(temp)
end

end

For a full list of options and functionalities for creating and utilizing a Gauge, refer to the Prometheus client documentation: github.com/prometheus/client_ruby?tab=readme-ov-file#gauge



73
74
75
76
77
78
79
# File 'lib/emrb/instruments/instruments.rb', line 73

def gauge(identifier, docs = "...", **, &)
  check_identifier!(identifier)
  opts = block_opts(**, &)
  State.gauge(id_for(identifier), docs, **opts).tap do |g|
    define_singleton_method(identifier) { g }
  end
end

#histogram(identifier, docs = "...") ⇒ Object

Initializes a new Prometheus histogram using the provided identifier, documentation, and either optional keyword arguments or a block that returns the keyword arguments. The block takes precedence when supplied.

identifier - The counter identifier. docs - The instrument docstring. Defaults to “…”

Usage example:

class MyApp

class Metrics
  include Emrb::Instruments
  histogram :request_duration, "Duration of requests" do
    { labels: [:path, :method], buckets: [0.1, 0.2] }
   end
end

def measure_request_duration
  labels = { path: request.path, method: request.env["REQUEST_METHOD"].downcase }      #
  Metrics.request_duration.observe(Benchmark.realtime { yield }, labels: )
end

get "/" do
  measure_request_duration { [200, "OK"] }
end

end

For all available options and functionalities for creating and utilizing a Histogram, refer to the Prometheus client documentation: github.com/prometheus/client_ruby?tab=readme-ov-file#histogram



111
112
113
114
115
116
117
# File 'lib/emrb/instruments/instruments.rb', line 111

def histogram(identifier, docs = "...", **, &)
  check_identifier!(identifier)
  opts = block_opts(**, &)
  State.histogram(id_for(identifier), docs, **opts).tap do |h|
    define_singleton_method(identifier) { h }
  end
end

#id_for(identifier) ⇒ Object

Internal: validates whether to concatenate the given identifier with a pre-existing susbsystem name.



277
278
279
280
281
# File 'lib/emrb/instruments/instruments.rb', line 277

def id_for(identifier)
  return identifier unless @subsystem

  :"#{@subsystem}_#{identifier}"
end

#push(job) ⇒ Object

push the current registry state to a Pushgateway. It receives an obligatory job identifier, and optionally all supported keyword arguments of a Prometheus::Client::Push.

job - Job identifier

Usage example:

class MyJob

class Metrics
  include Emrb::Instruments
  counter :processed_tasks
end

def do_the_things
  begin
    tasks.each do |t|
      < ... >
      Metrics.processed_tasks.inc
    end
  rescue
      < ... >
  ensure
    Metrics.push("job_name")
  end
end

end



177
# File 'lib/emrb/instruments/instruments.rb', line 177

def push(job, **) = State.push(job, **)

#push_periodically(job, frequency = 10) ⇒ Object

Periodically invokes #push in a given frequency.

job - Job identifier frequency - Frequency, in seconds, in which #push will be called.

Returns nothing.



185
186
187
188
189
190
# File 'lib/emrb/instruments/instruments.rb', line 185

def push_periodically(job, frequency = 10, **)
  Thread.new do
    sleep(frequency)
    push(job, **)
  end
end

#subsystem(prefix, inherit_presets: false, **presets) ⇒ Object

Provides a way to compose metrics for different subsystems. All instruments created within a subsystem will have their identifiers prefixed with the prefix param.

prefix - determines the prefix of all instruments declared

within the subsystem and how to access those instruments
within the implementation.

inherit_presets - determines whether or not to inherit presets from

the parent. Defaults to false.

presets - preset of labels to be used by all Instruments

of a given subsystem.

Usage example:

class Metrics

subsystem :http do
  histogram :request_duration, "duration of requests" do
    { labels: [:method, :path] }
  end
end

subsystem :postgres do
  subsystem :master, op: "write" do
    counter :op_count
  end

  subsystem :replica, op: "read" do
    counter :op_count
  end
end

end

# Acessing instruments: Metrics.http.request_duration Metrics.postgres.master.op_count Metrics.postgres.replica.op_count rubocop:disable Metrics/MethodLength

Raises:

  • (LocalJumpError)


257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/emrb/instruments/instruments.rb', line 257

def subsystem(prefix, inherit_presets: false, **presets, &)
  raise LocalJumpError, "no block given" unless block_given?

  s_prefix = prefix.to_s
  s_prefix.delete_suffix! "_" if s_prefix.end_with? "_"
  subsystem = id_for(s_prefix.to_sym)

  presets.merge! @presets if inherit_presets && !@presets.nil?

  s = Class.new
  s.include(Instruments)
  s.instance_variable_set(:@subsystem, subsystem)
  s.instance_variable_set(:@presets, presets)
  s.instance_eval(&)
  define_singleton_method(prefix) { s }
end

#summary(identifier, docs = "...") ⇒ Object

def do_a_call

  Metrics.call_duration.observe(Benchmark.realtime { < ... > })
end

end

For a complete list of options and functionalities for creating and utilizing a Summary, refer to the Prometheus client documentation: github.com/prometheus/client_ruby?tab=readme-ov-file#summary



142
143
144
145
146
147
148
# File 'lib/emrb/instruments/instruments.rb', line 142

def summary(identifier, docs = "...", **, &)
  check_identifier!(identifier)
  opts = block_opts(**, &)
  State.summary(id_for(identifier), docs, **opts).tap do |s|
    define_singleton_method(identifier) { s }
  end
end

#with_presets(**labels) ⇒ Object

Allows instruments to be declared with preset labels.

labels - A hash containing the preset labels and values

Usage example:

class Metrics

include Emrb::Instruments
with_presets my: "label", other: "label" do
  counter :my_counter, "a counter"
end

end

Metrics.my_counter.labels => [:my, :other] Metrics.my_counter.preset_labels => { my: “label”, other: “label” }

Raises:

  • (LocalJumpError)


207
208
209
210
211
212
213
214
215
216
# File 'lib/emrb/instruments/instruments.rb', line 207

def with_presets(**labels, &)
  raise LocalJumpError, "no block given" unless block_given?
  raise ArgumentError, "labels are empty" if labels.nil? || labels.empty?

  old = (@presets ||= {})
  current = old.merge labels
  @presets = current
  instance_eval(&)
  @presets = old
end