module StatsD::Instrument

The StatsD::Instrument module provides metaprogramming methods to instrument your methods with StatsD metrics. E.g., you can create counters on how often a method is called, how often it is successful, the duration of the methods call, etc.

Constants

MetricExpectation

@private

UNSPECIFIED
VERSION
VOID
VoidClass

Public Class Methods

current_timestamp() click to toggle source

Even though this method is considered private, and is no longer used internally, applications in the wild rely on it. As a result, we cannot remove this method until the next major version.

@deprecated Use Process.clock_gettime(Process::CLOCK_MONOTONIC) instead.

# File lib/statsd/instrument.rb, line 118
def self.current_timestamp
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
duration() { || ... } click to toggle source

Even though this method is considered private, and is no longer used internally, applications in the wild rely on it. As a result, we cannot remove this method until the next major version.

@deprecated You can implement similar functionality yourself using

`Process.clock_gettime(Process::CLOCK_MONOTONIC)`. Think about what will
happen if an exception happens during the block execution though.
# File lib/statsd/instrument.rb, line 129
def self.duration
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  yield
  Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
end
generate_metric_name(prefix, key, callee, *args) click to toggle source

@private

# File lib/statsd/instrument.rb, line 107
def self.generate_metric_name(prefix, key, callee, *args)
  name = key.respond_to?(:call) ? key.call(callee, args).gsub('::', '.') : key.gsub('::', '.')
  name = "#{prefix}.#{name}" if prefix
  name
end

Public Instance Methods

statsd_count(method, name, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil, sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg, prefix: nil, no_prefix: false, client: nil) click to toggle source

Adds counter instrumentation to a method.

The metric will be incremented for every call of the instrumented method, no matter whether what the method returns, or whether it raises an exception.

@param method (see statsd_measure) @param name (see statsd_measure) @param metric_options (see statsd_measure) @return [void]

Calls superclass method
# File lib/statsd/instrument.rb, line 287
def statsd_count(method, name, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
  sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg,
  prefix: nil, no_prefix: false, client: nil)

  add_to_method(method, name, :count) do
    define_method(method) do |*args, &block|
      client ||= StatsD.singleton_client
      prefix ||= client.prefix unless no_prefix
      key = StatsD::Instrument.generate_metric_name(prefix, name, self, *args)
      client.increment(key, sample_rate: sample_rate, tags: tags, no_prefix: true)
      super(*args, &block)
    end
  end
end
statsd_count_if(method, name, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil, sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg, prefix: nil, no_prefix: false, client: nil) { |result| ... } click to toggle source

Adds success counter instrumentation to a method.

A method call will be considered successful if it does not raise an exception, and the result is true-y. Only for successful calls, the metric will be incremented.

@param method (see statsd_measure) @param name (see statsd_measure) @yield (see statsd_count_success) @yieldparam result (see statsd_count_success) @yieldreturn (see statsd_count_success) @return [void] @see statsd_count_success

Calls superclass method
# File lib/statsd/instrument.rb, line 246
def statsd_count_if(method, name, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
  sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg,
  prefix: nil, no_prefix: false, client: nil)

  add_to_method(method, name, :count_if) do
    define_method(method) do |*args, &block|
      begin
        truthiness = result = super(*args, &block)
      rescue
        truthiness = false
        raise
      else
        if block_given?
          begin
            truthiness = yield(result)
          rescue
            truthiness = false
          end
        end
        result
      ensure
        if truthiness
          client ||= StatsD.singleton_client
          prefix ||= client.prefix unless no_prefix
          key = StatsD::Instrument.generate_metric_name(prefix, name, self, *args)
          client.increment(key, sample_rate: sample_rate, tags: tags, no_prefix: true)
        end
      end
    end
  end
end
statsd_count_success(method, name, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil, sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg, prefix: nil, no_prefix: false, client: nil) { |result| ... } click to toggle source

Adds success and failure counter instrumentation to a method.

A method call will be considered successful if it does not raise an exception, and the result is true-y. For successful calls, the metric [name].success will be incremented; for failed calls, the metric name is [name].failure.

@param method (see statsd_measure) @param name (see statsd_measure) @param metric_options (see statsd_measure) @yield You can pass a block to this method if you want to define yourself what is a successful call

based on the return value of the method.

@yieldparam result The return value of the instrumented method. @yieldreturn [Boolean] Return true iff the return value is considered a success, false otherwise. @return [void] @see statsd_count_if

Calls superclass method
# File lib/statsd/instrument.rb, line 202
def statsd_count_success(method, name, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
  sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg,
  prefix: nil, no_prefix: false, client: nil)

  add_to_method(method, name, :count_success) do
    define_method(method) do |*args, &block|
      begin
        truthiness = result = super(*args, &block)
      rescue
        truthiness = false
        raise
      else
        if block_given?
          begin
            truthiness = yield(result)
          rescue
            truthiness = false
          end
        end
        result
      ensure
        client ||= StatsD.singleton_client
        suffix = truthiness == false ? 'failure' : 'success'
        prefix ||= client.prefix unless no_prefix
        key = StatsD::Instrument.generate_metric_name(prefix, name, self, *args)
        client.increment("#{key}.#{suffix}",
          sample_rate: sample_rate, tags: tags, no_prefix: true)
      end
    end
  end
end
statsd_distribution(method, name, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil, sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg, prefix: nil, no_prefix: false, client: nil) click to toggle source

Adds execution duration instrumentation to a method as a distribution.

@param method [Symbol] The name of the method to instrument. @param name [String, call] The name of the metric to use. You can also pass in a

callable to dynamically generate a metric name

