graphite 0.1.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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/HISTORY.rdoc +4 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +54 -0
- data/TODO.txt +1 -0
- data/VERSION +1 -0
- data/examples/example_daemon.rb +36 -0
- data/graphite.gemspec +64 -0
- data/lib/graphite.rb +6 -0
- data/lib/graphite/client.rb +109 -0
- data/lib/graphite/event_machine_handler.rb +33 -0
- data/lib/graphite/logger.rb +40 -0
- data/test/helper.rb +9 -0
- data/test/test_graphite.rb +7 -0
- metadata +93 -0
data/.document
ADDED
data/.gitignore
ADDED
data/HISTORY.rdoc
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 CapitalThought
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= graphite
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
+
* Send me a pull request. Bonus points for topic branches.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2010 CapitalThought. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "graphite"
|
8
|
+
gem.summary = gem.description = %Q{Ruby client for sending stats to Graphite}
|
9
|
+
gem.email = "mike@capitalthought.com"
|
10
|
+
gem.homepage = "http://github.com/otherinbox/graphite"
|
11
|
+
gem.authors = ["Ian Ragsdale", "Mike Subelsky"]
|
12
|
+
gem.add_dependency "eventmachine"
|
13
|
+
gem.add_dependency "rufus-scheduler"
|
14
|
+
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
Rake::TestTask.new(:test) do |test|
|
24
|
+
test.libs << 'lib' << 'test'
|
25
|
+
test.pattern = 'test/**/test_*.rb'
|
26
|
+
test.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rcov/rcovtask'
|
31
|
+
Rcov::RcovTask.new do |test|
|
32
|
+
test.libs << 'test'
|
33
|
+
test.pattern = 'test/**/test_*.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
task :test => :check_dependencies
|
43
|
+
|
44
|
+
task :default => :test
|
45
|
+
|
46
|
+
require 'rake/rdoctask'
|
47
|
+
Rake::RDocTask.new do |rdoc|
|
48
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
49
|
+
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "graphite #{version}"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
end
|
data/TODO.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
* Write tests
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
ENV["RAILS_ENV"] ||= "production"
|
3
|
+
require File.dirname(__FILE__) + "/../../config/environment"
|
4
|
+
|
5
|
+
Thread.abort_on_exception = true
|
6
|
+
EM.threadpool_size = 1
|
7
|
+
|
8
|
+
Graphite::Client.new(GRAPHITE_SERVER, "yourapp.#{Rails.env}.global") do |graphite|
|
9
|
+
|
10
|
+
REPLICATION_SECONDS_BEHIND_OID = '.1.3.6.1.4.1.8072.1.3.2.4.1.2.11.115.108.97.118.101.83.116.97.116.117.115.6'
|
11
|
+
|
12
|
+
graphite.metric "health.database.slave_seconds_behind", 5.minutes do
|
13
|
+
`/usr/bin/snmpget -v2c -c oibsnmp db.example.com #{REPLICATION_SECONDS_BEHIND_OID}`.split.last.to_f
|
14
|
+
end
|
15
|
+
|
16
|
+
graphite.metric "users.new_past_week", 15.minutes do
|
17
|
+
User.count(:conditions => ["created_at >= '?'",7.days.ago])
|
18
|
+
end
|
19
|
+
|
20
|
+
graphite.metric "users.new_past_day", 15.minutes do
|
21
|
+
User.count(:conditions => ["created_at >= '?'",1.day.ago])
|
22
|
+
end
|
23
|
+
|
24
|
+
graphite.metric "users.new_past_hour", 1.minutes, :shifts => ['24h', '7d', '30d','90d'] do
|
25
|
+
User.count(:conditions => ["created_at >= '?'",1.hour.ago])
|
26
|
+
end
|
27
|
+
|
28
|
+
graphite.metric "users.active", 5.minutes, :shifts => ['24h','7d','30d','90d'] do
|
29
|
+
User.count(:conditions => "active = 1")
|
30
|
+
end
|
31
|
+
|
32
|
+
graphite.metric "users.inactive", 5.minutes do
|
33
|
+
User.count(:conditions => "active = 0")
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/graphite.gemspec
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{graphite}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Ian Ragsdale", "Mike Subelsky"]
|
12
|
+
s.date = %q{2010-02-03}
|
13
|
+
s.description = %q{Ruby client for sending stats to Graphite}
|
14
|
+
s.email = %q{mike@capitalthought.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"HISTORY.rdoc",
|
23
|
+
"LICENSE",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"TODO.txt",
|
27
|
+
"VERSION",
|
28
|
+
"examples/example_daemon.rb",
|
29
|
+
"graphite.gemspec",
|
30
|
+
"lib/graphite.rb",
|
31
|
+
"lib/graphite/client.rb",
|
32
|
+
"lib/graphite/event_machine_handler.rb",
|
33
|
+
"lib/graphite/logger.rb",
|
34
|
+
"test/helper.rb",
|
35
|
+
"test/test_graphite.rb"
|
36
|
+
]
|
37
|
+
s.homepage = %q{http://github.com/otherinbox/graphite}
|
38
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
39
|
+
s.require_paths = ["lib"]
|
40
|
+
s.rubygems_version = %q{1.3.5}
|
41
|
+
s.summary = %q{Ruby client for sending stats to Graphite}
|
42
|
+
s.test_files = [
|
43
|
+
"test/helper.rb",
|
44
|
+
"test/test_graphite.rb",
|
45
|
+
"examples/example_daemon.rb"
|
46
|
+
]
|
47
|
+
|
48
|
+
if s.respond_to? :specification_version then
|
49
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
50
|
+
s.specification_version = 3
|
51
|
+
|
52
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
53
|
+
s.add_runtime_dependency(%q<eventmachine>, [">= 0"])
|
54
|
+
s.add_runtime_dependency(%q<rufus-scheduler>, [">= 0"])
|
55
|
+
else
|
56
|
+
s.add_dependency(%q<eventmachine>, [">= 0"])
|
57
|
+
s.add_dependency(%q<rufus-scheduler>, [">= 0"])
|
58
|
+
end
|
59
|
+
else
|
60
|
+
s.add_dependency(%q<eventmachine>, [">= 0"])
|
61
|
+
s.add_dependency(%q<rufus-scheduler>, [">= 0"])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
data/lib/graphite.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'rufus-scheduler'
|
3
|
+
|
4
|
+
module Graphite
|
5
|
+
class Client
|
6
|
+
|
7
|
+
# Expects a string in the form of "hostname:port_num" where port_num is optional, and a prefix
|
8
|
+
# to identify this server. Example:
|
9
|
+
# Graphite::Client.new("graphite.example.com", "yourapp.#{Rails.env}.instances.#{hostname}.#{$$}")
|
10
|
+
|
11
|
+
def initialize(server, prefix)
|
12
|
+
@logger = Graphite::Logger.new(server)
|
13
|
+
@prefix = prefix
|
14
|
+
@metrics = {}
|
15
|
+
@counters = {}
|
16
|
+
@shifts = {}
|
17
|
+
|
18
|
+
if block_given?
|
19
|
+
@scheduler = Rufus::Scheduler::EmScheduler.start_new
|
20
|
+
yield self
|
21
|
+
start_logger_timer
|
22
|
+
@scheduler.join
|
23
|
+
else
|
24
|
+
Graphite::EventMachineHandler.ensure_running
|
25
|
+
@scheduler = Rufus::Scheduler::EmScheduler.start_new
|
26
|
+
start_logger_timer
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def metric(name, frequency = 1.minute, options = {})
|
31
|
+
add_shifts(name,options[:shifts]) if options[:shifts]
|
32
|
+
@scheduler.every(frequency, :first_in => '1m') do
|
33
|
+
result = yield
|
34
|
+
log({name => result})
|
35
|
+
cleanup
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_shifts(name, shifts)
|
40
|
+
shifts.each do |seconds|
|
41
|
+
@shifts[seconds] ||= []
|
42
|
+
@shifts[seconds] << name
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def metrics(frequency = 1.minute)
|
47
|
+
@scheduler.every(frequency, :first_in => '1m') do
|
48
|
+
results = yield
|
49
|
+
log(results)
|
50
|
+
cleanup
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def increment!(counter, n = 1)
|
55
|
+
full_counter = "#{@prefix}.#{counter}"
|
56
|
+
@counters[full_counter] ||= 0
|
57
|
+
@counters[full_counter] += n
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def log(results)
|
63
|
+
results.keys.each do |k,v|
|
64
|
+
@metrics["#{@prefix}.#{k}"] = results.delete(k)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def send_counters
|
69
|
+
to_send = {}
|
70
|
+
@counters.keys.each do |k|
|
71
|
+
to_send[k] = @counters.delete(k)
|
72
|
+
end
|
73
|
+
@logger.log(Time.now, to_send) if to_send.size > 0
|
74
|
+
end
|
75
|
+
|
76
|
+
def send_metrics
|
77
|
+
@logger.log(Time.now, @metrics) if @metrics.size > 0
|
78
|
+
send_counters
|
79
|
+
send_shifts
|
80
|
+
end
|
81
|
+
|
82
|
+
# Sends metrics into the future
|
83
|
+
def send_shifts
|
84
|
+
@shifts.each do |time, metrics|
|
85
|
+
to_send = {}
|
86
|
+
|
87
|
+
metrics.each do |k|
|
88
|
+
key = "#{@prefix}.#{k}"
|
89
|
+
shifted_key = key + "_shifted.#{time}_ago"
|
90
|
+
to_send[shifted_key] = @metrics[key]
|
91
|
+
end
|
92
|
+
|
93
|
+
seconds = Rufus.parse_time_string time
|
94
|
+
@logger.log(Time.now + seconds,to_send)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def start_logger_timer
|
99
|
+
@scheduler.every("60s", :blocking => true) do
|
100
|
+
send_metrics
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Blocks get run in a threadpool -- sharing is caring.
|
105
|
+
def cleanup
|
106
|
+
ActiveRecord::Base.clear_active_connections! if defined?(ActiveRecord::Base)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# This code originally written by Bob Potter is extracted from OtherInbox
|
2
|
+
# TODO EMH code should be spun out into its own gem
|
3
|
+
|
4
|
+
module Graphite
|
5
|
+
class EventMachineHandler
|
6
|
+
|
7
|
+
def self.ensure_running
|
8
|
+
start_em unless EventMachine.reactor_running?
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def self.start_em
|
14
|
+
em = Thread.new { EM.run{} }
|
15
|
+
|
16
|
+
# We want to fail if there is an exception in EM otherwise we're dead in the water
|
17
|
+
em.abort_on_exception = true
|
18
|
+
|
19
|
+
%w(INT TERM).each do |sig|
|
20
|
+
old = trap(sig) do
|
21
|
+
EM.stop if EM.reactor_running?
|
22
|
+
|
23
|
+
if old.respond_to? :call
|
24
|
+
old.call
|
25
|
+
else
|
26
|
+
old
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module Graphite
|
4
|
+
class Logger
|
5
|
+
attr_accessor :logger
|
6
|
+
|
7
|
+
DEFAULT_PORT = 2003
|
8
|
+
|
9
|
+
# Initialize a new Graphite::Logger class; expects a string containing a DNS name for the Graphite server.
|
10
|
+
# This hostname may optional include a port number, e.g. "graphite.example.com:3333". If not specified,
|
11
|
+
# DEFAULT_PORT will be used. If you specify a Ruby-compatible logger object in the second parameter,
|
12
|
+
# a string containing the graphite message will be logged there before it is sent to the socket.
|
13
|
+
|
14
|
+
def initialize(server_host, logger = nil)
|
15
|
+
@server = server_host
|
16
|
+
@logger = logger
|
17
|
+
end
|
18
|
+
|
19
|
+
def socket
|
20
|
+
if @socket.nil? || @socket.closed?
|
21
|
+
host, port = @server.split(/:/)
|
22
|
+
port ||= DEFAULT_PORT
|
23
|
+
@socket = TCPSocket.new(host, port)
|
24
|
+
end
|
25
|
+
@socket
|
26
|
+
end
|
27
|
+
|
28
|
+
# Write a bunch of values to the server taken at the given time
|
29
|
+
def log(time, measurements)
|
30
|
+
message = ""
|
31
|
+
measurements.each do |key, value|
|
32
|
+
raise "Measurement is not numeric" unless value.respond_to? :to_f
|
33
|
+
message << "#{key} #{value.to_f} #{time.to_i}\n"
|
34
|
+
end
|
35
|
+
logger.info("Graphite: #{message}") if logger
|
36
|
+
socket.write(message)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
data/test/helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: graphite
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ian Ragsdale
|
8
|
+
- Mike Subelsky
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2010-02-03 00:00:00 -05:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: eventmachine
|
18
|
+
type: :runtime
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
version:
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rufus-scheduler
|
28
|
+
type: :runtime
|
29
|
+
version_requirement:
|
30
|
+
version_requirements: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
version:
|
36
|
+
description: Ruby client for sending stats to Graphite
|
37
|
+
email: mike@capitalthought.com
|
38
|
+
executables: []
|
39
|
+
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files:
|
43
|
+
- LICENSE
|
44
|
+
- README.rdoc
|
45
|
+
files:
|
46
|
+
- .document
|
47
|
+
- .gitignore
|
48
|
+
- HISTORY.rdoc
|
49
|
+
- LICENSE
|
50
|
+
- README.rdoc
|
51
|
+
- Rakefile
|
52
|
+
- TODO.txt
|
53
|
+
- VERSION
|
54
|
+
- examples/example_daemon.rb
|
55
|
+
- graphite.gemspec
|
56
|
+
- lib/graphite.rb
|
57
|
+
- lib/graphite/client.rb
|
58
|
+
- lib/graphite/event_machine_handler.rb
|
59
|
+
- lib/graphite/logger.rb
|
60
|
+
- test/helper.rb
|
61
|
+
- test/test_graphite.rb
|
62
|
+
has_rdoc: true
|
63
|
+
homepage: http://github.com/otherinbox/graphite
|
64
|
+
licenses: []
|
65
|
+
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options:
|
68
|
+
- --charset=UTF-8
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: "0"
|
76
|
+
version:
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: "0"
|
82
|
+
version:
|
83
|
+
requirements: []
|
84
|
+
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.3.5
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Ruby client for sending stats to Graphite
|
90
|
+
test_files:
|
91
|
+
- test/helper.rb
|
92
|
+
- test/test_graphite.rb
|
93
|
+
- examples/example_daemon.rb
|