harness 0.9.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -0
  3. data/README.md +108 -323
  4. data/harness.gemspec +3 -12
  5. data/lib/harness.rb +69 -68
  6. data/lib/harness/async_queue.rb +29 -0
  7. data/lib/harness/fake_collector.rb +40 -0
  8. data/lib/harness/instrumentation.rb +21 -6
  9. data/lib/harness/null_collector.rb +23 -0
  10. data/lib/harness/subscription.rb +7 -0
  11. data/lib/harness/sync_queue.rb +14 -0
  12. data/lib/harness/version.rb +1 -1
  13. data/test/acceptance_test.rb +20 -0
  14. data/test/active_support_notifications_test.rb +93 -0
  15. data/test/async_queue_test.rb +14 -0
  16. data/test/instrumentation_test.rb +54 -0
  17. data/test/null_collector_test.rb +29 -0
  18. data/test/test_helper.rb +38 -34
  19. metadata +21 -196
  20. data/lib/harness/adapters/librato_adapter.rb +0 -90
  21. data/lib/harness/adapters/memory_adapter.rb +0 -27
  22. data/lib/harness/adapters/null_adapter.rb +0 -11
  23. data/lib/harness/adapters/stathat_adapter.rb +0 -75
  24. data/lib/harness/adapters/statsd_adapter.rb +0 -50
  25. data/lib/harness/consumer.rb +0 -35
  26. data/lib/harness/counter.rb +0 -29
  27. data/lib/harness/gauge.rb +0 -23
  28. data/lib/harness/integration/action_controller.rb +0 -9
  29. data/lib/harness/integration/action_mailer.rb +0 -9
  30. data/lib/harness/integration/action_view.rb +0 -9
  31. data/lib/harness/integration/active_model_serializers.rb +0 -9
  32. data/lib/harness/integration/active_support.rb +0 -9
  33. data/lib/harness/integration/sidekiq.rb +0 -47
  34. data/lib/harness/job.rb +0 -23
  35. data/lib/harness/measurement.rb +0 -43
  36. data/lib/harness/queues/delayed_job_queue.rb +0 -7
  37. data/lib/harness/queues/resque_queue.rb +0 -29
  38. data/lib/harness/queues/sidekiq_queue.rb +0 -31
  39. data/lib/harness/queues/synchronous_queue.rb +0 -18
  40. data/lib/harness/railtie.rb +0 -71
  41. data/lib/harness/tasks.rake +0 -6
  42. data/test/integration/counters_with_redis_test.rb +0 -67
  43. data/test/integration/instrumentation_test.rb +0 -50
  44. data/test/integration/integrations/action_controller_test.rb +0 -51
  45. data/test/integration/integrations/action_mailer_test.rb +0 -22
  46. data/test/integration/integrations/action_view_test.rb +0 -32
  47. data/test/integration/integrations/active_model_serializers_test.rb +0 -22
  48. data/test/integration/integrations/active_support_test.rb +0 -41
  49. data/test/integration/integrations/sidekiq_test.rb +0 -54
  50. data/test/integration/logging_test.rb +0 -17
  51. data/test/integration/queues/delayed_job_test.rb +0 -59
  52. data/test/integration/queues/resque_test.rb +0 -24
  53. data/test/integration/queues/sidekiq_test.rb +0 -34
  54. data/test/integration/railtie_test.rb +0 -26
  55. data/test/unit/adapters/librato_adapter_test.rb +0 -169
  56. data/test/unit/adapters/memory_adapter_test.rb +0 -22
  57. data/test/unit/adapters/stathat_adapter_test.rb +0 -144
  58. data/test/unit/adapters/statsd_adapter_test.rb +0 -74
  59. data/test/unit/counter_test.rb +0 -84
  60. data/test/unit/gauge_test.rb +0 -93
  61. data/test/unit/harness_test.rb +0 -39
  62. data/test/unit/measurement_test.rb +0 -76
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 331d4b6080058b3d3691dbba570c2fd997a07d6b
4
- data.tar.gz: 305a235a8ab89e07bd091dc381876ff9c4a7e656
3
+ metadata.gz: 231a1af8531021b021a97eec867519b6e2d75813
4
+ data.tar.gz: 539d0e596ef148b085286b3fdf7e6b8a85b14725
5
5
  SHA512:
