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.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/README.md +108 -323
- data/harness.gemspec +3 -12
- data/lib/harness.rb +69 -68
- data/lib/harness/async_queue.rb +29 -0
- data/lib/harness/fake_collector.rb +40 -0
- data/lib/harness/instrumentation.rb +21 -6
- data/lib/harness/null_collector.rb +23 -0
- data/lib/harness/subscription.rb +7 -0
- data/lib/harness/sync_queue.rb +14 -0
- data/lib/harness/version.rb +1 -1
- data/test/acceptance_test.rb +20 -0
- data/test/active_support_notifications_test.rb +93 -0
- data/test/async_queue_test.rb +14 -0
- data/test/instrumentation_test.rb +54 -0
- data/test/null_collector_test.rb +29 -0
- data/test/test_helper.rb +38 -34
- metadata +21 -196
- data/lib/harness/adapters/librato_adapter.rb +0 -90
- data/lib/harness/adapters/memory_adapter.rb +0 -27
- data/lib/harness/adapters/null_adapter.rb +0 -11
- data/lib/harness/adapters/stathat_adapter.rb +0 -75
- data/lib/harness/adapters/statsd_adapter.rb +0 -50
- data/lib/harness/consumer.rb +0 -35
- data/lib/harness/counter.rb +0 -29
- data/lib/harness/gauge.rb +0 -23
- data/lib/harness/integration/action_controller.rb +0 -9
- data/lib/harness/integration/action_mailer.rb +0 -9
- data/lib/harness/integration/action_view.rb +0 -9
- data/lib/harness/integration/active_model_serializers.rb +0 -9
- data/lib/harness/integration/active_support.rb +0 -9
- data/lib/harness/integration/sidekiq.rb +0 -47
- data/lib/harness/job.rb +0 -23
- data/lib/harness/measurement.rb +0 -43
- data/lib/harness/queues/delayed_job_queue.rb +0 -7
- data/lib/harness/queues/resque_queue.rb +0 -29
- data/lib/harness/queues/sidekiq_queue.rb +0 -31
- data/lib/harness/queues/synchronous_queue.rb +0 -18
- data/lib/harness/railtie.rb +0 -71
- data/lib/harness/tasks.rake +0 -6
- data/test/integration/counters_with_redis_test.rb +0 -67
- data/test/integration/instrumentation_test.rb +0 -50
- data/test/integration/integrations/action_controller_test.rb +0 -51
- data/test/integration/integrations/action_mailer_test.rb +0 -22
- data/test/integration/integrations/action_view_test.rb +0 -32
- data/test/integration/integrations/active_model_serializers_test.rb +0 -22
- data/test/integration/integrations/active_support_test.rb +0 -41
- data/test/integration/integrations/sidekiq_test.rb +0 -54
- data/test/integration/logging_test.rb +0 -17
- data/test/integration/queues/delayed_job_test.rb +0 -59
- data/test/integration/queues/resque_test.rb +0 -24
- data/test/integration/queues/sidekiq_test.rb +0 -34
- data/test/integration/railtie_test.rb +0 -26
- data/test/unit/adapters/librato_adapter_test.rb +0 -169
- data/test/unit/adapters/memory_adapter_test.rb +0 -22
- data/test/unit/adapters/stathat_adapter_test.rb +0 -144
- data/test/unit/adapters/statsd_adapter_test.rb +0 -74
- data/test/unit/counter_test.rb +0 -84
- data/test/unit/gauge_test.rb +0 -93
- data/test/unit/harness_test.rb +0 -39
- data/test/unit/measurement_test.rb +0 -76
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 231a1af8531021b021a97eec867519b6e2d75813
|
4
|
+
data.tar.gz: 539d0e596ef148b085286b3fdf7e6b8a85b14725
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 169cb10c469eb21a91d3f0fdb884595aadd66ea487da6a5408b530af6f2a68ae0dd26dbe61900514146b6474e187a001c26e2b7b422dddcc3a8dd5f4e2cbb610
|
7
|
+
data.tar.gz: bceeb202d6c69d3688a5085ecf2b6e25f5a32182ed3dd041214fe619828021c8dd7c02d94be50e175c961484288274e2970f107997d84343d0880cb3592edc61
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,339 +1,123 @@
|
|
1
1
|
# Harness
|
2
|
-
[][travis]
|
3
3
|
[][gem]
|
4
|
-
[][codeclimate]
|
5
|
+
[][gemnasium]
|
6
6
|
|
7
7
|
[gem]: https://rubygems.org/gems/harness
|
8
|
-
[travis]: http://travis-ci.org/
|
9
|
-
[gemnasium]: https://gemnasium.com/
|
10
|
-
[codeclimate]: https://codeclimate.com/github/
|
11
|
-
[coveralls]: https://coveralls.io/r/
|
12
|
-
|
13
|
-
Harness
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
-
|
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
|
-
|
109
|
+
## Configuring
|
227
110
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
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
|
-
|
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
|
+
|