harness 0.9.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+