Overview
Camayoc is a flexible way to keep track of application stats. Inspired by various logging frameworks (especially Log4r) it makes it easy to send stats information to multiple destinations via the use of Handlers. Right out of the box, it supports sending stats to the following:
- A statsd daemon for ridiculously easy Graphite stats
- An in-memory hash for in-process stats
- A logger or IO stream
- Redis (EXPERIMENTAL - you'll need the redis gem)
Philosophy
Application stats are critical. But even critical things get ignored if they're hard (believe me, we know). Stats should be easy:
- Collecting stats should take just one line of code
- All stat collection should be fire-and-forget with no error handling required
- Organizing stats should be easy
- There should be essentially no performance cost to collecting stats
Examples
Here's all it takes to fire stats to statsd.
require 'camayoc'
# Grab a stats instance
stats = Camayoc["my_app"]
# Add a handler for statsd
stats.add(Camayoc::Handlers::Statsd.new(:host=>"localhost",:port=>1234))
# later in your app
def do_some_stuff
# Do some stuff
Camayoc["my_app"].increment("did_stuff")
end
Your stat will be sent to statsd with the name "my_app.did_stuff". See the statsd docs for more information about how that gets translated into Graphite.
Namespaces
Many logging frameworks support the concept of namespaced logs that "extend" other logs. This makes it easy to log messages from different areas of your app and stay sane. Camayoc does this as well.
Let's say you have a service within your app where you want to store some timing data.
stats = Camayoc["my_app"]
stats.add(Camayoc::Handlers::Statsd.new(:host=>"localhost",:port=>1234))
class AwesomeService
def be_awesome
start_time = Time.now.to_f
# Be, you know, awesome
ms = ((Time.now_to_f - start_time) * 1000).round
Camayoc["my_app:awesome_service"].timing("awesome_duration",ms)
end
end
This will automatically create a timing metric in graphite via statsd called "my_app.awesome_service.awesome_duration". It does this by using the statsd handler already configured for its "my_app" parent. Now, about handlers...
Handlers
Just like loggers can have multiple outputters, you might want to send your stats to different places.
app_stats = Camayoc["my_app"]
app_stats.add(Camayoc::Handlers::Statsd.new(:host=>"localhost",:port=>1234))
foo_stats = Camayoc["my_app:foo"]
foo_stats.add(Camayoc::Handlers::Redis.new(:host=>"localhost"))
app_stats.count("bar",1000) # Stats end up in statsd
foo_stats.count("baz",150) # Stats go to redis *and* statsd
Filters
Sometimes you may want to send only certain stats to certain places.
app_stats = Camayoc["my_app"]
app_stats.add(Camayoc::Handlers::Statsd.new(:host=>"localhost",:port=>1234))
foo_stats = Camayoc["my_app:foo"]
foo_stats.add(Camayoc::Handlers::Redis.new(:host=>"localhost"),:when=>/baz/)
foo_stats.increment("baz",1000) #Stats go to redis and statsd
foo_stats.increment("bar",5) #Stats only go to statsd, not redis
There are other options as well like :if and :unless that take Procs that can be executed to determine if a metric should be sent to the specified handler. See Camayoc::Handlers::Filter for more.
Available Handlers
Statsd
Class: Camayoc::Handlers::Statsd
This handler sends data to the statd daemon for use in graphite. If you can get graphite and statsd going, then you'll get pretty graphs.
Memory
Class: Camayoc::Handlers::Memory
Stores counts and timing data in an in-memory hash. This might be handy for storing some temporary in-process stats.
Logger
Class: Camayoc::Handlers::Logger
Writes out stat values and a timestamp to a logger instance for later analysis. The format and method called on the logger can be controlled by constructor arguments. See the class for details.
IO
Class: Camayoc::Handlers::IO
Writes out stats values and a timestamp to some stream of your choice. See class for configuration details.
Redis (Experimental)
Class: Camayoc::Handlers::Redis (require "camayoc/handlers/redis" first)
The redis gem is required to use this handler. This is a very, very simple implementation that stores some data in redis. It is in no way a full-fledged time-based stats system. It does make it easy to collect some simple counts and some timing data. You can easily retrieve the stored data from redis by using the #get method.
Implementing a Handler
Let's say you want to implement your own handler, pehaps to MongoDB. Handlers just need to respond to a simple interface. See Camayoc::Handlers::StatEvent for info on the argument to the two methods.
class SomeHandler
def count(stat_event)
# Do something
end
def timing(stat_event)
# Do something
end
end
If you write a handler and would like it included in Camayoc, please fork and send a pull request and we'll get it integrated in.