harness 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ log
data/README.md CHANGED
@@ -7,6 +7,8 @@ redis before being sent to the service.
7
7
  Currently Supported Services:
8
8
 
9
9
  * Librato
10
+ * Statsd (thanks to fluxlux)
11
+ * Stathat
10
12
 
11
13
  Current Features:
12
14
 
@@ -45,20 +47,26 @@ end
45
47
 
46
48
  Add this line to your application's Gemfile:
47
49
 
48
- gem 'harness'
50
+ ```
51
+ gem 'harness'
52
+ ```
49
53
 
50
54
  And then execute:
51
55
 
52
- $ bundle
56
+ ```
57
+ $ bundle
58
+ ```
53
59
 
54
60
  Or install it yourself as:
55
61
 
56
- $ gem install harness
62
+ ```
63
+ $ gem install harness
64
+ ```
57
65
 
58
66
  ## Usage
59
67
 
60
68
  In the metrics world there are two types of things: Gauges and Counters.
61
- Gauges are time senstive and represent something at a specific point in
69
+ Gauges are time sensitive and represent something at a specific point in
62
70
  time. Counters keep track of things and should be increasing. Counters
63
71
  can be reset back to zero. You can combine counters and/or gauges to
64
72
  correlate data about your application. Meters monitor counters. They
@@ -94,7 +102,14 @@ stored in redis and incremented. This means you can simply pass
94
102
  may also pass `:counter => 5` if you'd like to provide your own value.
95
103
  This value is stored in redis so the next time `:counter => true` will
96
104
  work correctly. You can reset all the counters back to zero by calling:
97
- `Harness.reset_counters!`.
105
+ `Harness.reset_counters!`.
106
+
107
+ **NOTE**: You should use the bundled rake task to reset counters with
108
+ a cron job. This will prevent unbounded growth of this metadata. You
109
+ can call `rake harness:reset_counters` to do this. You should call
110
+ this rake task at whatever your longest measurable interval is. Here's
111
+ an example: You log gauges every 12 hours. You should reset the
112
+ counters every 12 hours. This issue is discussed [here](https://github.com/twinturbo/harness/issues/15).
98
113
 
99
114
  ```ruby
100
115
  class MyClass
@@ -106,14 +121,25 @@ class MyClass
106
121
  end
107
122
  ```
108
123
 
109
- The instuments name will be sent as the name (`important_method.my_class`)
124
+ The instruments name will be sent as the name (`important_method.my_class`)
110
125
  for that gauge or counter.
111
126
 
127
+ Note that `ActiveSupport::Notifications.instrument` doesn't require
128
+ a block. This can be useful when you are taking an instant measurement.
129
+
130
+ ```ruby
131
+ class MyClass
132
+ def important_method(stuff)
133
+ ActiveSupport::Notifications.instrument "important_method.my_class", :counter => true
134
+ end
135
+ end
136
+ ```
137
+
112
138
  Harness will do all the extra work in sending these metrics to whatever
113
139
  service you're using.
114
140
 
115
141
  Once you the counters are you are instrumented, then you can meter them.
116
- Meters allow you take arbitary readings of counter rates. The results
142
+ Meters allow you take arbitrary readings of counter rates. The results
117
143
  return a gauge so they can be logged as well.
118
144
 
119
145
  ```ruby
@@ -137,13 +163,18 @@ meter.per_hour
137
163
 
138
164
  ## Customizing
139
165
 
140
- You can pash a hash to `:counter` or `:gauge` to initialize the
166
+ You can pass a hash to `:counter` or `:gauge` to initialize the
141
167
  measurement your own way.
142
168
 
169
+ If you pass a value attribute to a gauge, it will be the value
170
+ sent instead of the duration of the block.
171
+
143
172
  ```ruby
144
173
  class MyClass
145
174
  def important_method(stuff)
146
- ActiveSupport::Notifications.instrument "important_method.my_class", :gauge => { :id => 'custom-id', :name => "My Measurement" } do
175
+ ActiveSupport::Notifications.instrument "important_method.my_class",
176
+ :gauge => { :id => 'custom-id', :name => "My Measurement",
177
+ :value => my_current_val, :units => 'cogs' } do
147
178
  do_important_stuff
148
179
  end
149
180
  end
@@ -152,10 +183,10 @@ end
152
183
 
153
184
  ## One Off Gauges and Counters
154
185
 
155
- You can instantiate `Harness::Counter` and `Harness::Guage` wherever you
186
+ You can instantiate `Harness::Counter` and `Harness::Gauge` wherever you
156
187
  want. Events from `ActiveSupport` are just converted to these classes
157
188
  under the covers anyways. You can use these class if you want to take
158
- peridocial measurements or tracking something that happens outside the
189
+ periodic measurements or tracking something that happens outside the
159
190
  application.
160
191
 
161
192
  ```ruby
@@ -174,21 +205,51 @@ counter.time # defaults to Time.now
174
205
  counter.value = read_total_users_in_database
175
206
  counter.log
176
207
 
177
- # Both class take an option hash
208
+ ### Both classes take an option hash
178
209
 
179
- gauge = Harness::Guage.new :time => Time.now, :id => 'foo.bar'
210
+ gauge = Harness::Gauge.new :time => Time.now, :id => 'foo.bar'
180
211
  counter = Harness::Counter.new :time => Time.now, :id => 'foo.bar'
181
212
  ```
182
213
 
183
214
  ## Configuration
184
215
 
216
+ ### Librato
185
217
  ```ruby
186
218
  Harness.config.adapter = :librato
187
219
 
188
220
  Harness.config.librato.email = 'example@example.com'
189
221
  Harness.config.librato.token = 'your-api-key'
190
222
 
191
- Harness.redis = Redis.new
223
+ ```
224
+
225
+ ### StatsD
226
+
227
+ Harness does **not** configure StatsD for you. It uses the StatsD class
228
+ under the covers. If you've already configured that in your own way, great.
229
+ If not, you can use the configuration proxy as described below. You
230
+ must also add `statsd-instrument` to your `Gemfile`. This is a soft
231
+ dependency that is not installed for you.
232
+
233
+ ```ruby
234
+ Harness.config.adapter = :statsd
235
+
236
+ # Harness.config.statsd is a proxy for the StatsD class
237
+ Harness.config.statsd.host = 'localhost'
238
+ Harness.config.statsd.port = '8080'
239
+ Harness.config.statsd.default_sample_rate = 0.1
240
+ Harness.config.statsd.logger = Rails.logger
241
+
242
+ # You can assign your own StatsD implementation
243
+ # by setting the "backend" attribute
244
+ Harness.config.statsd.backend = CustomStatsD
245
+ ```
246
+
247
+ ### Stathat
248
+
249
+ ```ruby
250
+ Harness.config.adapter = :stathat
251
+
252
+ Harness.config.stathat.ezkey = 'example@example.com'
192
253
  ```
193
254
 
194
255
  ## Rails Integration
@@ -209,8 +270,8 @@ You can configure Harness from `application.rb`
209
270
 
210
271
  ```ruby
211
272
  config.harness.adapter = :librato
212
- config.librato.email = 'example@example.com'
213
- config.librato.token = 'your-api-key'
273
+ config.harness.librato.email = 'example@example.com'
274
+ config.harness.librato.token = 'your-api-key'
214
275
  ```
215
276
 
216
277
  Redis will be automatically configured if you `REDISTOGO_URL` or
@@ -225,7 +286,7 @@ require 'erb'
225
286
  file = Rails.root.join 'config', 'resque.yml'
226
287
  config = YAML.load(ERB.new(File.read(Rails.root.join('config', 'redis.yml'))).result)
227
288
 
228
- Harness.redis = Redis.new(:url => config[Rails.env])
289
+ Harness.redis = Redis::Namespace.new('harness', :redis => Redis.connect(:url => config[Rails.env]))
229
290
  ```
230
291
 
231
292
  `rake harness:reset_counters` is also added.
@@ -239,10 +300,10 @@ logged in production.
239
300
  ### Background Processing
240
301
 
241
302
  Harness integrates automatically with Resque or Sidekiq. This is because
242
- reporting measurements can take time and add unncessary overhead to the
303
+ reporting measurements can take time and add unnecessary overhead to the
243
304
  response time. If neither of these libraries are present, measurements
244
305
  **will be posted in realtime.** You can set your own queue by
245
- specifiying a class like so:
306
+ specifying a class like so:
246
307
 
247
308
  ```ruby
248
309
  Harness.config.queue = MyCustomQueue
@@ -23,6 +23,10 @@ Gem::Specification.new do |gem|
23
23
  gem.add_development_dependency "webmock"
24
24
  gem.add_development_dependency "resque"
25
25
  gem.add_development_dependency "sidekiq"
26
+ gem.add_development_dependency "delayed_job_active_record"
26
27
  gem.add_development_dependency "active_model_serializers"
27
28
  gem.add_development_dependency "rails"
29
+ gem.add_development_dependency "sqlite3"
30
+ gem.add_development_dependency "minitest"
31
+ gem.add_development_dependency "statsd-instrument"
28
32
  end
@@ -26,7 +26,7 @@ module Harness
26
26
 
27
27
  def adapter=(val)
28
28
  if val.is_a? Symbol
29
- @adapter = "Harness::#{val.to_s.camelize}Adapter".constantize
29
+ @adapter = "Harness::#{val.to_s.camelize}Adapter".constantize.new
30
30
  else
31
31
  @adapter = val
32
32
  end
@@ -34,9 +34,9 @@ module Harness
34
34
 
35
35
  def queue=(val)
36
36
  if val.is_a? Symbol
37
- @queue= "Harness::#{val.to_s.camelize}Queue".constantize
37
+ @queue = "Harness::#{val.to_s.camelize}Queue".constantize.new
38
38
  else
39
- @queue= val
39
+ @queue = val
40
40
  end
41
41
  end
42
42
 
@@ -90,16 +90,13 @@ require 'harness/instrumentation'
90
90
 
91
91
  require 'harness/job'
92
92
 
93
- require 'harness/queues/syncronous_queue'
93
+ require 'harness/queues/synchronous_queue'
94
94
 
95
95
  require 'harness/adapters/librato_adapter'
96
96
  require 'harness/adapters/memory_adapter'
97
97
  require 'harness/adapters/null_adapter'
98
-
99
- require 'harness/integration/action_controller'
100
- require 'harness/integration/action_view'
101
- require 'harness/integration/action_mailer'
102
- require 'harness/integration/active_support'
98
+ require 'harness/adapters/statsd_adapter'
99
+ require 'harness/adapters/stathat_adapter'
103
100
 
104
101
  require 'harness/railtie' if defined?(Rails)
105
102
 
@@ -12,7 +12,7 @@ module Harness
12
12
  @config ||= Config.new
13
13
  end
14
14
 
15
- def self.log_gauge(gauge)
15
+ def log_gauge(gauge)
16
16
  raise Harness::LoggingError if gauge.id.length > 63
17
17
 
18
18
  post({:gauges => [{
@@ -25,7 +25,7 @@ module Harness
25
25
  }]})
26
26
  end
27
27
 
28
- def self.log_counter(counter)
28
+ def log_counter(counter)
29
29
  raise Harness::LoggingError if counter.id.length > 63
30
30
 
31
31
  post({:counters => [{
@@ -39,7 +39,7 @@ module Harness
39
39
  end
40
40
 
41
41
  private
42
- def self.post(params)
42
+ def post(params)
43
43
  unless config.email && config.token
44
44
  raise "Adapter not configured. Ensure email and token are set."
45
45
  end
@@ -68,7 +68,11 @@ module Harness
68
68
  true
69
69
  end
70
70
 
71
- def self.sanitize(name)
71
+ def config
72
+ self.class.config
73
+ end
74
+
75
+ def sanitize(name)
72
76
  if Harness.config.namespace
73
77
  key = "#{name}.#{Harness.config.namespace}"
74
78
  else
@@ -8,12 +8,20 @@ module Harness
8
8
  @counters ||= []
9
9
  end
10
10
 
11
- def self.log_gauge(gauge)
11
+ def log_gauge(gauge)
12
12
  gauges << gauge
13
13
  end
14
14
 
15
- def self.log_counter(counter)
15
+ def log_counter(counter)
16
16
  counters << counter
17
17
  end
18
+
19
+ def counters
20
+ self.class.counters
21
+ end
22
+
23
+ def gauges
24
+ self.class.gauges
25
+ end
18
26
  end
19
27
  end
@@ -1,10 +1,10 @@
1
1
  module Harness
2
2
  class NullAdapter
3
- def self.log_gauge(gauge)
3
+ def log_gauge(gauge)
4
4
 
5
5
  end
6
6
 
7
- def self.log_counter(counter)
7
+ def log_counter(counter)
8
8
 
9
9
  end
10
10
  end
@@ -0,0 +1,75 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+
4
+ module Harness
5
+ class StathatAdapter
6
+ class Config
7
+ attr_accessor :ezkey
8
+ end
9
+
10
+ def self.config
11
+ @config ||= Config.new
12
+ end
13
+
14
+ def log_gauge(gauge)
15
+ raise Harness::LoggingError if gauge.id.length > 255
16
+
17
+ post({
18
+ :stat => sanitize(gauge.id),
19
+ :value => gauge.value,
20
+ })
21
+ end
22
+
23
+ def log_counter(counter)
24
+ raise Harness::LoggingError if counter.id.length > 255
25
+
26
+ post({
27
+ :stat => sanitize(counter.id),
28
+ :count => counter.value,
29
+ })
30
+ end
31
+
32
+ private
33
+
34
+ def post(params)
35
+ unless config.ezkey
36
+ raise "Adapter not configured. Ensure ezkey is set."
37
+ end
38
+
39
+ uri = URI.parse('https://api.stathat.com/ez')
40
+
41
+ Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
42
+ request = Net::HTTP::Post.new uri.request_uri
43
+
44
+ request.set_form_data params.merge(:ezkey => config.ezkey)
45
+
46
+ response = http.request request
47
+
48
+ unless response.code.to_i == 200
49
+ text = %Q{
50
+ Server Said: #{response.body}
51
+ Sent: #{params.inspect}
52
+ }
53
+
54
+ raise Harness::LoggingError, text
55
+ end
56
+ end
57
+
58
+ true
59
+ end
60
+
61
+ def config
62
+ self.class.config
63
+ end
64
+
65
+ def sanitize(name)
66
+ if Harness.config.namespace
67
+ key = "#{name}.#{Harness.config.namespace}"
68
+ else
69
+ key = name
70
+ end
71
+
72
+ key.gsub('/', '.')
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,50 @@
1
+ require 'statsd-instrument'
2
+
3
+ module Harness
4
+ class StatsdAdapter
5
+ class Config
6
+ delegate :host, :port, :default_sample_rate, :mode, :logger, :to => :backend
7
+ delegate :host=, :port=, :default_sample_rate=, :mode=, :logger=, :to => :backend
8
+
9
+ def backend=(value)
10
+ @backend = value
11
+ end
12
+
13
+ def backend
14
+ @backend ||= StatsD
15
+ end
16
+ end
17
+
18
+ def self.config
19
+ @config ||= Config.new
20
+ end
21
+
22
+ def log_gauge(gauge)
23
+ validate!
24
+ backend.gauge sanitize(gauge.id), gauge.value
25
+ end
26
+
27
+ def log_counter(counter)
28
+ validate!
29
+ backend.increment sanitize(counter.id), counter.value
30
+ end
31
+
32
+ private
33
+ def validate!
34
+ raise "Adapter not configured. Ensure host and port are set." unless config.host and config.port
35
+ end
36
+
37
+ def sanitize(name)
38
+ key = Harness.config.namespace ? "#{Harness.config.namespace}.#{name}" : name
39
+ key.gsub(%r{[^a-z0-9]}, '.')
40
+ end
41
+
42
+ def backend
43
+ config.backend
44
+ end
45
+
46
+ def config
47
+ self.class.config
48
+ end
49
+ end
50
+ end
@@ -15,7 +15,7 @@ module Harness
15
15
  end
16
16
 
17
17
  gauge.id ||= event.name
18
- gauge.value = event.duration
18
+ gauge.value ||= event.duration
19
19
 
20
20
  gauge
21
21
  end
@@ -0,0 +1,7 @@
1
+ module Harness
2
+ class DelayedJobQueue
3
+ def push(measurement)
4
+ Harness::Job.new.delay(:queue => 'harness').log(measurement)
5
+ end
6
+ end
7
+ end
@@ -18,7 +18,7 @@ module Harness
18
18
  end
19
19
  end
20
20
 
21
- def self.push(measurement)
21
+ def push(measurement)
22
22
  if measurement.is_a? Gauge
23
23
  Resque.enqueue SendGauge, measurement.attributes
24
24
  elsif measurement.is_a? Counter
@@ -20,7 +20,7 @@ module Harness
20
20
  end
21
21
  end
22
22
 
23
- def self.push(measurement)
23
+ def push(measurement)
24
24
  if measurement.is_a? Gauge
25
25
  SendGauge.perform_async measurement.attributes
26
26
  elsif measurement.is_a? Counter
@@ -1,6 +1,6 @@
1
1
  module Harness
2
- class SyncronousQueue
3
- def self.push(measurement)
2
+ class SynchronousQueue
3
+ def push(measurement)
4
4
  begin
5
5
  Harness::Job.new.log(measurement)
6
6
  rescue LoggingError => ex
@@ -9,5 +9,10 @@ module Harness
9
9
  logger.warn "[Harness] Could not post measurement! Enable debug logging to see full errors"
10
10
  end
11
11
  end
12
+
13
+ private
14
+ def logger
15
+ Harness.logger
16
+ end
12
17
  end
13
18
  end
@@ -12,29 +12,26 @@ module Harness
12
12
  # Custom instrumentation can be turned on as follows
13
13
  # See files in lib/harness/integration for available integrations
14
14
  #
15
- # config.harness.insturment.sidekiq = true
16
- # config.harness.insturment.active_model_serializers = true
15
+ # config.harness.instrument.sidekiq = true
16
+ # config.harness.instrument.active_model_serializers = true
17
17
 
18
18
  rake_tasks do
19
19
  load "harness/tasks.rake"
20
20
  end
21
21
 
22
22
  initializer "harness.adapter" do |app|
23
- case Rails.env
24
- when 'development'
25
- app.config.harness.adapter = :null
26
- when 'test'
27
- app.config.harness.adapter = :null
28
- else
29
- app.config.harness.adapter = :librato
30
- end
23
+ app.config.harness.adapter ||= case Rails.env
24
+ when 'development' then :null
25
+ when 'test' then :null
26
+ else :librato
27
+ end
31
28
  end
32
29
 
33
30
  initializer "harness.logger" do |app|
34
31
  Harness.logger = Rails.logger
35
32
  end
36
33
 
37
- initializer "harness.redis" do
34
+ initializer "harness.redis" do
38
35
  if existing_url = ENV['REDISTOGO_URL'] || ENV['REDIS_URL']
39
36
  Harness.redis ||= Redis::Namespace.new('harness', :redis => Redis.connect(:url => existing_url))
40
37
  else
@@ -43,7 +40,7 @@ module Harness
43
40
  end
44
41
 
45
42
  initializer "harness.queue" do
46
- Harness.config.queue = Harness::SyncronousQueue
43
+ Harness.config.queue = :synchronous
47
44
  end
48
45
 
49
46
  initializer "harness.queue.production" do |app|
@@ -55,6 +52,9 @@ module Harness
55
52
  elsif defined?(Sidekiq::Worker) && use_real_queue
56
53
  require 'harness/queues/sidekiq_queue'
57
54
  Harness.config.queue = :sidekiq
55
+ elsif defined?(Delayed::Worker) && use_real_queue
56
+ require 'harness/queues/delayed_job_queue'
57
+ Harness.config.queue = :delayed_job
58
58
  end
59
59
  end
60
60
 
@@ -1,3 +1,3 @@
1
1
  module Harness
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -33,6 +33,7 @@ class ActiveSupportIntegration < IntegrationTest
33
33
 
34
34
  private
35
35
  def instrument(event)
36
+ require 'harness/integration/active_support'
36
37
  ActiveSupport::Notifications.instrument "#{event}.active_support" do |*args|
37
38
  # nada
38
39
  end
@@ -0,0 +1,59 @@
1
+ require 'test_helper'
2
+ require 'harness/queues/delayed_job_queue'
3
+ require 'fileutils'
4
+
5
+ class DelayedJobTest < IntegrationTest
6
+ def setup
7
+ super
8
+
9
+ tmp = File.expand_path('../../../tmp', __FILE__)
10
+ db = File.join(tmp, 'db.sqlite3')
11
+
12
+ FileUtils.mkdir_p tmp
13
+ FileUtils.rm_f db
14
+
15
+ ActiveRecord::Base.establish_connection(
16
+ :adapter => "sqlite3",
17
+ :database => db
18
+ )
19
+
20
+ Class.new(ActiveRecord::Migration) do
21
+ def self.up
22
+ suppress_messages do
23
+ create_table :delayed_jobs, :force => true do |table|
24
+ table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue
25
+ table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually.
26
+ table.text :handler # YAML-encoded string of the object that will do work
27
+ table.text :last_error # reason for last failure (See Note below)
28
+ table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.
29
+ table.datetime :locked_at # Set when a client is working on this object
30
+ table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
31
+ table.string :locked_by # Who is working on this object (if locked)
32
+ table.string :queue # The name of the queue this job is in
33
+ table.timestamps
34
+ end
35
+
36
+ add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority'
37
+ end
38
+ end
39
+ end.up
40
+
41
+ Delayed::Worker.delay_jobs = false
42
+
43
+ Harness.config.queue = :delayed_job
44
+ end
45
+
46
+ def test_a_gauge_is_logged
47
+ instrument "test-gauge", :gauge => true
48
+
49
+ assert_gauge_logged "test-gauge"
50
+ end
51
+
52
+ def test_a_counter_is_logged
53
+ instrument "test-counter", :counter => true
54
+
55
+ assert_counter_logged "test-counter"
56
+
57
+ assert_equal 1, counters.first.value
58
+ end
59
+ end
@@ -19,4 +19,8 @@ class RailtieTest < MiniTest::Unit::TestCase
19
19
  assert app.config.harness.instrument.action_view
20
20
  refute app.config.harness.instrument.active_support
21
21
  end
22
+
23
+ def test_configures_queue
24
+ assert_kind_of Harness::SynchronousQueue, app.config.harness.queue
25
+ end
22
26
  end
@@ -5,6 +5,8 @@ SimpleCov.start
5
5
 
6
6
  require 'resque'
7
7
  require 'sidekiq'
8
+ require 'delayed_job_active_record'
9
+ require 'sqlite3'
8
10
 
9
11
  require 'harness'
10
12
 
@@ -23,7 +25,7 @@ Harness.redis = Redis::Namespace.new 'harness-test', :redis => Redis.connect(:ho
23
25
  class IntegrationTest < MiniTest::Unit::TestCase
24
26
  def setup
25
27
  Harness.config.adapter = :memory
26
- Harness.config.queue = :syncronous
28
+ Harness.config.queue = :synchronous
27
29
 
28
30
  gauges.clear ; counters.clear
29
31
  redis.flushall
@@ -58,7 +60,7 @@ class IntegrationTest < MiniTest::Unit::TestCase
58
60
  end
59
61
 
60
62
  def instrument(name, data = {})
61
- ActiveSupport::Notifications.instrument name, data do
63
+ ActiveSupport::Notifications.instrument name, data do
62
64
  # nothing
63
65
  end
64
66
  end
@@ -2,7 +2,7 @@ require 'test_helper'
2
2
 
3
3
  class LibratoAdapterTest < MiniTest::Unit::TestCase
4
4
  def setup
5
- @adapter = Harness::LibratoAdapter
5
+ @adapter = Harness::LibratoAdapter.new
6
6
 
7
7
  @gauge = Harness::Gauge.new
8
8
  @gauge.id = "fake-gauge"
@@ -66,7 +66,7 @@ class LibratoAdapterTest < MiniTest::Unit::TestCase
66
66
  assert_requested expected_request
67
67
  end
68
68
 
69
- def test_logging_gague_raises_an_exception
69
+ def test_logging_gauge_raises_an_exception
70
70
  stub_request(:post, %r{metrics}).to_return(:status => 500, :body => "message")
71
71
 
72
72
  assert_raises Harness::LoggingError do
@@ -5,7 +5,7 @@ class MemoryAdapterTest < MiniTest::Unit::TestCase
5
5
  Harness::MemoryAdapter.counters.clear
6
6
  Harness::MemoryAdapter.gauges.clear
7
7
 
8
- @adapter = Harness::MemoryAdapter
8
+ @adapter = Harness::MemoryAdapter.new
9
9
  end
10
10
 
11
11
  def test_log_gauge_adds_to_gauges
@@ -0,0 +1,143 @@
1
+ require 'test_helper'
2
+
3
+ class StathatAdapterTest < MiniTest::Unit::TestCase
4
+ def setup
5
+ @adapter = Harness::StathatAdapter.new
6
+
7
+ @gauge = Harness::Gauge.new
8
+ @gauge.id = "fake-gauge"
9
+ @gauge.name = "Fake Gauge"
10
+ @gauge.source = "minitest"
11
+ @gauge.time = Time.now
12
+ @gauge.value = "55"
13
+
14
+ @counter = Harness::Counter.new
15
+ @counter.id = "fake-counter"
16
+ @counter.name = "Fake Counter"
17
+ @counter.source = "minitest"
18
+ @counter.time = Time.now
19
+ @counter.value = "55"
20
+ @counter.units = :bytes
21
+
22
+ Harness::StathatAdapter.config.ezkey = token
23
+ Harness.config.namespace = nil
24
+ end
25
+
26
+ def test_gauge_is_logged
27
+ args = {
28
+ :stat => @gauge.id,
29
+ :ezkey => token,
30
+ :value => @gauge.value,
31
+ }
32
+
33
+ expected_request = stub_request(:post, "https://api.stathat.com/ez").
34
+ with(:body => args, :headers => {'Content-Type'=>'application/x-www-form-urlencoded'}).
35
+ to_return(:status => 200)
36
+
37
+ assert @adapter.log_gauge(@gauge)
38
+ assert_requested expected_request
39
+ end
40
+
41
+ def test_gauge_is_logged_with_namespace
42
+ Harness.config.namespace = :foo
43
+
44
+ args = {
45
+ :stat => "#{@gauge.id}.foo",
46
+ :ezkey => token,
47
+ :value => @gauge.value,
48
+ }
49
+
50
+ expected_request = stub_request(:post, "https://api.stathat.com/ez").
51
+ with(:body => args, :headers => {'Content-Type'=>'application/x-www-form-urlencoded'}).
52
+ to_return(:status => 200)
53
+
54
+ assert @adapter.log_gauge(@gauge)
55
+ assert_requested expected_request
56
+ end
57
+
58
+ def test_logging_gauge_raises_an_exception
59
+ stub_request(:post, %r{stathat}).to_return(:status => 500, :body => "message")
60
+
61
+ assert_raises Harness::LoggingError do
62
+ @adapter.log_gauge @gauge
63
+ end
64
+ end
65
+
66
+ def test_logging_gauge_raises_an_exception_when_id_is_too_long
67
+ @gauge.id = "f" * 256
68
+
69
+ assert_raises Harness::LoggingError do
70
+ @adapter.log_gauge @gauge
71
+ end
72
+ end
73
+
74
+ def test_logging_gauge_raises_an_exception_when_not_configured
75
+ Harness::StathatAdapter.config.ezkey = nil
76
+
77
+ assert_raises RuntimeError do
78
+ @adapter.log_gauge @gauge
79
+ end
80
+ end
81
+
82
+ def test_counter_is_logged
83
+ args = {
84
+ :stat => @counter.id,
85
+ :ezkey => token,
86
+ :count => @counter.value,
87
+ }
88
+
89
+ expected_request = stub_request(:post, "https://api.stathat.com/ez").
90
+ with(:body => args, :headers => {'Content-Type'=>'application/x-www-form-urlencoded'}).
91
+ to_return(:status => 200)
92
+
93
+ assert @adapter.log_counter(@counter)
94
+ assert_requested expected_request
95
+ end
96
+
97
+ def test_counter_is_logged_with_namespace
98
+ Harness.config.namespace = :foo
99
+
100
+ args = {
101
+ :stat => "#{@counter.id}.foo",
102
+ :ezkey => token,
103
+ :count => @counter.value,
104
+ }
105
+
106
+ expected_request = stub_request(:post, "https://api.stathat.com/ez").
107
+ with(:body => args, :headers => {'Content-Type'=>'application/x-www-form-urlencoded'}).
108
+ to_return(:status => 200)
109
+
110
+ assert @adapter.log_counter(@counter)
111
+ assert_requested expected_request
112
+ end
113
+
114
+ def test_logging_counter_raises_an_exception
115
+ stub_request(:post, %r{stathat}).to_return(:status => 500, :body => "message")
116
+
117
+ assert_raises Harness::LoggingError do
118
+ @adapter.log_counter @counter
119
+ end
120
+ end
121
+
122
+ def test_logging_counter_raises_an_exception_when_not_configured
123
+ Harness::StathatAdapter.config.ezkey = nil
124
+
125
+ assert_raises RuntimeError do
126
+ @adapter.log_counter @counter
127
+ end
128
+ end
129
+
130
+ def test_logging_counter_raises_an_exception_when_id_is_too_long
131
+ @counter.id = "f" * 256
132
+
133
+ assert_raises Harness::LoggingError do
134
+ @adapter.log_counter @counter
135
+ end
136
+ end
137
+
138
+ private
139
+
140
+ def token
141
+ 'example@example.com'
142
+ end
143
+ end
@@ -0,0 +1,73 @@
1
+ require 'test_helper'
2
+ require 'ostruct'
3
+
4
+ class StatsdAdapterTest < MiniTest::Unit::TestCase
5
+ def setup
6
+ @adapter = Harness::StatsdAdapter.new
7
+
8
+ @gauge = Harness::Gauge.new
9
+ @gauge.id = "fake-gauge"
10
+ @gauge.name = "Fake Gauge"
11
+ @gauge.source = "minitest"
12
+ @gauge.time = Time.now
13
+ @gauge.value = 55
14
+
15
+ @counter = Harness::Counter.new
16
+ @counter.id = "fake-counter"
17
+ @counter.name = "Fake Counter"
18
+ @counter.source = "minitest"
19
+ @counter.time = Time.now
20
+ @counter.value = 1337
21
+ @counter.units = :bytes
22
+ Harness.config.namespace = nil
23
+ end
24
+
25
+ def test_gauge_is_logged
26
+ mock_backend = MiniTest::Mock.new
27
+ mock_backend.expect :host, 'foo'
28
+ mock_backend.expect :port, 'bar'
29
+
30
+ Harness::StatsdAdapter.config.backend = mock_backend
31
+ mock_backend.expect :gauge, true, [String, 55]
32
+
33
+ assert @adapter.log_gauge(@gauge)
34
+ assert mock_backend.verify
35
+ end
36
+
37
+ def test_logging_gauge_raises_an_exception_when_not_configured
38
+ mock_backend = MiniTest::Mock.new
39
+ mock_backend.expect :host, nil
40
+ mock_backend.expect :port, nil
41
+
42
+ Harness::StatsdAdapter.config.backend = mock_backend
43
+ mock_backend.expect :gauge, true, [String, 55]
44
+
45
+ assert_raises RuntimeError do
46
+ @adapter.log_gauge @gauge
47
+ end
48
+ end
49
+
50
+ def test_counter_is_logged
51
+ mock_backend = MiniTest::Mock.new
52
+ mock_backend.expect :host, 'foo'
53
+ mock_backend.expect :port, 'bar'
54
+
55
+ Harness::StatsdAdapter.config.backend = mock_backend
56
+ mock_backend.expect :increment, true, [String, 1337]
57
+
58
+ assert @adapter.log_counter(@counter)
59
+ assert mock_backend.verify
60
+ end
61
+
62
+ def test_logging_counter_raises_an_exception_when_not_configured
63
+ mock_backend = MiniTest::Mock.new
64
+ mock_backend.expect :host, nil
65
+ mock_backend.expect :port, nil
66
+
67
+ Harness::StatsdAdapter.config.backend = mock_backend
68
+
69
+ assert_raises RuntimeError do
70
+ @adapter.log_counter @counter
71
+ end
72
+ end
73
+ end
@@ -65,6 +65,16 @@ class GaugeTest < MiniTest::Unit::TestCase
65
65
  assert_equal 'foo', gauge.id
66
66
  end
67
67
 
68
+ def test_sets_value_from_payload_if_number
69
+ base = Time.now
70
+
71
+ event = ActiveSupport::Notifications::Event.new "name", base - 1, Time.now, nil, :gauge => {value: 42}
72
+
73
+ gauge = Harness::Gauge.from_event event
74
+
75
+ assert_equal 42, gauge.value
76
+ end
77
+
68
78
  def test_initializes_time_if_not_set
69
79
  gauge = Harness::Gauge.new
70
80
 
@@ -4,25 +4,25 @@ class HarnessModuleTest < MiniTest::Unit::TestCase
4
4
  def test_can_set_the_adapter_with_a_symbol
5
5
  Harness.config.adapter = :memory
6
6
 
7
- assert_equal Harness::MemoryAdapter, Harness.config.adapter
7
+ assert_kind_of Harness::MemoryAdapter, Harness.config.adapter
8
8
  end
9
9
 
10
- def test_can_set_the_adapter_with_a_class
11
- Harness.config.adapter = Harness::MemoryAdapter
10
+ def test_can_set_the_adapter_with_an_adapter_instance
11
+ Harness.config.adapter = Harness::MemoryAdapter.new
12
12
 
13
- assert_equal Harness::MemoryAdapter, Harness.config.adapter
13
+ assert_kind_of Harness::MemoryAdapter, Harness.config.adapter
14
14
  end
15
15
 
16
16
  def test_can_set_the_queue_with_a_symbol
17
- Harness.config.queue = :syncronous
17
+ Harness.config.queue = :synchronous
18
18
 
19
- assert_equal Harness::SyncronousQueue, Harness.config.queue
19
+ assert_kind_of Harness::SynchronousQueue, Harness.config.queue
20
20
  end
21
21
 
22
- def test_can_set_the_queue_with_a_class
23
- Harness.config.adapter = Harness::SyncronousQueue
22
+ def test_can_set_the_queue_with_a_queue_instance
23
+ Harness.config.queue = Harness::SynchronousQueue.new
24
24
 
25
- assert_equal Harness::SyncronousQueue, Harness.config.queue
25
+ assert_kind_of Harness::SynchronousQueue, Harness.config.queue
26
26
  end
27
27
 
28
28
  def test_uses_method_missing_to_configure_adapters
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: harness
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-05 00:00:00.000000000 Z
12
+ date: 2012-11-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: &70133152109020 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: '3'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70133152109020
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '3'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: redis
27
- requirement: &70133152107200 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: '0'
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *70133152107200
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: redis-namespace
38
- requirement: &70133152104740 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
@@ -43,10 +53,15 @@ dependencies:
43
53
  version: '0'
44
54
  type: :runtime
45
55
  prerelease: false
46
- version_requirements: *70133152104740
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: simplecov
49
- requirement: &70133152152020 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ! '>='
@@ -54,10 +69,15 @@ dependencies:
54
69
  version: '0'
55
70
  type: :development
56
71
  prerelease: false
57
- version_requirements: *70133152152020
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
58
78
  - !ruby/object:Gem::Dependency
59
79
  name: webmock
60
- requirement: &70133152149760 !ruby/object:Gem::Requirement
80
+ requirement: !ruby/object:Gem::Requirement
61
81
  none: false
62
82
  requirements:
63
83
  - - ! '>='
@@ -65,10 +85,15 @@ dependencies:
65
85
  version: '0'
66
86
  type: :development
67
87
  prerelease: false
68
- version_requirements: *70133152149760
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
69
94
  - !ruby/object:Gem::Dependency
70
95
  name: resque
71
- requirement: &70133152148660 !ruby/object:Gem::Requirement
96
+ requirement: !ruby/object:Gem::Requirement
72
97
  none: false
73
98
  requirements:
74
99
  - - ! '>='
@@ -76,10 +101,31 @@ dependencies:
76
101
  version: '0'
77
102
  type: :development
78
103
  prerelease: false
79
- version_requirements: *70133152148660
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
80
110
  - !ruby/object:Gem::Dependency
81
111
  name: sidekiq
82
- requirement: &70133152148020 !ruby/object:Gem::Requirement
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: delayed_job_active_record
128
+ requirement: !ruby/object:Gem::Requirement
83
129
  none: false
84
130
  requirements:
85
131
  - - ! '>='
@@ -87,10 +133,15 @@ dependencies:
87
133
  version: '0'
88
134
  type: :development
89
135
  prerelease: false
90
- version_requirements: *70133152148020
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
91
142
  - !ruby/object:Gem::Dependency
92
143
  name: active_model_serializers
93
- requirement: &70133152146920 !ruby/object:Gem::Requirement
144
+ requirement: !ruby/object:Gem::Requirement
94
145
  none: false
95
146
  requirements:
96
147
  - - ! '>='
@@ -98,10 +149,63 @@ dependencies:
98
149
  version: '0'
99
150
  type: :development
100
151
  prerelease: false
101
- version_requirements: *70133152146920
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
102
158
  - !ruby/object:Gem::Dependency
103
159
  name: rails
104
- requirement: &70133152145560 !ruby/object:Gem::Requirement
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ - !ruby/object:Gem::Dependency
175
+ name: sqlite3
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ type: :development
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ - !ruby/object:Gem::Dependency
191
+ name: minitest
192
+ requirement: !ruby/object:Gem::Requirement
193
+ none: false
194
+ requirements:
195
+ - - ! '>='
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
198
+ type: :development
199
+ prerelease: false
200
+ version_requirements: !ruby/object:Gem::Requirement
201
+ none: false
202
+ requirements:
203
+ - - ! '>='
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
206
+ - !ruby/object:Gem::Dependency
207
+ name: statsd-instrument
208
+ requirement: !ruby/object:Gem::Requirement
105
209
  none: false
106
210
  requirements:
107
211
  - - ! '>='
@@ -109,7 +213,12 @@ dependencies:
109
213
  version: '0'
110
214
  type: :development
111
215
  prerelease: false
112
- version_requirements: *70133152145560
216
+ version_requirements: !ruby/object:Gem::Requirement
217
+ none: false
218
+ requirements:
219
+ - - ! '>='
220
+ - !ruby/object:Gem::Version
221
+ version: '0'
113
222
  description: ''
114
223
  email:
115
224
  - me@broadcastingadam.com
@@ -127,6 +236,8 @@ files:
127
236
  - lib/harness/adapters/librato_adapter.rb
128
237
  - lib/harness/adapters/memory_adapter.rb
129
238
  - lib/harness/adapters/null_adapter.rb
239
+ - lib/harness/adapters/stathat_adapter.rb
240
+ - lib/harness/adapters/statsd_adapter.rb
130
241
  - lib/harness/consumer.rb
131
242
  - lib/harness/counter.rb
132
243
  - lib/harness/gauge.rb
@@ -140,9 +251,10 @@ files:
140
251
  - lib/harness/job.rb
141
252
  - lib/harness/measurement.rb
142
253
  - lib/harness/meter.rb
254
+ - lib/harness/queues/delayed_job_queue.rb
143
255
  - lib/harness/queues/resque_queue.rb
144
256
  - lib/harness/queues/sidekiq_queue.rb
145
- - lib/harness/queues/syncronous_queue.rb
257
+ - lib/harness/queues/synchronous_queue.rb
146
258
  - lib/harness/railtie.rb
147
259
  - lib/harness/tasks.rake
148
260
  - lib/harness/version.rb
@@ -156,17 +268,19 @@ files:
156
268
  - test/integration/integrations/sidekiq_test.rb
157
269
  - test/integration/logging_test.rb
158
270
  - test/integration/meter_test.rb
271
+ - test/integration/queues/delayed_job_test.rb
159
272
  - test/integration/queues/resque_test.rb
160
273
  - test/integration/queues/sidekiq_test.rb
161
274
  - test/integration/railtie_test.rb
162
275
  - test/test_helper.rb
163
276
  - test/unit/adapters/librato_adapter_test.rb
164
277
  - test/unit/adapters/memory_adapter_test.rb
278
+ - test/unit/adapters/stathat_adapter_test.rb
279
+ - test/unit/adapters/statsd_adapter_test.rb
165
280
  - test/unit/counter_test.rb
166
281
  - test/unit/gauge_test.rb
167
282
  - test/unit/harness_test.rb
168
283
  - test/unit/measurement_test.rb
169
- - test/unit/meter_test.rb
170
284
  homepage: ''
171
285
  licenses: []
172
286
  post_install_message:
@@ -179,15 +293,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
179
293
  - - ! '>='
180
294
  - !ruby/object:Gem::Version
181
295
  version: '0'
296
+ segments:
297
+ - 0
298
+ hash: -4565454551887677115
182
299
  required_rubygems_version: !ruby/object:Gem::Requirement
183
300
  none: false
184
301
  requirements:
185
302
  - - ! '>='
186
303
  - !ruby/object:Gem::Version
187
304
  version: '0'
305
+ segments:
306
+ - 0
307
+ hash: -4565454551887677115
188
308
  requirements: []
189
309
  rubyforge_project:
190
- rubygems_version: 1.8.11
310
+ rubygems_version: 1.8.24
191
311
  signing_key:
192
312
  specification_version: 3
193
313
  summary: ''
@@ -202,14 +322,16 @@ test_files:
202
322
  - test/integration/integrations/sidekiq_test.rb
203
323
  - test/integration/logging_test.rb
204
324
  - test/integration/meter_test.rb
325
+ - test/integration/queues/delayed_job_test.rb
205
326
  - test/integration/queues/resque_test.rb
206
327
  - test/integration/queues/sidekiq_test.rb
207
328
  - test/integration/railtie_test.rb
208
329
  - test/test_helper.rb
209
330
  - test/unit/adapters/librato_adapter_test.rb
210
331
  - test/unit/adapters/memory_adapter_test.rb
332
+ - test/unit/adapters/stathat_adapter_test.rb
333
+ - test/unit/adapters/statsd_adapter_test.rb
211
334
  - test/unit/counter_test.rb
212
335
  - test/unit/gauge_test.rb
213
336
  - test/unit/harness_test.rb
214
337
  - test/unit/measurement_test.rb
215
- - test/unit/meter_test.rb
File without changes