6
- metadata.gz: ba1e3dbeeeafcdb43c21099b98ccda432c660b23d1f3cf130450a2c924d0894c527e5f66b94799c1e38d2025f3bc31b1ccf1638d667976b2c624805362b6f743
7
- data.tar.gz: a6810d20de6134b3c1c43ff2aacec5d1e273248aaa6be557c771490f8c3da06fdd65f1cb5d6533683b0827378fc7891bee0c84bf74a60cbb7a035c897721ce87
6
+ metadata.gz: 169cb10c469eb21a91d3f0fdb884595aadd66ea487da6a5408b530af6f2a68ae0dd26dbe61900514146b6474e187a001c26e2b7b422dddcc3a8dd5f4e2cbb610
7
+ data.tar.gz: bceeb202d6c69d3688a5085ecf2b6e25f5a32182ed3dd041214fe619828021c8dd7c02d94be50e175c961484288274e2970f107997d84343d0880cb3592edc61
data/.travis.yml CHANGED
@@ -1,3 +1,5 @@
1
1
  rvm:
2
2
  - "1.9.3"
3
3
  - "2.0.0"
4
+ - "jruby-19mode"
5
+ - "rbx-19mode"
data/README.md CHANGED
@@ -1,339 +1,123 @@
1
1
  # Harness
