README.md in harness-0.9.1 vs README.md in harness-1.0.0

- old
+ new

@@ -1,345 +1,130 @@ # Harness -[![Build Status](https://secure.travis-ci.org/twinturbo/harness.png?branch=master)][travis] +[![Build Status](https://secure.travis-ci.org/ahawkins/harness.png?branch=master)][travis] [![Gem Version](https://badge.fury.io/rb/harness.png)][gem] -[![Code Climate](https://codeclimate.com/github/twinturbo/harness.png)][codeclimate] -[![Dependency Status](https://gemnasium.com/twinturbo/harness.png?travis)][gemnasium] +[![Code Climate](https://codeclimate.com/github/ahawkins/harness.png)][codeclimate] +[![Dependency Status](https://gemnasium.com/ahawkins/harness.png?travis)][gemnasium] [gem]: https://rubygems.org/gems/harness -[travis]: http://travis-ci.org/twinturbo/harness -[gemnasium]: https://gemnasium.com/twinturbo/harness -[codeclimate]: https://codeclimate.com/github/twinturbo/harness -[coveralls]: https://coveralls.io/r/twinturbo/harness +[travis]: http://travis-ci.org/ahawkins/harness +[gemnasium]: https://gemnasium.com/ahawkins/harness +[codeclimate]: https://codeclimate.com/github/ahawkins/harness +[coveralls]: https://coveralls.io/r/ahawkins/harness -Harness connects measurements coming from `ActiveSupport::Notifications` -to external metric tracking services. Counters are stored locally with -redis before being sent to the service. +Harness provides you with high level application metrics. It collects +metrics from various sources and forwards them to the collector. You +can use any collector that implements the `Statsd` interface. Harness +also collects metrics from `ActiveSupport::Notifications` and forwards +them to the collector. -Currently Supported Services: +Harness only assumes one thing: the collector can do proper metric +aggregation and statistics. Example: using statsd will calculate the +90th percentile and averages. -* Librato -* Statsd (thanks to fluxlux) -* Stathat +Harness is designed for very high traffic applications. Instrumenting +code should cost as close to 0 as possible. All metrics are processed +in a separate thread. The main thread will never do any more work than +needed. Using a thread allows you to instrument 10,000's metrics per +second without worrying. -Current Features: +Harness's has two main goals: -* Track counters over time (# of registered users) -* Read time specific values (# time to cache something) -* Build meters on top of counters (# requests per second) -* Sidekiq integration -* Resque integration -* Rails integration -* Capture and log all measurements coming out of Rails +1. Make instrumentation fast and cheap +2. Provide high level system metrics (think like a car dashboard for + your app). -**Crash Course** +Solving #1 is easy: include `Harness::Instrumented` in your class. #2 +is slightly more complicated, but Harness automatically collects all +the metrics for you. -```ruby -class ComplicatedClass - def hard_work - # Automatically track how long each of these calls takes so they can - # tracked and compared over time. - ActiveSupport::Notifications.instrument "hard_work", :gauge => true do - # do hard_work - end - end +## Application Independent Metrics - def register_user - # Automatically track the total # of registered users you have. - # As well, as be able to take measurements of users created in a - # specific interval - ActiveSupport::Notifications.instrument "register_user", :counter => true do - # register_user - end - end -end -``` +Most ruby applications are using a similar stack: rack, a cache, a key +value store, job processor, and persistent data store. Visiblity is +the most important thing when it comes to application performance. You +can only improve something when you can measure it. Harness takes care +of the measuring. Harness integrates with common components in the +ruby eosystem and gives you the data you need. You should put this +data on your dashboard and pay attention to it. -## Installation +These metrics should be enough to give you a high level overview on +how all the different layers in your stack are performing. **Harness +is not for drilling down into a specific request or peice of code.** +You should use the ruby-prof for that. In short, Harness is not a +replacement for new-relic. They serve different purposes. However, you +could deduce all the information newrelic provides from +instrumentation data. -Add this line to your application's Gemfile: +## Supported Libraries & Projects -```ruby -gem 'harness' -``` +Harness is an interface. All integrations use the interface. +Instrumentation for popular libraries are provided as gems. This +allows anyone to release instrumentations. Individual gems can be +maintained and released separate of this gem. Here is the definite +list. -And then execute: +* [harness-actioncontroller](http://github.com/ahawkins/harness-actioncontroller) +* [harness-actionmailer](http://github.com/ahawkins/harness-actionmailer) +* [harness-actionview](http://github.com/ahawkins/harness-actionview) +* [harness-activerecord](http://github.com/ahawkins/harness/activerecord) +* [harness-active\_model\_serialzier](http://github.com/ahawkins/harness-active_model_serializers) +* [harness-activesupport](http://github.com/ahawkins/harness-activesupport) +* [harness-haproxy](http://github.com/ahawkins/harness-haproxy) +* [harness-memcached](http://github.com/ahawkins/harness-memcached) - [dalli](http://github.com/merpahm/dalli) through harness-activesupport +* [harness-moped](http://github.com/ahawkins/harness-moped) - (mongoid) +* [harness-rack](http://github.com/ahawkins/harness-rack) +* [harness-redis](http://github.com/ahawkins/harness-redis) +* [harness-sequel](http://github.com/ahawkins/harness-sequel) +* [harness-sidekiq](http://github.com/ahawkins/harness-sidekiq) +* [harness-varnish](http://github.com/ahawkins/harness-varnish) -```shell -bundle -``` +## Instrumenting -Or install it yourself as: +`Harness` provides the same interface as `statsd`. You can interact +with `Harness` directly. This is not advised. You should `include` or +`extend` `Harness::Instrumentation` in your class. Here are some +examples. -```shell -gem install harness -``` - -## Usage - -In the metrics world there are two types of things: Gauges and Counters. -Gauges are time sensitive and represent something at a specific point in -time. Counters keep track of things and should be increasing. Counters -can be reset back to zero. You can combine counters and/or gauges to -correlate data about your application. Meters monitor counters. They -allow you look at rates of counters (read: counters per second). - -Harness makes this process easily. Harness' primary goal it make it dead -simple to start measuring different parts of your application. -`ActiveSupport::Notifications` makes this very easy because it provides -measurements and implements the observer pattern. - -## Tracking Things - -I guess you read the `ActiveSupport::Notifications` -[documentation](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) -before going any further or this will seems like php to you. Harness -hooks into your notifications and looks for `:gauge` or `:counter` -options. If either is present, it will be sent to the external service. -For example, you can track how long it's taking to do a specific thing: - ```ruby -class MyClass - def important_method(stuff) - ActiveSupport::Notifications.instrument "important_method.my_class", :gauge => true do - do_important_stuff - end - end -end -``` +class UseCase + include Harness::Instrumentation -You can do the same with a counter. Counter values are automatically -stored in redis and incremented. This means you can simply pass -`:counter => true` in instrumentations if you'd like to count it. You -may also pass `:counter => 5` if you'd like to provide your own value. -This value is stored in redis so the next time `:counter => true` will -work correctly. You can reset all the counters back to zero by calling: -`Harness.reset_counters!`. + def run! + increment 'foo' + increment 'foo', 5 -**NOTE**: You should use the bundled rake task to reset counters with -a cron job. This will prevent unbounded growth of this metadata. You -can call `rake harness:reset_counters` to do this. You should call -this rake task at whatever your longest measurable interval is. Here's -an example: You log gauges every 12 hours. You should reset the -counters every 12 hours. This issue is discussed [here](https://github.com/twinturbo/harness/issues/15). + decrement 'foo' + decrement 'foo', 5 -```ruby -class MyClass - def important_method(stuff) - ActiveSupport::Notifications.instrument "important_method.my_class", :counter => true do - do_important_stuff - end - end -end -``` + count 'foo', 1000 -The instruments name will be sent as the name (`important_method.my_class`) -for that gauge or counter. - -Note that `ActiveSupport::Notifications.instrument` doesn't require -a block. This can be useful when you are taking an instant measurement. - -```ruby -class MyClass - def important_method(stuff) - ActiveSupport::Notifications.instrument "important_method.my_class", :counter => true - end -end -``` - -Harness will do all the extra work in sending these metrics to whatever -service you're using. - -Once you the counters are you are instrumented, then you can meter them. -Meters allow you take arbitrary readings of counter rates. The results -return a gauge so they can be logged as well. - -```ruby -# Define a counter -class MyClass - def important_method(stuff) - ActiveSupport::Notifications.instrument "important_method.my_class", :counter => true do - do_important_stuff + time 'foo' do + # do hard work end end end - -# Now you can meter it -meter = Harness::Meter.new('important_method.my_class') -meter.per_second # returns a gauge -meter.per_second.value # if you just want the number -meter.per(1.hour).value # You can use your own interval -meter.per_minute -meter.per_hour ``` -## Customizing +That's all there is to it! -You can pass a hash to `:counter` or `:gauge` to initialize the -measurement your own way. +## Configuring -If you pass a value attribute to a gauge, it will be the value -sent instead of the duration of the block. +Harness has two configuration options: the queue and collector. +`Harness::AsyncQueue` is the default queue. This means all metrics are +logged in a separate thread to never block the main thread. This makes +harness more performant in high traffic scenarios. +`Harness::NullCollector`. There is also a `Harness::SyncQueue` +useful for testing (but really used in practice). -```ruby -class MyClass - def important_method(stuff) - ActiveSupport::Notifications.instrument "important_method.my_class", - :gauge => { :id => 'custom-id', :name => "My Measurement", - :value => my_current_val, :units => 'cogs' } do - do_important_stuff - end - end -end ``` - -## One Off Gauges and Counters - -You can instantiate `Harness::Counter` and `Harness::Gauge` wherever you -want. Events from `ActiveSupport` are just converted to these classes -under the covers anyways. You can use these class if you want to take -periodic measurements or tracking something that happens outside the -application. - -```ruby -gauge = Harness::Gauge.new -gauge.id = "foo.bar" -gauge.name = "Foo's Bar" -gauge.time # defaults to Time.now -gauge.value = readings_from_my_server -gauge.units = 'bytes' -gauge.log - -counter = Harness::Counter.new -counter.id = "foo.bar" -counter.name = "# of Foo bars" -counter.time # defaults to Time.now -counter.value = read_total_users_in_database -counter.log - -### Both classes take an option hash - -gauge = Harness::Gauge.new :time => Time.now, :id => 'foo.bar' -counter = Harness::Counter.new :time => Time.now, :id => 'foo.bar' +Harness.collector = Statsd.new 'something.com' +Harness.queue = Harness::AsyncQueue ``` -## Configuration - -### Librato - -```ruby -require 'harness/adapters/librato_adapter' - -Harness.config.adapter = :librato - -Harness.config.librato.email = 'example@example.com' -Harness.config.librato.token = 'your-api-key' - -``` - -### StatsD - -Harness does **not** configure StatsD for you. It uses the StatsD class -under the covers. If you've already configured that in your own way, great. -If not, you can use the configuration proxy as described below. You -must also add `statsd-instrument` to your `Gemfile`. This is a soft -dependency that is not installed for you. - -```ruby -require 'harness/adapters/statsd_adapter' - -Harness.config.adapter = :statsd - -# Harness.config.statsd is a proxy for the StatsD class -Harness.config.statsd.host = 'localhost' -Harness.config.statsd.port = '8080' -Harness.config.statsd.default_sample_rate = 0.1 -Harness.config.statsd.logger = Rails.logger - -# You can assign your own StatsD implementation -# by setting the "backend" attribute -Harness.config.statsd.backend = CustomStatsD -``` - -### Stathat - -```ruby -require 'harness/adapters/stathat_adapter' - -Harness.config.adapter = :stathat - -Harness.config.stathat.ezkey = 'example@example.com' -``` - -## Rails Integration - -Harness will automatically log metrics coming from `ActionPack`, -`ActiveRecord`, and `ActionMailer`. `ActiveSupport` instrumentation is -disabled by default. First require your adapter in an initializer -Harness no longer requires adapters with external dependencies for -you. Create a `config/initializers/harness.rb` and require your -adapter in it: - -```ruby -require 'harness/adapters/statsd_adapter' -# other configuration -``` - -Also, custom integrations are disabled by default. -You can turn on instrumentation for specific components like so: - -```ruby -config.harness.instrument.action_controller = false -config.harness.instrument.active_support = true -config.harness.instrument.sidekiq = true -config.harness.instrument.active_model_serializers = true -``` - -You can configure Harness from `application.rb` - -```ruby -config.harness.adapter = :librato -config.harness.librato.email = 'example@example.com' -config.harness.librato.token = 'your-api-key' -``` - -Redis will be automatically configured if you `REDISTOGO_URL` or -`REDIS_URL` environment variables at set. They are wrapped in a -namespace so there will be no conflicts. If they are not present, the -default values are used. You can customize this in an initializer: - -```ruby -# config/initializers/harness.rb -require 'erb' - -file = Rails.root.join 'config', 'resque.yml' -config = YAML.load(ERB.new(File.read(Rails.root.join('config', 'redis.yml'))).result) - -Harness.redis = Redis::Namespace.new('harness', :redis => Redis.connect(:url => config[Rails.env])) -``` - -`rake harness:reset_counters` is also added. - -### Rails Environments - -Measurements are completely ignored in the test env. They are processed -in development mode, but not sent to the external service. Everything is -logged in production. - -### Background Processing - -Harness integrates automatically with Resque or Sidekiq. This is because -reporting measurements can take time and add unnecessary overhead to the -response time. If neither of these libraries are present, measurements -**will be posted in realtime.** You can set your own queue by -specifying a class like so: - -```ruby -Harness.config.queue = MyCustomQueue -``` - ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Added some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request +