Permalink
Browse files

Initial import.

  • Loading branch information...
jqr committed Oct 27, 2011
0 parents commit 99be073bf74fe932eee900fa5939c6d7bb75fab6
@@ -0,0 +1,5 @@
+.DS_Store
+Thumbs.db
+Gemfile.lock
+pkg/
+*.gem
@@ -0,0 +1,3 @@
+source :rubygems
+
+gemspec
@@ -0,0 +1,5 @@
+guard 'rspec', :version => 2 do
+ watch(%r{^spec/.+_spec\.rb$})
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
+ watch(%r{spec/(spec_helper|test_server).rb}) { "spec/" }
+end
@@ -0,0 +1,35 @@
+= Instrumental Agent
+
+Instrument anything.
+
+== Setup
+
+Add the gem to your Gemfile.
+
+ gem 'instrumental-agent'
+
+Head over to instrumentalapp.com and setup an account, then
+initialize the agent.
+
+ I = Instrumental::Agent.new('YOUR_API_KEY')
+
+ # or, if you're using eventmachine already
+
+ I = Instrumental::Agent.new('YOUR_API_KEY', :start_reactor => false)
+
+Now you can begin to use instrumental to track your application
+
+ I.gauge('load', 1.23)
+ I.increment('signups')
+
+Data without historical context sucks, so Instrumental lets you
+backfill data to. Letting you see deep into your past.
+
+ User.find_each do |user|
+ I.increment('signups', 1, user.created_at)
+ end
+
+Running under Rails? You can also give our experimental rack middleware
+a shot by initializing it with:
+
+ Instrumental::Middleware.boot
@@ -0,0 +1 @@
+require 'bundler/gem_tasks'
@@ -0,0 +1,151 @@
+#!/usr/bin/env ruby
+
+require 'rubygems'
+$: << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
+require 'instrumental_agent'
+
+
+class SystemInspector
+ TYPES = [:gauges, :incrementors]
+ attr_accessor *TYPES
+
+ def initialize
+ @gauges = {}
+ @incrementors = {}
+ @platform =
+ case RUBY_PLATFORM
+ when /linux/
+ Linux
+ when /darwin/
+ OSX
+ else
+ raise "unsupported OS"
+ end
+ end
+
+ def load_all
+ load @platform.load_cpu
+ load @platform.load_memory
+ load @platform.load_disks
+ end
+
+ def load(stats)
+ @gauges.merge!(stats[:gauges] || {})
+ end
+
+ module OSX
+ def self.load_cpu
+ { :gauges => top }
+ end
+
+ def self.top
+ lines = []
+ processes = date = load = cpu = nil
+ IO.popen('top -l 1 -n 0') do |top|
+ processes = top.gets.split(': ')[1]
+ date = top.gets
+ load = top.gets.split(': ')[1]
+ cpu = top.gets.split(': ')[1]
+ end
+
+ user, system, idle = cpu.split(", ").map { |v| v.to_f }
+ load1, load5, load15 = load.split(", ").map { |v| v.to_f }
+ total, running, stuck, sleeping, threads = processes.split(", ").map { |v| v.to_i }
+
+ {
+ 'cpu.user' => user,
+ 'cpu.system' => system,
+ 'cpu.idle' => idle,
+ 'load.1min' => load1,
+ 'load.5min' => load5,
+ 'load.15min' => load15,
+ 'processes.total' => total,
+ 'processes.running' => running,
+ 'processes.stuck' => stuck,
+ 'processes.sleeping' => sleeping,
+ 'threads' => threads,
+ }
+ end
+
+ def self.load_memory
+ # TODO: swap
+ { :gauges => vm_stat }
+ end
+
+ def self.vm_stat
+ header, *rows = `vm_stat`.split("\n")
+ page_size = header.match(/page size of (\d+) bytes/)[1].to_i
+ sections = ["free", "active", "inactive", "wired", "speculative", "wired down"]
+ output = {}
+ total = 0.0
+ rows.each do |row|
+ if match = row.match(/Pages (.*):\s+(\d+)\./)
+ section, value = match[1, 2]
+ if sections.include?(section)
+ value = value.to_f * page_size / 1024 / 1024
+ output["memory.#{section.gsub(' ', '_')}_mb"] = value
+ total += value
+ end
+ end
+ end
+ output["memory.free_percent"] = output["memory.free_mb"] / total * 100 # TODO: verify
+ output
+ end
+
+ def self.load_disks
+ { :gauges => df }
+ end
+
+ def self.df
+ output = {}
+ `df -k`.split("\n").grep(%r{^/dev/}).each do |line|
+ device, total, used, available, capacity, mount = line.split(/\s+/)
+ names = [File.basename(device)]
+ names << 'root' if mount == '/'
+ names.each do |name|
+ output["disk.#{name}.total_mb"] = total.to_f / 1024
+ output["disk.#{name}.used_mb"] = used.to_f / 1024
+ output["disk.#{name}.available_mb"] = available.to_f / 1024
+ output["disk.#{name}.available_percent"] = available.to_f / total.to_f * 100
+ end
+ end
+ output
+ end
+
+ def self.netstat(interface = 'en1')
+ # mostly functional network io stats
+ headers, *lines = `netstat -ibI #{interface}`.split("\n").map { |l| l.split(/\s+/) } # FIXME: vulnerability?
+ headers = headers.map { |h| h.downcase }
+ lines.each do |line|
+ if !line[3].include?(':')
+ return Hash[headers.zip(line)]
+ end
+ end
+ end
+ end
+end
+
+# TODO: swap
+# TODO: utilization
+
+
+token, collector = *ARGV
+unless token
+ puts "Usage: #{$0} <token> [collector]"
+ exit 1
+end
+I = Instrumental::Agent.new(token, :collector => collector)
+
+host = `hostname`.chomp
+
+puts "Collecting stats under the hostname: #{host}"
+
+loop do
+ inspector = SystemInspector.new
+ inspector.load_all
+ inspector.gauges.each do |stat, value|
+ I.gauge("#{host}.#{stat}", value)
+ end
+ # I.increment("#{host}.#{stat}", delta)
+ sleep 1
+end
@@ -0,0 +1,24 @@
+$:.push File.expand_path("../lib", __FILE__)
+require "instrumental/version"
+
+Gem::Specification.new do |s|
+ s.name = "instrumental_agent"
+ s.version = Instrumental::VERSION
+ s.authors = ["Elijah Miller", "Christopher Zelenak"]
+ s.email = ["elijah.miller@gmail.com", "netshade@gmail.com"]
+ s.homepage = "http://github.com/fastestforward/instrumental_agent"
+ s.summary = %q{Agent for reporting data to instrumentalapp.com}
+ s.description = %q{Keep track of anything.}
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ s.require_paths = ["lib"]
+
+ s.add_runtime_dependency(%q<eventmachine>, [">= 0"])
+ s.add_development_dependency(%q<rspec>, ["~> 2.0"])
+ s.add_development_dependency(%q<guard>, [">= 0"])
+ s.add_development_dependency(%q<guard-rspec>, [">= 0"])
+ s.add_development_dependency(%q<growl_notify>, [">= 0"])
+ s.add_development_dependency(%q<rb-fsevent>, [">= 0"])
+end
Oops, something went wrong.

0 comments on commit 99be073

Please sign in to comment.