Project

ddmetrics

0.01
Low commit activity in last 3 years
A long-lived project that still receives updates
Non-timeseries measurements for Ruby programs
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

Gem version Gem downloads Build status Code Climate

DDMetrics

DDMetrics is a Ruby library for recording and analysing measurements in short-running Ruby processes.

If you are looking for a full-featured timeseries monitoring system, look no further than Prometheus.

Example

Take the following (naïve) cache implementation as a starting point:

class Cache
  def initialize
    @map = {}
  end

  def []=(key, value)
    @map[key] = value
  end

  def [](key)
    @map[key]
  end
end

To start instrumenting this code, require ddmetrics, create a counter, and record some metrics:

require 'ddmetrics'

class Cache
  attr_reader :counter

  def initialize
    @map = {}
    @counter = DDMetrics::Counter.new
  end

  def []=(key, value)
    @counter.increment(type: :set)

    @map[key] = value
  end

  def [](key)
    if @map.key?(key)
      @counter.increment(type: :get_hit)
    else
      @counter.increment(type: :get_miss)
    end

    @map[key]
  end
end

Let’s construct a cache and exercise it:

cache = Cache.new

cache['greeting']
cache['greeting']
cache['greeting'] = 'Hi there!'
cache['greeting']
cache['greeting']
cache['greeting']

Finally, get the recorded values:

cache.counter.get(type: :set)      # => 1
cache.counter.get(type: :get_hit)  # => 3
cache.counter.get(type: :get_miss) # => 2

Or even print all stats:

puts cache.counter
    type │ count
─────────┼──────
 get_hit │     3
get_miss │     2
     set │     1

Scope

  • No timeseries: Metrics are not recorded over time. If you want to record timeseries data, consider using Prometheus.

  • Not intended for long-running processes: Metrics data (particularly summary metrics) can accumulate in memory and cause memory pressure. This project is not suited for long-running processes, such as servers. For monitoring long-running processes, consider using Prometheus.

  • Not thread-safe: The implementation is not thread-safe. If you require thread safety, consider wrapping the functionality provided.

Installation

Add this line to your application's Gemfile:

gem 'ddmetrics'

And then execute:

$ bundle

Or install it yourself as:

$ gem install ddmetrics

Usage

DDMetrics provides two metric types:

  • A counter is an integer metric that only ever increases. Examples: cache hits, number of files written, …

  • A summary records observations, and provides functionality for describing the distribution of the observations through quantiles. Examples: outgoing request durations, size of written files, …

Each metric is recorded with a label, which is a hash that is useful to further refine the kind of data that is being recorded. For example:

cache_hits_counter.increment(target: :file_cache)
request_durations_summary.observe(1.07, api: :weather)

Counters

To create a counter, instantiate DDMetrics::Counter:

counter = DDMetrics::Counter.new

To increment a counter, call #increment with a label:

counter.increment(target: :file_cache)

To get the value for a certain label, use #get:

counter.get(target: :file_cache)
# => 1

Summaries

To create a summary, instantiate DDMetrics::Summary:

summary = DDMetrics::Summary.new

To observe a value, call #observe with the value to observe, along with a label:

summary.observe(0.88, api: :weather)
summary.observe(1.07, api: :weather)
summary.observe(0.91, api: :weather)

To get the list of observations for a certain label, use #get, which will return a DDMetrics::Stats instance:

summary.get(api: :weather)
# => <DDMetrics::Stats>

The following methods are available on DDMetrics::Stats:

  • #count: returns the number of values
  • #sum: returns the sum of all values
  • #avg: returns the average of all values
  • #min: returns the minimum value
  • #max: returns the maximum value
  • #quantile(fraction): returns the quantile at the given fraction (0.0 – 1.0)

Printing metrics

To print a metric, use #to_s. For example:

summary = DDMetrics::Summary.new

summary.observe(2.1, filter: :erb)
summary.observe(4.1, filter: :erb)
summary.observe(5.3, filter: :haml)

puts summary

Output:

filter │ count    min    .50    .90    .95    max    tot
───────┼────────────────────────────────────────────────
   erb │     2   2.10   3.10   3.90   4.00   4.10   6.20
  haml │     1   5.30   5.30   5.30   5.30   5.30   5.30

Stopwatch

The DDMetrics::Stopwatch class can be used to measure durations. Use #start and #stop to start and stop the stopwatch, respectively, and #duration to read the value of the stopwatch:

stopwatch = DDMetrics::Stopwatch.new

stopwatch.start
sleep 1
stopwatch.stop
puts "That took #{stopwatch.duration}s."
# Output: That took 1.006831s.

A stopwatch, once created, will never reset its duration. Running the stopwatch again will add to the existing duration:

stopwatch.start
sleep 1
stopwatch.stop
puts "That took #{stopwatch.duration}s."
# Output: That took 2.012879s.

You can use #run with a block for convenience:

stopwatch.run do
  sleep 1
end

#run will return the value of the block:

result = stopwatch.run do
  sleep 1
  'Donkey'
end
puts result
# Output: Donkey

You can query whether or not a stopwatch is running using #running?; #stopped? is the opposite of #running?.

Development

Install dependencies:

$ bundle

Run tests:

$ bundle exec rake

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/ddfreyne/ddmetrics. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the DDMetrics project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.