Yodeler

A generic instrumentation library thats supports reporting to multiple endpoints via pluggable backend adapters.

Spoutin' off noise to whoever is listening.

Build Status Test Coverage Code Climate

Installation

Add this line to your application's Gemfile:

gem 'yodeler', '~>0.1.2'

And then execute:

$ bundle

Or install it yourself as:

$ gem install yodeler

Usage

Configuration

Single endpoint

In config/initializers/yodeler.rb

Yodeler.configure do |client|
  # if no endpoint name is given, it defaults to :default
  client.adapter(:http) do |http|
    http.path = '/events'
    http.host = 'example.com'
    # http.port = 80
    # http.use_ssl = false
    # http.default_params = {}
  end

  # if no timestamp_format is set, defaults to UTC ISO8601
  client.timestamp_format = :iso8601
  #client.timestamp_format = :epoch
  #client.timestamp_format = -> { Time.now.whatever.you_feel_like! }
end

Multiple Endpoints

In config/initializers/yodeler.rb


Yodeler.configure do |client|
  client.endpoint(:sales_reporting).adapter(:http) do |http|
    http.path = '/events'
    http.host = 'sales.example.com'
  end

  client.endpoint(:devops_reporting).adapter(:http) do |http|
    http.path = '/events'
    http.host = 'devops.example.com'
  end

  # by default, the client dispatches to the first registered endpoint
  # you can change it to a different one
  # Alternatively you can dispatch to a set of endpoints when dispatching a metric
  #   Yodeler.gauge('users.count', 35, to: [:sales_reporting, :devops_reporting])
  client.default_endpoint_name = :devops_reporting
end

Full Configuration Example

In config/yodeler.yml

development:
  auth_token: SOSECUREZ
  sales_reporting:
    host: localhost
    port: 3030
  devops_reporting:
    host: localhost
    port: 3031    

In config/initializers/yodeler.rb

config = YAML.load(File.read("./config/yodeler.yml"))[Rails.env]

Yodeler.configure do |client|
  client.endpoint(:sales_reporting).adapter(:http) do |http|
    http.path = '/events'
    http.host = config[:sales_reporting][:host]
    http.port = config[:sales_reporting][:port]
    http.default_params = {
      auth_token: config[:auth_token]
    }
  end

  client.endpoint(:devops_reporting).adapter(:http) do |http|
    http.path = '/events'
    http.host = config[:devops_reporting][:host]
    http.port = config[:devops_reporting][:port]
    http.default_params = {
      auth_token: config[:auth_token]
    }

    # Overwrite the default http dispatcher or overwrite an individual metric dispatcher
    #   http.handle(:gauge){ |url, metric, default_params| ... something cool ... }
    http.handle(:default) do |url, metric, default_params|
      # This is the default handler definition, but you could change it
      HTTP.post(url, json: default_params.merge(metric.to_hash))
    end
  end

  client.default_endpoint_name = :devops_reporting
end

Dashing Example

Yodeler.configure do |client|
  client.endpoint(:karma_widget).adapter(:http) do |http|
    http.path = '/widgets/karma'
    http.host = 'localhost'
    http.default_params = {
      auth_token: config[:auth_token]
    }
  end

  client.endpoint(:user_count_widget).adapter(:http) do |http|
    http.path = '/widgets/user_count'
    http.host = 'localhost'
    http.default_params = {
      auth_token: config[:auth_token]
    }
  end
end

Publishing Metrics and Events

All instrumentation methods support an options hash

  • :prefix - [~String] :prefix your metric/event names
  • :tags - [Array, String, Symbol] :tags ([]) array of tags to apply to metric/event
  • :sample_rate - [Float] :sample_rate (1.0) The sample rate to use
  • :to - [Array, Symbol] :to array of endpoint names to send the metric/event to. If not set will send to Yodeler::Client#default_endpoint_name
  • :meta - [Hash] :meta additional meta data to send with metric/event. Meta data like :tags, :hostname and :timestamp are merged into this hash

Gauge

Yodeler.gauge 'user.count', 35
Yodeler.gauge 'user.count', 35, prefix: 'test'
Yodeler.gauge 'user.count', 35, sample_rate: 0.5
Yodeler.gauge 'user.count', 35, prefix: 'test', tags: ['cool']
Yodeler.gauge 'user.count', 35, to: [:devops_reporting, :sales_reporting]

Increment

Yodeler.increment 'users.count'
Yodeler.increment 'users.count', to: [:devops_reporting, :sales_reporting]
Yodeler.increment 'revenue', 10_000
Yodeler.increment 'revenue', 10_000, to: [:devops_reporting, :sales_reporting]
Yodeler.increment 'revenue', 10_000, to: [:devops_reporting, :sales_reporting], meta: {ip: request.remote_ip}

Timing

Yodeler.timing('eat.sandwich', {prefix: :test}) do
  user.eat(sandwich)
end #=> returns result of block

Yodeler.timing 'eat.sandwich', 250 #in ms
Yodeler.timing 'eat.sandwich', 250, to: [:devops_reporting, :sales_reporting]

Event

wizz_bang = {name: 'Wizz Bang 3000', image_url: 'http://example.com/wizzbang.jpg'}
Yodeler.publish 'product.sold', wizz_bang
Yodeler.publish 'product.sold', wizz_bang, prefix: 'ecommerce'
Yodeler.publish 'product.sold', wizz_bang, sample_rate: 0.25
Yodeler.publish 'product.sold', wizz_bang, to: [:devops_reporting, :sales_reporting]
Yodeler.publish 'product.sold', wizz_bang, to: [:devops_reporting, :sales_reporting], meta: {ip: request.remote_ip}

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/coryodaniel/yodeler.

TODOs

  • [ ] Custom adapter documentation
  • [ ] Client#format_options -> Metric.format_options
  • [ ] Client#default_endpoint_name accept array of names
  • [ ] Dispatch to any object or proc, if adapter not registered
    • client.endpoint(:dashboard).use(:something_that_responds_to_dispatch)
    • client.endpoint(:dashboard).use{ |metric| MyWorker.perform_later(metric) }
  • [ ] more yard docs