@param metric_options (see StatsD#measure) @return [void] @note Supported by the datadog implementation only (in beta)

Calls superclass method
# File lib/statsd/instrument.rb, line 171
def statsd_distribution(method, name, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
  sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg,
  prefix: nil, no_prefix: false, client: nil)

  add_to_method(method, name, :distribution) do
    define_method(method) do |*args, &block|
      client ||= StatsD.singleton_client
      prefix ||= client.prefix unless no_prefix
      key = StatsD::Instrument.generate_metric_name(prefix, name, self, *args)
      client.distribution(key, sample_rate: sample_rate, tags: tags, no_prefix: true) do
        super(*args, &block)
      end
    end
  end
end
statsd_instrumentations() click to toggle source

@private

# File lib/statsd/instrument.rb, line 96
def statsd_instrumentations
  if defined?(@statsd_instrumentations)
    @statsd_instrumentations
  elsif respond_to?(:superclass) && superclass.respond_to?(:statsd_instrumentations)
    superclass.statsd_instrumentations
  else
    @statsd_instrumentations = {}
  end
end
statsd_measure(method, name, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil, as_dist: false, sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg, prefix: nil, no_prefix: false, client: nil) click to toggle source

Adds execution duration instrumentation to a method as a timing.

@param method [Symbol] The name of the method to instrument. @param name [String, call] The name of the metric to use. You can also pass in a

callable to dynamically generate a metric name

@param metric_options (see StatsD#measure) @return [void]

Calls superclass method
# File lib/statsd/instrument.rb, line 142
def statsd_measure(method, name, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
  as_dist: false, sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg,
  prefix: nil, no_prefix: false, client: nil)

  if as_dist
    return statsd_distribution(method, name, # rubocop:disable StatsD/MetricPrefixArgument
      sample_rate: sample_rate, tags: tags, prefix: prefix, no_prefix: no_prefix)
  end

  add_to_method(method, name, :measure) do
    define_method(method) do |*args, &block|
      client ||= StatsD.singleton_client
      prefix ||= client.prefix unless no_prefix
      key = StatsD::Instrument.generate_metric_name(prefix, name, self, *args)
      client.measure(key, sample_rate: sample_rate, tags: tags, no_prefix: true) do
        super(*args, &block)
      end
    end
  end
end
statsd_remove_count(method, name) click to toggle source

Removes StatsD counter instrumentation from a method @param method [Symbol] The method to remove instrumentation from. @param name [String] The name of the metric that was used. @return [void] @see statsd_count

# File lib/statsd/instrument.rb, line 307
def statsd_remove_count(method, name)
  remove_from_method(method, name, :count)
end
statsd_remove_count_if(method, name) click to toggle source

Removes StatsD conditional counter instrumentation from a method @param method (see statsd_remove_count) @param name (see statsd_remove_count) @return [void] @see statsd_count_if

# File lib/statsd/instrument.rb, line 316
def statsd_remove_count_if(method, name)
  remove_from_method(method, name, :count_if)
end
statsd_remove_count_success(method, name) click to toggle source

Removes StatsD success counter instrumentation from a method @param method (see statsd_remove_count) @param name (see statsd_remove_count) @return [void] @see statsd_count_success

# File lib/statsd/instrument.rb, line 325
def statsd_remove_count_success(method, name)
  remove_from_method(method, name, :count_success)
end
statsd_remove_distribution(method, name) click to toggle source

Removes StatsD distribution instrumentation from a method @param method (see statsd_remove_count) @param name (see statsd_remove_count) @return [void] @see statsd_measure

# File lib/statsd/instrument.rb, line 343
def statsd_remove_distribution(method, name)
  remove_from_method(method, name, :distribution)
end
statsd_remove_measure(method, name) click to toggle source

Removes StatsD measure instrumentation from a method @param method (see statsd_remove_count) @param name (see statsd_remove_count) @return [void] @see statsd_measure

# File lib/statsd/instrument.rb, line 334
def statsd_remove_measure(method, name)
  remove_from_method(method, name, :measure)
end

Private Instance Methods

add_to_method(method, name, action, &block) click to toggle source
# File lib/statsd/instrument.rb, line 365
def add_to_method(method, name, action, &block)
  instrumentation_module = statsd_instrumentation_for(method, name, action)

  if instrumentation_module.method_defined?(method)
    raise ArgumentError, "Already instrumented #{method} for #{self.name}"
  end

  unless method_defined?(method) || private_method_defined?(method)
    raise ArgumentError, "could not find method #{method} for #{self.name}"
  end

  method_scope = method_visibility(method)

  instrumentation_module.module_eval(&block)
  instrumentation_module.send(method_scope, method)
  prepend(instrumentation_module) unless self < instrumentation_module
end
method_visibility(method) click to toggle source
# File lib/statsd/instrument.rb, line 387
def method_visibility(method)
  if private_method_defined?(method)
    :private
  elsif protected_method_defined?(method)
    :protected
  else
    :public
  end
end
remove_from_method(method, name, action) click to toggle source
# File lib/statsd/instrument.rb, line 383
def remove_from_method(method, name, action)
  statsd_instrumentation_for(method, name, action).send(:remove_method, method)
end
statsd_instrumentation_for(method, name, action) click to toggle source
# File lib/statsd/instrument.rb, line 353
def statsd_instrumentation_for(method, name, action)
  unless statsd_instrumentations.key?([method, name, action])
    mod = Module.new do
      define_singleton_method(:inspect) do
        "StatsD_Instrument_#{method}_for_#{action}_with_#{name}"
      end
    end
    @statsd_instrumentations = statsd_instrumentations.merge([method, name, action] => mod)
  end
  @statsd_instrumentations[[method, name, action]]
end