yodeler 0.0.10 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +33 -0
  3. data/.gitignore +9 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +6 -0
  6. data/Gemfile +4 -0
  7. data/Guardfile +16 -0
  8. data/README.md +158 -67
  9. data/Rakefile +4 -21
  10. data/bin/console +14 -0
  11. data/bin/setup +7 -0
  12. data/lib/yodeler/adapter_not_registered_error.rb +14 -0
  13. data/lib/yodeler/adapters/http_adapter.rb +51 -0
  14. data/lib/yodeler/adapters/memory_adapter.rb +23 -0
  15. data/lib/yodeler/adapters/void_adapter.rb +10 -0
  16. data/lib/yodeler/client.rb +166 -0
  17. data/lib/yodeler/duplicate_endpoint_name_error.rb +9 -0
  18. data/lib/yodeler/endpoint.rb +35 -0
  19. data/lib/yodeler/metric.rb +36 -0
  20. data/lib/yodeler/version.rb +1 -1
  21. data/lib/yodeler.rb +41 -106
  22. data/yodeler.gemspec +29 -0
  23. metadata +44 -102
  24. data/MIT-LICENSE +0 -20
  25. data/app/assets/javascripts/yodeler/application.js +0 -13
  26. data/app/assets/stylesheets/yodeler/application.css +0 -13
  27. data/app/controllers/yodeler/application_controller.rb +0 -4
  28. data/app/helpers/yodeler/application_helper.rb +0 -4
  29. data/app/views/layouts/yodeler/application.html.erb +0 -14
  30. data/config/routes.rb +0 -2
  31. data/lib/generators/yodeler/USAGE +0 -2
  32. data/lib/generators/yodeler/install_generator.rb +0 -23
  33. data/lib/generators/yodeler/templates/initializer.rb +0 -7
  34. data/lib/generators/yodeler/templates/migration.rb +0 -50
  35. data/lib/tasks/yodeler_tasks.rake +0 -4
  36. data/lib/yodeler/configuration.rb +0 -12
  37. data/lib/yodeler/engine.rb +0 -10
  38. data/lib/yodeler/listens_to_yodeler.rb +0 -32
  39. data/lib/yodeler/models/event.rb +0 -17
  40. data/lib/yodeler/models/event_type.rb +0 -6
  41. data/lib/yodeler/models/event_types/base.rb +0 -59
  42. data/lib/yodeler/models/event_types/noop_event_type.rb +0 -12
  43. data/lib/yodeler/models/notification.rb +0 -30
  44. data/lib/yodeler/models/subscription.rb +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3e61910148974128f8852d2b671187a588150737
4
- data.tar.gz: 7b22137cdea54c279888833b91729b8eb85c7ae4
3
+ metadata.gz: fa2389240d853b1e917ca4741cd9d8ca0c9bde03
4
+ data.tar.gz: 43aae361bf5632fea1641a3914f2b5390759cffe
5
5
  SHA512:
6
- metadata.gz: 7dda2288ceb0a4e29d9f9927faefbb113f124c754a6e77318c29f980aa178ede37f3362b7e8b153b8f91f502fa0d7eb32193aee92665f484f831dc6184094a7a
7
- data.tar.gz: 88e3857b1b847f493d2fe8609d0e143d4f29f8cff66885ac68144e078dbbe8fbee8f95d6ae545e0b9eca63b173a1d6f29a2b6948ed8954037bef0b45bb2b2738
6
+ metadata.gz: a081b0689cd5e2f0f2a3c192a54832459a0935f020aa7392a23ec3e4d9771fdff89280b120e57219e14507c597f38266ce71ae6150f67118ff7459fb42cf33be
7
+ data.tar.gz: 7eb2cd49a592a382610031303a10a1eeb4313c2893aa1c5d40e48ce0d7446dd04af2cd119aefaf5d2a5860f21f6252e4fa3401f6ef804f57f4b41ce4c6b9f1a7
data/.codeclimate.yml ADDED
@@ -0,0 +1,33 @@
1
+ ---
2
+ engines:
3
+ brakeman:
4
+ enabled: false
5
+ bundler-audit:
6
+ enabled: true
7
+ duplication:
8
+ enabled: true
9
+ config:
10
+ languages:
11
+ - ruby
12
+ fixme:
13
+ enabled: true
14
+ rubocop:
15
+ enabled: true
16
+ ratings:
17
+ paths:
18
+ - Gemfile.lock
19
+ - "**.erb"
20
+ - "**.haml"
21
+ - "**.rb"
22
+ - "**.rhtml"
23
+ - "**.slim"
24
+ - "**.inc"
25
+ - "**.js"
26
+ - "**.jsx"
27
+ - "**.module"
28
+ - "**.php"
29
+ - "**.py"
30
+ exclude_paths:
31
+ - config/**/*
32
+ - db/**/*
33
+ - spec/**/*
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.6
4
+ - 2.2.3
5
+ - 2.3.0
6
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in yodeler.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,16 @@
1
+ guard :rspec, cmd: "bundle exec rspec" do
2
+ require "guard/rspec/dsl"
3
+ dsl = Guard::RSpec::Dsl.new(self)
4
+
5
+ # Feel free to open issues for suggestions and improvements
6
+
7
+ # RSpec files
8
+ rspec = dsl.rspec
9
+ watch(rspec.spec_helper) { rspec.spec_dir }
10
+ watch(rspec.spec_support) { rspec.spec_dir }
11
+ watch(rspec.spec_files)
12
+
13
+ # Ruby files
14
+ ruby = dsl.ruby
15
+ dsl.watch_spec_files_for(ruby.lib_files)
16
+ end
data/README.md CHANGED
@@ -1,106 +1,197 @@
1
- Yodeler
2
- =======
1
+ # Yodeler
3
2
 
