# AllSeeingEye A highly configurable Gem to collect specific data from requests, and view that data in a cute little Sinatra app. AllSeeingEye writes information about each request it observes into Redis. What kind of information? Why, whatever you want! From the relatively banal (IP address, URI, user agent) to complicated application-specific data (user account, ordered product, money spent), anything can be recorded by AllSeeingEye, stored in Redis, and indexed to be viewed quickly in Sinatra. With pretty Flot-based graphs, even. It comes with a Rails2 integration by default, but I think with a little work it'd be pretty easy to get it into Rails3. ## Background Our application receives about half a million requests every day. While we have Google Analytics, and it's totally awesome, many of those requests come from our iPhone app, and those requests don't receive the level of tracking we need. We could load reporting software onto a users' iOS device: but, ethical issues aside, why would we want to? Almost anything we'd be interested in tracking requires server interaction anyway, so it's easier and faster to track it all on the server side. The obvious solution was to mine the Rails logfiles, but we have multiple servers and weaving all the logs was a pain -- and even when we did, some of the data we wanted wasn't exposed. My next thought was to log requests to our database, but we experience enough database traffic as is without writing every request. A MemCached-backed write-through buffer temporarily worked but when the writes happened the database still got very unhappy... and the table got enormous very quickly. Redis was a good compromise though. We use it for the truly excellent Resque, so it's already part of our stack. It's fast and, while it's persistent, it's not exactly a tragedy if the Redis DB gets hosed, as none of the request data is so important we can't just start collecting it again. Combined with some code to store arbitrary data, we quickly had a winner on our hands. So, AllSeeingEye was born out of our need to track incoming requests accurately, quickly, and in a way that is easily displayable to our business users. ## Installation Get Redis via the amazing Homebrew: `brew install redis` Add AllSeeingEye to your project's Gemfile: `gem 'all\_seeing\_eye'` Install it with Bundler: `bundle install` ## Setup bundle exec ruby script/generate all\_seeing\_eye This puts the AllSeeingEye config file at config/all\_seeing\_eye.yml. The file is annotated with all the information you need to get started, but some quick explanation: ### Timeout AllSeeingEye's data collection is wrapped by a timeout to ensure speediness. I keep this value at 30 milliseconds (0.03), but feel free to change it to suit your application's needs. ### Round To Seconds One data point per request is too many to sensibly view on a graph. This rounds the created\_at time of requests to whatever (I choose 10 minutes), so that all requests within 10 minutes are collected and displayed together. ### Redis Environment and then location of the redis server. If you do not include this key, AllSeeingEye tries to look for a redis.yml or a resque.yml file instead to find the server. ### Models You can define a number of different models to collect data with. Each has its own separate namespace, so data won't be shared between them. I only use one right now (I call it 'request'), but if you want more then go for it. Each model definition should be structured like this: model\_name: first\_field\_name: object: object_name method: .method_name second\_field\_name: object: hash method: "['key']" Model names and field names are completely arbitrary, and I try to humanize them as best as I can. Object and method are the literal object and the actual method you want executed, with one exception: the object 'controller' is translated into 'self'. The result of this is stored by AllSeeingEye in redis. So, for example, an object could be "request" and the method could be "request\_uri". I'm trying to do a little secure sanity-checking here rather than just directly eval'ing a string, but you can still mess things up pretty badly if you choose a bad object and/or bad method... so try not to do that. As a helpful hint, recording "request\_uri" isn't nearly as helpful as you might think. Try to save requests without any params and without a leading slash by defining a method in ApplicationController instead: protected def clean_request request.request_uri.split('?').first[1..-1] end Then, in your all\_seeing\_eye.yml: request: uri: object: controller method: .clean_request ## Running When your Rails application starts, you should see each request contain "+++ Request watched by AllSeeingEye." That means AllSeeingEye is running successfully. Good job! Starting the server is simple. Just type `all-seeing-eye` from the commandline and Sinatra should start automatically. ## Extras I included the Nginx config we use for AllSeeingEye as a small bonus. It's in examples/all\_seeing\_eye-nginx.conf. ## Contributing to AllSeeingEye * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it * Fork the project * Start a feature/bugfix branch * Commit and push until you are happy with your contribution * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally. * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it. ## Copyright Copyright (c) 2011 Josh Symonds. See LICENSE.txt for further details.