2
- [![Build Status](https://secure.travis-ci.org/twinturbo/harness.png?branch=master)][travis]
2
+ [![Build Status](https://secure.travis-ci.org/ahawkins/harness.png?branch=master)][travis]
3
3
  [![Gem Version](https://badge.fury.io/rb/harness.png)][gem]
4
- [![Code Climate](https://codeclimate.com/github/twinturbo/harness.png)][codeclimate]
5
- [![Dependency Status](https://gemnasium.com/twinturbo/harness.png?travis)][gemnasium]
4
+ [![Code Climate](https://codeclimate.com/github/ahawkins/harness.png)][codeclimate]
5
+ [![Dependency Status](https://gemnasium.com/ahawkins/harness.png?travis)][gemnasium]
6
6
 
7
7
  [gem]: https://rubygems.org/gems/harness
8
- [travis]: http://travis-ci.org/twinturbo/harness
9
- [gemnasium]: https://gemnasium.com/twinturbo/harness
10
- [codeclimate]: https://codeclimate.com/github/twinturbo/harness
11
- [coveralls]: https://coveralls.io/r/twinturbo/harness
12
-
13
- Harness connects measurements coming from `ActiveSupport::Notifications`
14
- to external metric tracking services. Counters are stored locally with
15
- redis before being sent to the service.
16
-
17
- Currently Supported Services:
18
-
19
- * Librato
20
- * Statsd (thanks to fluxlux)
21
- * Stathat
22
-
23
- Current Features:
24
-
25
- * Track counters over time (# of registered users)
26
- * Read time specific values (# time to cache something)
27
- * Build meters on top of counters (# requests per second)
28
- * Sidekiq integration
29
- * Resque integration
30
- * Rails integration
31
- * Capture and log all measurements coming out of Rails
32
-
33
- **Crash Course**
34
-
35
- ```ruby
36
- class ComplicatedClass
37
- def hard_work
38
- # Automatically track how long each of these calls takes so they can
39
- # tracked and compared over time.
40
- ActiveSupport::Notifications.instrument "hard_work", :gauge => true do
41
- # do hard_work
42
- end
43
- end
44
-
45
- def register_user
46
- # Automatically track the total # of registered users you have.
47
- # As well, as be able to take measurements of users created in a
48
- # specific interval
49
- ActiveSupport::Notifications.instrument "register_user", :counter => true do
50
- # register_user
51
- end
52
- end
53
- end
54
- ```
55
-
56
- ## Installation
57
-
58
- Add this line to your application's Gemfile:
59
-
60
- ```ruby
61
- gem 'harness'
62
- ```
63
-
64
- And then execute:
65
-
66
- ```shell
67
- bundle
68
- ```
69
-
70
- Or install it yourself as:
71
-
72
- ```shell
73
- gem install harness
74
- ```
75
-
76
- ## Usage
77
-
78
- In the metrics world there are two types of things: Gauges and Counters.
79
- Gauges are time sensitive and represent something at a specific point in
80
- time. Counters keep track of things and should be increasing. Counters
81
- can be reset back to zero. You can combine counters and/or gauges to
82
- correlate data about your application. Meters monitor counters. They
83
- allow you look at rates of counters (read: counters per second).
84
-
85
- Harness makes this process easily. Harness' primary goal it make it dead
86
- simple to start measuring different parts of your application.
87
- `ActiveSupport::Notifications` makes this very easy because it provides
88
- measurements and implements the observer pattern.
89
-
90
- ## Tracking Things
91
-
92
- I guess you read the `ActiveSupport::Notifications`
93
- [documentation](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html)
94
- before going any further or this will seems like php to you. Harness
95
- hooks into your notifications and looks for `:gauge` or `:counter`
96
- options. If either is present, it will be sent to the external service.
97
- For example, you can track how long it's taking to do a specific thing:
98
-
99
- ```ruby
100
- class MyClass
101
- def important_method(stuff)
102
- ActiveSupport::Notifications.instrument "important_method.my_class", :gauge => true do
103
- do_important_stuff
104
- end
105
- end
106
- end
107
- ```
108
-
109
- You can do the same with a counter. Counter values are automatically
110
- stored in redis and incremented. This means you can simply pass
111
- `:counter => true` in instrumentations if you'd like to count it. You
112
- may also pass `:counter => 5` if you'd like to provide your own value.
113
- This value is stored in redis so the next time `:counter => true` will
114
- work correctly. You can reset all the counters back to zero by calling:
115
- `Harness.reset_counters!`.
116
-
117
- **NOTE**: You should use the bundled rake task to reset counters with
118
- a cron job. This will prevent unbounded growth of this metadata. You
119
- can call `rake harness:reset_counters` to do this. You should call
120
- this rake task at whatever your longest measurable interval is. Here's
121
- an example: You log gauges every 12 hours. You should reset the
122
- counters every 12 hours. This issue is discussed [here](https://github.com/twinturbo/harness/issues/15).
123
-
124
- ```ruby
125
- class MyClass
126
- def important_method(stuff)
127
- ActiveSupport::Notifications.instrument "important_method.my_class", :counter => true do
128
- do_important_stuff
129
- end
130
- end
131
- end
132
- ```
133
-
134
- The instruments name will be sent as the name (`important_method.my_class`)
135
- for that gauge or counter.
136
-
137
- Note that `ActiveSupport::Notifications.instrument` doesn't require
138
- a block. This can be useful when you are taking an instant measurement.
139
-
140
- ```ruby
141
- class MyClass
142
- def important_method(stuff)
143
- ActiveSupport::Notifications.instrument "important_method.my_class", :counter => true
144
- end
145
- end
146
- ```
147
-
148
- Harness will do all the extra work in sending these metrics to whatever
149
- service you're using.
150
-
151
- Once you the counters are you are instrumented, then you can meter them.
152
- Meters allow you take arbitrary readings of counter rates. The results
153
- return a gauge so they can be logged as well.
154
-
155
- ```ruby
156
- # Define a counter
157
- class MyClass
158
- def important_method(stuff)
159
- ActiveSupport::Notifications.instrument "important_method.my_class", :counter => true do
160
- do_important_stuff
161
- end
162
- end
163
- end
164
-
165
- # Now you can meter it
166
- meter = Harness::Meter.new('important_method.my_class')
167
- meter.per_second # returns a gauge
168
- meter.per_second.value # if you just want the number
169
- meter.per(1.hour).value # You can use your own interval
170
- meter.per_minute
171
- meter.per_hour
172
- ```
173
-
174
- ## Customizing
175
-
176
- You can pass a hash to `:counter` or `:gauge` to initialize the
177
- measurement your own way.
178
-
179
- If you pass a value attribute to a gauge, it will be the value
180
- sent instead of the duration of the block.
181
-
182
- ```ruby
183
- class MyClass
184
- def important_method(stuff)
185
- ActiveSupport::Notifications.instrument "important_method.my_class",
186
- :gauge => { :id => 'custom-id', :name => "My Measurement",
187
- :value => my_current_val, :units => 'cogs' } do
188
- do_important_stuff
8
+ [travis]: http://travis-ci.org/ahawkins/harness
9
+ [gemnasium]: https://gemnasium.com/ahawkins/harness
10
+ [codeclimate]: https://codeclimate.com/github/ahawkins/harness
11
+ [coveralls]: https://coveralls.io/r/ahawkins/harness
12
+
13
+ Harness provides you with high level application metrics. It collects
14
+ metrics from various sources and forwards them to the collector. You
15
+ can use any collector that implements the `Statsd` interface. Harness
16
+ also collects metrics from `ActiveSupport::Notifications` and forwards
17
+ them to the collector.
18
+
19
+ Harness only assumes one thing: the collector can do proper metric
20
+ aggregation and statistics. Example: using statsd will calculate the
21
+ 90th percentile and averages.
22
+
23
+ Harness is designed for very high traffic applications. Instrumenting
24
+ code should cost as close to 0 as possible. All metrics are processed
25
+ in a separate thread. The main thread will never do any more work than
26
+ needed. Using a thread allows you to instrument 10,000's metrics per
27
+ second without worrying.
28
+
29
+ Harness's has two main goals:
30
+
31
+ 1. Make instrumentation fast and cheap
32
+ 2. Provide high level system metrics (think like a car dashboard for
33
+ your app).
34
+
35
+ Solving #1 is easy: include `Harness::Instrumented` in your class. #2
36
+ is slightly more complicated, but Harness automatically collects all
37
+ the metrics for you.
38
+
39
+ ## Application Independent Metrics
40
+
41
+ Most ruby applications are using a similar stack: rack, a cache, a key
42
+ value store, job processor, and persistent data store. Visiblity is
43
+ the most important thing when it comes to application performance. You
44
+ can only improve something when you can measure it. Harness takes care
45
+ of the measuring. Harness integrates with common components in the
46
+ ruby eosystem and gives you the data you need. You should put this
47
+ data on your dashboard and pay attention to it.
48
+
49
+ These metrics should be enough to give you a high level overview on
50
+ how all the different layers in your stack are performing. **Harness
51
+ is not for drilling down into a specific request or peice of code.**
52
+ You should use the ruby-prof for that. In short, Harness is not a
53
+ replacement for new-relic. They serve different purposes. However, you
54
+ could deduce all the information newrelic provides from
55
+ instrumentation data.
56
+
57
+ ## Supported Libraries & Projects
58
+
59
+ Harness is an interface. All integrations use the interface.
60
+ Instrumentation for popular libraries are provided as gems. This
61
+ allows anyone to release instrumentations. Individual gems can be
62
+ maintained and released separate of this gem. Here is the definite
63
+ list.
64
+
65
+ * [harness-actioncontroller](http://github.com/ahawkins/harness-actioncontroller)
66
+ * [harness-actionmailer](http://github.com/ahawkins/harness-actionmailer)
67
+ * [harness-actionview](http://github.com/ahawkins/harness-actionview)
68
+ * [harness-activerecord](http://github.com/ahawkins/harness/activerecord)
69
+ * [harness-active\_model\_serialzier](http://github.com/ahawkins/harness-active_model_serializers)
70
+ * [harness-activesupport](http://github.com/ahawkins/harness-activesupport)
71
+ * [harness-haproxy](http://github.com/ahawkins/harness-haproxy)
72
+ * [harness-memcached](http://github.com/ahawkins/harness-memcached) - [dalli](http://github.com/merpahm/dalli) through harness-activesupport
73
+ * [harness-moped](http://github.com/ahawkins/harness-moped) - (mongoid)
74
+ * [harness-rack](http://github.com/ahawkins/harness-rack)
75
+ * [harness-redis](http://github.com/ahawkins/harness-redis)
76
+ * [harness-sequel](http://github.com/ahawkins/harness-sequel)
77
+ * [harness-sidekiq](http://github.com/ahawkins/harness-sidekiq)
78
+ * [harness-varnish](http://github.com/ahawkins/harness-varnish)
79
+
80
+ ## Instrumenting
81
+
82
+ `Harness` provides the same interface as `statsd`. You can interact
83
+ with `Harness` directly. This is not advised. You should `include` or
84
+ `extend` `Harness::Instrumentation` in your class. Here are some
85
+ examples.
86
+
87
+ ```ruby
88
+ class UseCase
89
+ include Harness::Instrumentation
90
+
91
+ def run!
92
+ increment 'foo'
93
+ increment 'foo', 5
94
+
95
+ decrement 'foo'
96
+ decrement 'foo', 5
97
+
98
+ count 'foo', 1000
99
+
100
+ time 'foo' do
101
+ # do hard work
189
102
  end
190
103
  end
191
104
  end
192
105
  ```
193
106
 
194
- ## One Off Gauges and Counters
195
-
196
- You can instantiate `Harness::Counter` and `Harness::Gauge` wherever you
197
- want. Events from `ActiveSupport` are just converted to these classes
198
- under the covers anyways. You can use these class if you want to take
199
- periodic measurements or tracking something that happens outside the
200
- application.
201
-
202
- ```ruby
203
- gauge = Harness::Gauge.new
204
- gauge.id = "foo.bar"
205
- gauge.name = "Foo's Bar"
206
- gauge.time # defaults to Time.now
207
- gauge.value = readings_from_my_server
208
- gauge.units = 'bytes'
209
- gauge.log
210
-
211
- counter = Harness::Counter.new
212
- counter.id = "foo.bar"
213
- counter.name = "# of Foo bars"
214
- counter.time # defaults to Time.now
215
- counter.value = read_total_users_in_database
216
- counter.log
217
-
218
- ### Both classes take an option hash
219
-
220
- gauge = Harness::Gauge.new :time => Time.now, :id => 'foo.bar'
221
- counter = Harness::Counter.new :time => Time.now, :id => 'foo.bar'
222
- ```
223
-
224
- ## Configuration
107
+ That's all there is to it!
225
108
 
226
- ### Librato
109
+ ## Configuring
227
110
 
228
- ```ruby
229
- require 'harness/adapters/librato_adapter'
230
-
231
- Harness.config.adapter = :librato
232
-
233
- Harness.config.librato.email = 'example@example.com'
234
- Harness.config.librato.token = 'your-api-key'
111
+ Harness has two configuration options: the queue and collector.
112
+ `Harness::AsyncQueue` is the default queue. This means all metrics are
113
+ logged in a separate thread to never block the main thread. This makes
114
+ harness more performant in high traffic scenarios.
115
+ `Harness::NullCollector`. There is also a `Harness::SyncQueue`
116
+ useful for testing (but really used in practice).
235
117
 
236
118
  ```
237
-
238
- ### StatsD
239
-
240
- Harness does **not** configure StatsD for you. It uses the StatsD class
241
- under the covers. If you've already configured that in your own way, great.
242
- If not, you can use the configuration proxy as described below. You
243
- must also add `statsd-instrument` to your `Gemfile`. This is a soft
244
- dependency that is not installed for you.
245
-
246
- ```ruby
247
- require 'harness/adapters/statsd_adapter'
248
-
249
- Harness.config.adapter = :statsd
250
-
251
- # Harness.config.statsd is a proxy for the StatsD class
252
- Harness.config.statsd.host = 'localhost'
253
- Harness.config.statsd.port = '8080'
254
- Harness.config.statsd.default_sample_rate = 0.1
255
- Harness.config.statsd.logger = Rails.logger
256
-
257
- # You can assign your own StatsD implementation
258
- # by setting the "backend" attribute
259
- Harness.config.statsd.backend = CustomStatsD
260
- ```
261
-
262
- ### Stathat
263
-
264
- ```ruby
265
- require 'harness/adapters/stathat_adapter'
266
-
267
- Harness.config.adapter = :stathat
268
-
269
- Harness.config.stathat.ezkey = 'example@example.com'
270
- ```
271
-
272
- ## Rails Integration
273
-
274
- Harness will automatically log metrics coming from `ActionPack`,
275
- `ActiveRecord`, and `ActionMailer`. `ActiveSupport` instrumentation is
276
- disabled by default. First require your adapter in an initializer
277
- Harness no longer requires adapters with external dependencies for
278
- you. Create a `config/initializers/harness.rb` and require your
279
- adapter in it:
280
-
281
- ```ruby
282
- require 'harness/adapters/statsd_adapter'
283
- # other configuration
284
- ```
285
-
286
- Also, custom integrations are disabled by default.
287
- You can turn on instrumentation for specific components like so:
288
-
289
- ```ruby
290
- config.harness.instrument.action_controller = false
291
- config.harness.instrument.active_support = true
292
- config.harness.instrument.sidekiq = true
293
- config.harness.instrument.active_model_serializers = true
294
- ```
295
-
296
- You can configure Harness from `application.rb`
297
-
298
- ```ruby
299
- config.harness.adapter = :librato
300
- config.harness.librato.email = 'example@example.com'
301
- config.harness.librato.token = 'your-api-key'
302
- ```
303
-
304
- Redis will be automatically configured if you `REDISTOGO_URL` or
305
- `REDIS_URL` environment variables at set. They are wrapped in a
306
- namespace so there will be no conflicts. If they are not present, the
307
- default values are used. You can customize this in an initializer:
308
-
309
- ```ruby
310
- # config/initializers/harness.rb
311
- require 'erb'
312
-
313
- file = Rails.root.join 'config', 'resque.yml'
314
- config = YAML.load(ERB.new(File.read(Rails.root.join('config', 'redis.yml'))).result)
315
-
316
- Harness.redis = Redis::Namespace.new('harness', :redis => Redis.connect(:url => config[Rails.env]))
317
- ```
318
-
319
- `rake harness:reset_counters` is also added.
320
-
321
- ### Rails Environments
322
-
323
- Measurements are completely ignored in the test env. They are processed
324
- in development mode, but not sent to the external service. Everything is
325
- logged in production.
326
-
327
- ### Background Processing
328
-
329
- Harness integrates automatically with Resque or Sidekiq. This is because
330
- reporting measurements can take time and add unnecessary overhead to the
331
- response time. If neither of these libraries are present, measurements
332
- **will be posted in realtime.** You can set your own queue by
333
- specifying a class like so:
334
-
335
- ```ruby
336
- Harness.config.queue = MyCustomQueue
119
+ Harness.collector = Statsd.new 'something.com'
120
+ Harness.queue = Harness::AsyncQueue
337
121
  ```
338
122
 
339
123
  ## Contributing
@@ -343,3 +127,4 @@ Harness.config.queue = MyCustomQueue
343
127
  3. Commit your changes (`git commit -am 'Added some feature'`)
344
128
  4. Push to the branch (`git push origin my-new-feature`)
345
129
  5. Create new Pull Request
130
+