4
- Spouting noise to whoever is listening.
3
+ A generic instrumentation library thats supports reporting to multiple endpoints via pluggable backend adapters.
5
4
 
6
- Yodeler is an easy way to notify users of different events that occur in your app.
5
+ Spoutin' off noise to whoever is listening.
7
6
 
8
- * Notify your marketing team on the 10,000 sign-up.
9
- * Notify user's when their profile has been viewed
10
- * Notify your dev team about events that are not quite exception worthy, but you don't want to dig through a bunch of logs to find.
7
+ ![Build Status](https://travis-ci.org/coryodaniel/yodeler.svg "Build Status")
8
+ [![Test Coverage](https://codeclimate.com/github/coryodaniel/yodeler/badges/coverage.svg)](https://codeclimate.com/github/coryodaniel/yodeler/coverage)
9
+ [![Code Climate](https://codeclimate.com/github/coryodaniel/yodeler/badges/gpa.svg)](https://codeclimate.com/github/coryodaniel/yodeler)
11
10
 
11
+ ## Installation
12
12
 
13
- Features
14
- ========
13
+ Add this line to your application's Gemfile:
15
14
 
16
- * store notifications via ActiveRecord
17
- * benchmark event duration
18
- * i18n support
19
- * flexible payloads
20
-
21
-
22
- What's wrong with XMPP?
23
- =======================
15
+ ```ruby
16
+ gem 'yodeler', '~>0.1.1'
17
+ ```
24
18
 
25
- Why don't you fly to Walmart on a rocket?
19
+ And then execute:
26
20
 
27
- Don't even say it's because you don't have a rocket.
21
+ $ bundle
28
22
 
29
- Why don't you kill ants with a bazooka?
23
+ Or install it yourself as:
30
24
 
31
- Nevermind, you get it.
25
+ $ gem install yodeler
32
26
 
27
+ ## Usage
33
28
 
34
- Usage
35
- =====
29
+ ### Configuration
36
30
 
31
+ #### Single endpoint
32
+ In ```config/initializers/yodeler.rb```
37
33
  ```ruby
38
- # in config/initializers/yodeler.rb
39
- Yodeler.register :another_thousand_users
40
-
41
- Yodeler.register :view_user do
42
- # Default states are unread: 0, read: 1
43
- config.states = {
44
- unread: 0,
45
- read: 1,
46
- ignored: 2
47
- }
34
+ Yodeler.configure do |client|
35
+ # if no endpoint name is given, it defaults to :default
36
+ client.adapter(:http) do |http|
37
+ http.path = '/events'
38
+ http.host = 'example.com'
39
+ # http.port = 80
40
+ # http.use_ssl = false
41
+ # http.default_params = {}
42
+ end
48
43
  end
49
44
  ```
50
45
 
46
+ #### Multiple Endpoints
47
+ In ```config/initializers/yodeler.rb```
51
48
  ```ruby
52
- # in a controller or Anywhere you want to hear some noise
53
- class UserController < ApplicationController
54
- around_action :track_user_view, only: :show
55
-
56
- def show
57
- # do your User#show as normal
49
+
50
+ Yodeler.configure do |client|
51
+ client.endpoint(:sales_reporting).adapter(:http) do |http|
52
+ http.path = '/events'
53
+ http.host = 'sales.example.com'
58
54
  end
59
55
 
60
- private
56
+ client.endpoint(:devops_reporting).adapter(:http) do |http|
57
+ http.path = '/events'
58
+ http.host = 'devops.example.com'
59
+ end
61
60
 
62
- def track_user_view
63
- # This will also benchmark the action since it is in a block
64
- Yodeler.dispatch :view_user, {viewer_id: 3, viewee_id: 23} do |payload|
65
- yield #yield the action dispatch, the payload is available
61
+ # by default, the client dispatches to the first registered endpoint
62
+ # you can change it to a different one
63
+ # Alternatively you can dispatch to a set of endpoints when dispatching a metric
64
+ # Yodeler.gauge('users.count', 35, to: [:sales_reporting, :devops_reporting])
65
+ client.default_endpoint_name = :devops_reporting
66
+ end
67
+ ```
68
+
69
+ #### Full Configuration Example
70
+ In ```config/yodeler.yml```
71
+ ```yaml
72
+ development:
73
+ auth_token: SOSECUREZ
74
+ sales_reporting:
75
+ host: localhost
76
+ port: 3030
77
+ devops_reporting:
78
+ host: localhost
79
+ port: 3031
80
+ ```
81
+
82
+ In ```config/initializers/yodeler.rb```
83
+ ```ruby
84
+ config = YAML.load(File.read("./config/yodeler.yml"))[Rails.env]
85
+
86
+ Yodeler.configure do |client|
87
+ client.endpoint(:sales_reporting).adapter(:http) do |http|
88
+ http.path = '/events'
89
+ http.host = config[:sales_reporting][:host]
90
+ http.port = config[:sales_reporting][:port]
91
+ http.default_params = {
92
+ auth_token: config[:auth_token]
93
+ }
94
+ end
95
+
96
+ client.endpoint(:devops_reporting).adapter(:http) do |http|
97
+ http.path = '/events'
98
+ http.host = config[:devops_reporting][:host]
99
+ http.port = config[:devops_reporting][:port]
100
+ http.default_params = {
101
+ auth_token: config[:auth_token]
102
+ }
103
+
104
+ # Overwrite the default http dispatcher or overwrite an individual metric dispatcher
105
+ # http.handle(:gauge){|url, metric, default_params| ... something cool ... }
106
+ http.handle(:default) do |url, metric, default_params|
107
+ # This is the default handler definition, but you could change it
108
+ HTTP.post(url, json: default_params.merge(metric.to_hash))
66
109
  end
67
110
  end
111
+
112
+ client.default_endpoint_name = :devops_reporting
68
113
  end
114
+
69
115
  ```
70
116
 
117
+ #### [Dashing Example](https://github.com/shopify/dashing)
71
118
  ```ruby
72
- # off in some scorned rails-observer
73
- class UserObserver
74
- observers :user
75
-
76
- def after_create
77
- # let the marketing know another thousandth user has registered!
78
- if (User.count % 1000).zero?
79
- Yodeler.dispatch :another_thousand_users, {give_him_a_prize: @user.id}
80
- end
119
+ Yodeler.configure do |client|
120
+ client.endpoint(:karma_widget).adapter(:http) do |http|
121
+ http.path = '/widgets/karma'
122
+ http.host = 'localhost'
123
+ http.default_params = {
124
+ auth_token: config[:auth_token]
125
+ }
126
+ end
127
+
128
+ client.endpoint(:user_count_widget).adapter(:http) do |http|
129
+ http.path = '/widgets/user_count'
130
+ http.host = 'localhost'
131
+ http.default_params = {
132
+ auth_token: config[:auth_token]
133
+ }
81
134
  end
82
135
  end
83
136
  ```
84
137
 
138
+ ### Publishing Metrics and Events
85
139
 
86
- Getting around the objects
87
- ==========================
140
+ #### All instrumentation methods support an options hash
88
141
 
142
+ * :prefix - [~String] :prefix your metric/event names
143
+ * :tags - [Array<String,Symbol>, String, Symbol] :tags ([]) array of tags to apply to metric/event
144
+ * :sample_rate - [Float] :sample_rate (1.0) The sample rate to use
145
+ * :to - [Array<Symbol>, Symbol] :to array of endpoint names to send the metric/event to. If not set will send to Yodeler::Client#default_endpoint_name
146
+
147
+ #### Gauge
89
148
  ```ruby
90
- @event.subscriptions #=> Array<Yodeler::Subscription> all subscriber subscriptions
91
- @event.notifications #=> Array<Yodeler::Notifications>
149
+ Yodeler.gauge 'user.count', 35
150
+ Yodeler.gauge 'user.count', 35, prefix: 'test'
151
+ Yodeler.gauge 'user.count', 35, sample_rate: 0.5
152
+ Yodeler.gauge 'user.count', 35, prefix: 'test', tags: ['cool']
153
+ Yodeler.gauge 'user.count', 35, to: [:devops_reporting, :sales_reporting]
154
+ ```
92
155
 
93
- @notification.subscriber #=> Your 'subscriber' class, delegated to #subscription
94
- @notification.subscription.subscriber
95
- @notification.event #=> The subscriber to event, delegated to #subscription
96
- @notification.message #=> i18n message interpolated w/ the payload
156
+ #### Increment
157
+ ```ruby
158
+ Yodeler.increment 'users.count'
159
+ Yodeler.increment 'users.count', to: [:devops_reporting, :sales_reporting]
160
+ Yodeler.increment 'revenue', 10_000
161
+ Yodeler.increment 'revenue', 10_000, to: [:devops_reporting, :sales_reporting]
162
+
163
+ ```
97
164
 
98
- @subscription.subscriber #=> the 'subscriber' subscribed to this event
99
- @subscription.notifications #=> all notifications of this event type for this subscriber
100
- @subscription.event
165
+ #### Timing
166
+ ```ruby
167
+ Yodeler.timing('eat.sandwich', {prefix: :test}) do
168
+ user.eat(sandwich)
169
+ end #=> returns result of block
101
170
 
171
+ Yodeler.timing 'eat.sandwich', 250 #in ms
172
+ Yodeler.timing 'eat.sandwich', 250, to: [:devops_reporting, :sales_reporting]
102
173
  ```
103
174
 
104
- TODO
105
- =======
106
- * Pluggable back-end, redis, obvi
175
+ #### Event
176
+ ```ruby
177
+ wizz_bang = {name: 'Wizz Bang 3000', image_url: 'http://example.com/wizzbang.jpg'}
178
+ Yodeler.publish 'product.sold', wizz_bang
179
+ Yodeler.publish 'product.sold', wizz_bang, prefix: 'ecommerce'
180
+ Yodeler.publish 'product.sold', wizz_bang, sample_rate: 0.25
181
+ Yodeler.publish 'product.sold', wizz_bang, to: [:devops_reporting, :sales_reporting]
182
+ ```
183
+
184
+ ## Development
185
+
186
+ 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.
187
+
188
+ 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](https://rubygems.org).
189
+
190
+ ## Contributing
191
+
192
+ Bug reports and pull requests are welcome on GitHub at https://github.com/coryodaniel/yodeler.
193
+
194
+ ## TODOs
195
+ * [ ] Custom adapter documentation
196
+ * [ ] Client#format_options -> Metric.format_options
197
+ * [ ] Client#default_endpoint_name accept array of names
data/Rakefile CHANGED
@@ -1,23 +1,6 @@
1
- begin
2
- require 'bundler/setup'
3
- rescue LoadError
4
- puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
- end
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
6
3
 
7
- require 'rdoc/task'
8
-
9
- RDoc::Task.new(:rdoc) do |rdoc|
10
- rdoc.rdoc_dir = 'rdoc'
11
- rdoc.title = 'Yodeler'
12
- rdoc.options << '--line-numbers'
13
- rdoc.rdoc_files.include('README.rdoc')
14
- rdoc.rdoc_files.include('lib/**/*.rb')
15
- end
16
-
17
- APP_RAKEFILE = File.expand_path("../spec/test_app/Rakefile", __FILE__)
18
- load 'rails/tasks/engine.rake'
19
-
20
-
21
-
22
- Bundler::GemHelper.install_tasks
4
+ RSpec::Core::RakeTask.new(:spec)
23
5
 
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "yodeler"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,14 @@
1
+ module Yodeler
2
+ class AdapterNotRegisteredError < StandardError
3
+ attr_reader :name
4
+ def initialize(name:)
5
+ @name = name
6
+ msg = [
7
+ "No Yodeler Adapter registed for: ':#{name}'",
8
+ "Register an adapter with:",
9
+ "Yodeler.register_adapter(:#{name}, CustomAdapterClass)"
10
+ ].join("\n")
11
+ super(msg)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,51 @@
1
+ require 'http'
2
+
3
+ module Yodeler::Adapters
4
+ class HttpAdapter
5
+ attr_accessor :host
6
+ attr_accessor :port
7
+ attr_accessor :path
8
+ attr_accessor :use_ssl
9
+ attr_accessor :default_params
10
+
11
+ def initialize(host=nil, port:nil, path:nil, use_ssl:false, params:{})
12
+ @host = host
13
+ @port = port
14
+ @path = path
15
+ @use_ssl = use_ssl
16
+ @default_params = params
17
+ @handlers = {}
18
+
19
+ handle(:default) do |url, metric, default_params|
20
+ HTTP.post(url, json: default_params.merge(metric.to_hash))
21
+ end
22
+ end
23
+
24
+ def handle(type, &block)
25
+ @handlers[type] = block
26
+ end
27
+
28
+ def dispatch(metric)
29
+ (@handlers[metric.type] || @handlers[:default]).call(url, metric, default_params)
30
+ end
31
+
32
+ def url
33
+ "#{protocol}://#{host_with_port}#{path}"
34
+ end
35
+
36
+ private
37
+ def host_with_port
38
+ if port
39
+ "#{host}:#{port}"
40
+ else
41
+ host
42
+ end
43
+ end
44
+
45
+ def protocol
46
+ use_ssl ? :https : :http
47
+ end
48
+
49
+ Yodeler.register_adapter(:http, self)
50
+ end
51
+ end
@@ -0,0 +1,23 @@
1
+ module Yodeler::Adapters
2
+ class MemoryAdapter
3
+ attr_reader :queue
4
+ attr_accessor :max_queue_size
5
+
6
+ def initialize
7
+ @max_queue_size = 1000
8
+ flush!
9
+ end
10
+
11
+ def flush!
12
+ @queue = []
13
+ end
14
+
15
+ def dispatch(metric)
16
+ @queue << metric
17
+ @queue.shift if @queue.length > @max_queue_size
18
+ metric
19
+ end
20
+
21
+ Yodeler.register_adapter(:memory, self)
22
+ end
23
+ end
@@ -0,0 +1,10 @@
1
+ module Yodeler::Adapters
2
+ class VoidAdapter
3
+
4
+ def dispatch(*)
5
+ #noop
6
+ end
7
+
8
+ Yodeler.register_adapter(:void, self)
9
+ end
10
+ end
@@ -0,0 +1,166 @@
1
+ require 'socket'
2
+
3
+ module Yodeler
4
+ class Client
5
+ attr_accessor :default_endpoint_name
6
+ attr_accessor :default_prefix
7
+ attr_accessor :default_sample_rate
8
+ attr_reader :endpoints
9
+
10
+ def initialize
11
+ @endpoints = {}
12
+ @default_sample_rate = 1.0
13
+ @default_prefix = nil
14
+ @hostname = Socket.gethostname
15
+ end
16
+
17
+ # Register a new endpoint
18
+ #
19
+ # @param [Symbol|String] name of endpoint, must be unique
20
+ # @return [Yodeler::Endpoint]
21
+ def endpoint(name=:default, &block)
22
+ raise DuplicateEndpointNameError.new(name: name) if @endpoints[name]
23
+ @default_endpoint_name ||= name
24
+ @endpoints[name] = Endpoint.new(name,&block)
25
+ end
26
+
27
+ # Get the default endpoint
28
+ #
29
+ # @return [Yodeler::Endpoint] Get the default endpoint
30
+ def default_endpoint
31
+ @endpoints[default_endpoint_name]
32
+ end
33
+
34
+ # Syntax sugar for creating the default endpoint and set the adapter
35
+ #
36
+ # Useful if you just have one endpoint and don't care about its name
37
+ # like during testing or for simple metric reporting scenarios
38
+ # ... is this useful or is it a big ol' booger?
39
+ #
40
+ # @param [Symbol] name registered adapter name
41
+ # @param [Type] &block configuration for adapter
42
+ # @return [~Yodeler::Adapters::Base] the adapter
43
+ def adapter(name ,&block)
44
+ endpoint() if @endpoints.empty?
45
+ default_endpoint.use(name, &block)
46
+ end
47
+
48
+ # Set a gauge
49
+ #
50
+ # @example
51
+ # client.gauge('users.count', 20_000_000)
52
+ # client.gauge('users.count', 20_000_000, { tags: %w(something) })
53
+ #
54
+ # @param [~String] name of the metric
55
+ # @param [~Fixnum] value of the metric
56
+ # @param [Hash] opts={} Examples {#format_options}
57
+ # @return [Yodeler::Metric, nil] the dispatched metric, nil if not sampled
58
+ def gauge(name, value, opts={})
59
+ dispatch(:gauge, name, value, opts)
60
+ end
61
+
62
+ # Increment a counter
63
+ #
64
+ # @example
65
+ # client.increment 'user.signup'
66
+ # client.increment 'user.signup', {}
67
+ # client.increment 'user.signup', 1, {}
68
+ #
69
+ # @param [~String] name of the metric
70
+ # @param [~Fixnum] value=1 of the metric
71
+ # @param [Hash] opts={} Examples {#format_options}
72
+ # @return [Yodeler::Metric, nil] the dispatched metric, nil if not sampled
73
+ def increment(name, value=1, opts={})
74
+ if value.kind_of?(Hash)
75
+ opts = value
76
+ value = 1
77
+ end
78
+ dispatch(:increment, name, value, opts)
79
+ end
80
+
81
+ # Measure how long something takes
82
+ #
83
+ # @example
84
+ # client.timing 'eat.sandwich', 250
85
+ # client.timing('eat.pizza') do
86
+ # user.eat(pizza) #=> THAT WAS QUICK FATSO!
87
+ # end
88
+ #
89
+ # @param [~String] name of the metric
90
+ # @param [~Fixnum] value time in ms
91
+ # @param [Hash] opts={} Examples {#format_options}
92
+ # @return [Yodeler::Metric, nil, Object]
93
+ # the dispatched metric, nil if not sampled
94
+ # if a block is given the result of the block is returned
95
+ def timing(name, value=nil, opts={})
96
+ if value.kind_of?(Hash)
97
+ opts = value
98
+ value = nil
99
+ end
100
+
101
+ _retval = nil
102
+
103
+ if block_given?
104
+ start = Time.now
105
+ _retval = yield
106
+ value = Time.now - start
107
+ end
108
+
109
+ metric = dispatch(:timing, name, value, opts)
110
+ _retval || metric
111
+ end
112
+
113
+ # Publish an event
114
+ #
115
+ # @example
116
+ # client.publish('item.sold', purchase.to_json)
117
+ # client.publish('user.sign_up', {name: user.name, avatar: user.image_url})
118
+ #
119
+ # @param [~String] name of the metric
120
+ # @param [~Hash] value of the metric
121
+ # @param [Hash] opts={} Examples {#format_options}
122
+ # @return [Yodeler::Metric, nil] the dispatched metric, nil if not sampled
123
+ def publish(name, payload, opts={})
124
+ dispatch(:event, name, payload, opts)
125
+ end
126
+
127
+ # Formats/Defaults metric options
128
+ #
129
+ # @param [Hash] opts metric options
130
+ # @option opts [Array<String,Symbol>, String, Symbol] :tags ([]) array of tags to apply to metric/event
131
+ # @option opts [Float] :sample_rate (1.0) The sample rate to use
132
+ # @option opts [Array<Symbol>, Symbol] :to array of endpoint names to send the metric to. If not set will send to {Yodeler::Client#default_endpoint_name}
133
+ # @return [Hash] formatted, defaulted options
134
+ def format_options(opts)
135
+ endpoint_names = opts.delete(:to) || [default_endpoint_name]
136
+ tags = opts.delete(:tags)
137
+ prefix = opts.delete(:prefix) || default_prefix
138
+
139
+ {
140
+ prefix: prefix,
141
+ to: [endpoint_names].flatten.compact,
142
+ sample_rate: opts.delete(:sample_rate) || default_sample_rate,
143
+ tags: [tags].flatten.compact,
144
+ hostname: @hostname
145
+ }
146
+ end
147
+
148
+ private
149
+
150
+ def dispatch(type, name, value, opts)
151
+ opts = format_options(opts)
152
+ destinations = opts.delete(:to)
153
+ metric = Metric.new(type, name, value, opts)
154
+
155
+ return nil unless metric.sample?
156
+
157
+ destinations.each do |endpoint_name|
158
+ if @endpoints[endpoint_name]
159
+ @endpoints[endpoint_name].adapter.dispatch(metric)
160
+ end
161
+ end
162
+
163
+ metric
164
+ end
165
+ end
166
+ end