graphite-api 0.0.0.23 → 0.0.0.24
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +25 -15
- data/Rakefile +0 -5
- data/lib/graphite-api.rb +14 -12
- data/lib/graphite-api/buffer.rb +65 -48
- data/lib/graphite-api/cli.rb +57 -0
- data/lib/graphite-api/client.rb +23 -12
- data/lib/graphite-api/connector.rb +12 -6
- data/lib/graphite-api/connector_group.rb +17 -0
- data/lib/graphite-api/middleware.rb +17 -16
- data/lib/graphite-api/runner.rb +8 -47
- data/lib/graphite-api/scheduler.rb +1 -1
- data/lib/graphite-api/utils.rb +25 -4
- data/lib/graphite-api/version.rb +1 -18
- data/tasks/build.rake +1 -20
- data/tasks/tests.rake +8 -3
- metadata +7 -5
data/README.md
CHANGED
@@ -1,21 +1,24 @@
|
|
1
|
-
# GraphiteAPI
|
2
|
-
A Ruby API toolkit for [Graphite](http://graphite.wikidot.com/)
|
1
|
+
# GraphiteAPI [Beta]
|
2
|
+
A Ruby API toolkit for [Graphite](http://graphite.wikidot.com/).
|
3
3
|
|
4
4
|
## Description
|
5
|
-
Graphite
|
5
|
+
Graphite client and utilities for ruby
|
6
6
|
|
7
7
|
* **Simple** client for ruby.
|
8
|
-
*
|
9
|
-
*
|
10
|
-
*
|
8
|
+
* Ships with a **GraphiteAPI-Middleware**, which is a lightweight, event-driven, aggregator daemon.
|
9
|
+
* only one dependency (EventMachine).
|
10
|
+
* Utilities like scheduling and caching.
|
11
|
+
|
12
|
+
## Features
|
13
|
+
* Multiple Graphite servers support - GraphiteAPI-Middleware supports sending aggregate data to multiple graphite servers, useful for large data centers and backup purposes
|
14
|
+
* Reanimation mode - support cases which the same keys (with the same timestamps) can be received simultaneously and asynchronous from multiple input sources, in that cases GraphiteAPI-Middleware will "reanimate" old records that were already sent to Graphite server, and will send the sum of the reanimate record value + value of the just received record, to graphite server; this new summed record should override the key with the new value on Graphite database.
|
11
15
|
|
12
16
|
## Client Usage
|
13
17
|
```ruby
|
14
18
|
require 'graphite-api'
|
15
19
|
|
16
20
|
client = GraphiteAPI::Client.new(
|
17
|
-
:
|
18
|
-
:port => 2003,
|
21
|
+
:graphite => "graphite.example.com:2003",
|
19
22
|
:prefix => ["example","prefix"], # add example.prefix to each key
|
20
23
|
:interval => 60 # send to graphite every 60 seconds
|
21
24
|
)
|
@@ -39,23 +42,25 @@ Graphite Client and Utilities For Ruby
|
|
39
42
|
|
40
43
|
client.join # wait...
|
41
44
|
```
|
42
|
-
## Middleware Usage
|
43
|
-
|
44
|
-
`graphite-middleware --help`
|
45
|
+
## GraphiteAPI-Middleware Usage
|
45
46
|
|
46
47
|
```
|
47
|
-
|
48
|
+
[root@someplace]# graphite-middleware --help
|
49
|
+
|
50
|
+
GraphiteAPI Middleware Server
|
48
51
|
|
49
52
|
Usage: graphite-middleware [options]
|
50
|
-
-g, --graphite HOST
|
53
|
+
-g, --graphite HOST:PORT graphite host, in HOST:PORT format (can be specified multiple times)
|
51
54
|
-p, --port PORT listening port (default 2003)
|
52
55
|
-l, --log-file FILE log file
|
53
56
|
-L, --log-level LEVEL log level (default warn)
|
54
57
|
-P, --pid-file FILE pid file (default /var/run/graphite-middleware.pid)
|
55
58
|
-d, --daemonize run in background
|
56
59
|
-i, --interval INT report every X seconds (default 60)
|
57
|
-
-s, --slice SECONDS send to graphite in X seconds slices (default
|
58
|
-
-
|
60
|
+
-s, --slice SECONDS send to graphite in X seconds slices (default 60)
|
61
|
+
-r, --reanimation HOURS reanimate records that are younger than X hours, please see README
|
62
|
+
|
63
|
+
More Info @ https://github.com/kontera-technologies/graphite-api
|
59
64
|
```
|
60
65
|
|
61
66
|
## Installation
|
@@ -73,6 +78,11 @@ cd graphite-api
|
|
73
78
|
rake install
|
74
79
|
```
|
75
80
|
|
81
|
+
# TODO:
|
82
|
+
* Documentation
|
83
|
+
* trap signals and shutdown server
|
84
|
+
* More tests
|
85
|
+
|
76
86
|
## Bugs
|
77
87
|
|
78
88
|
If you find a bug, feel free to report it @ our [issues tracker](https://github.com/kontera-technologies/graphite-api/issues) on github.
|
data/Rakefile
CHANGED
data/lib/graphite-api.rb
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
module GraphiteAPI
|
2
2
|
ROOT = File.expand_path(File.dirname(__FILE__))
|
3
3
|
|
4
|
-
autoload :Version,
|
5
|
-
autoload :Client,
|
6
|
-
autoload :Scheduler,
|
7
|
-
autoload :Connector,
|
8
|
-
autoload :Middleware,
|
9
|
-
autoload :Runner,
|
10
|
-
autoload :Utils,
|
11
|
-
autoload :
|
12
|
-
autoload :
|
13
|
-
|
4
|
+
autoload :Version, "#{ROOT}/graphite-api/version"
|
5
|
+
autoload :Client, "#{ROOT}/graphite-api/client"
|
6
|
+
autoload :Scheduler, "#{ROOT}/graphite-api/scheduler"
|
7
|
+
autoload :Connector, "#{ROOT}/graphite-api/connector"
|
8
|
+
autoload :Middleware, "#{ROOT}/graphite-api/middleware"
|
9
|
+
autoload :Runner, "#{ROOT}/graphite-api/runner"
|
10
|
+
autoload :Utils, "#{ROOT}/graphite-api/utils"
|
11
|
+
autoload :CLI, "#{ROOT}/graphite-api/cli"
|
12
|
+
autoload :Buffer, "#{ROOT}/graphite-api/buffer"
|
13
|
+
autoload :Logger, "#{ROOT}/graphite-api/logger"
|
14
|
+
autoload :ConnectorGroup, "#{ROOT}/graphite-api/connector_group"
|
15
|
+
|
14
16
|
def self.version
|
15
|
-
GraphiteAPI::Version
|
17
|
+
GraphiteAPI::Version::VERSION
|
16
18
|
end
|
17
19
|
|
18
|
-
end
|
20
|
+
end
|
data/lib/graphite-api/buffer.rb
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
# buff.stream "mem.usage 1"
|
10
10
|
# buff.stream "90 1326842563\n"
|
11
11
|
# buff.stream "shuki.tuki 999 1326842563\n"
|
12
|
-
# buff.each {|o| p o}
|
12
|
+
# buff.pull.each {|o| p o}
|
13
13
|
#
|
14
14
|
# Produce:
|
15
15
|
# ["load_avg", 40.0, 1326881160]
|
@@ -18,84 +18,101 @@
|
|
18
18
|
# -----------------------------------------------------
|
19
19
|
module GraphiteAPI
|
20
20
|
class Buffer
|
21
|
-
attr_reader :leftovers,:options,:new_records,:in_cache_mode
|
22
21
|
|
23
|
-
|
22
|
+
attr_reader :options,:keys_to_send,:reanimation_mode, :streamer_buff
|
23
|
+
|
24
|
+
CLOSING_STREAM_CHAR = "\n" # end of message - when streaming to buffer obj
|
25
|
+
IGNORING_CHARS = "\r" # remove these chars from message
|
26
|
+
FLOATS_ROUND_BY = 2 # round(x) after joining floats
|
27
|
+
VALID_RECORD = /^[\w|\.]+ \d+(?:\.|\d)* \d+$/ # how a valid record should look like
|
28
|
+
|
29
|
+
def initialize options
|
24
30
|
@options = options
|
25
|
-
@
|
26
|
-
@
|
27
|
-
@
|
28
|
-
start_cleaner if
|
31
|
+
@keys_to_send = Hash.new {|h,k| h[k] = []}
|
32
|
+
@streamer_buff = Hash.new {|h,k| h[k] = ""}
|
33
|
+
@reanimation_mode = !options[:reanimation_exp].nil?
|
34
|
+
start_cleaner if reanimation_mode
|
29
35
|
end
|
30
36
|
|
31
|
-
def
|
32
|
-
|
33
|
-
hash[:
|
34
|
-
|
35
|
-
new_records << [time,k]
|
36
|
-
end
|
37
|
+
def push hash
|
38
|
+
Logger.debug [:buffer,:add,hash]
|
39
|
+
time = Utils::normalize_time(hash[:time],options[:slice])
|
40
|
+
hash[:metric].each { |k,v| cache_set(time,k,v) }
|
37
41
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
data
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
alias :<< :push
|
43
|
+
|
44
|
+
def stream data, client_id = nil
|
45
|
+
data.each_char do |char|
|
46
|
+
next if invalid_char? char
|
47
|
+
streamer_buff[client_id] += char
|
48
|
+
if char == CLOSING_STREAM_CHAR
|
49
|
+
push build_metric(*streamer_buff[client_id].split) if valid streamer_buff[client_id]
|
50
|
+
streamer_buff.delete client_id
|
45
51
|
end
|
46
|
-
leftovers[client_id].clear
|
47
|
-
end
|
48
|
-
|
49
|
-
leftovers[client_id] << data.pop if got_leftovers
|
50
|
-
data.each do |line|
|
51
|
-
next unless valid line
|
52
|
-
key,val,time = line.split
|
53
|
-
self << {:metric => {key => val},:time => (Time.at(time.to_i) rescue Time.now)}
|
54
52
|
end
|
55
53
|
end
|
56
|
-
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
54
|
+
|
55
|
+
def pull as = nil
|
56
|
+
Array.new.tap do |obj|
|
57
|
+
keys_to_send.each { |t, k| k.each { |o| obj.push cache_get(t, o, as) } }
|
58
|
+
clear
|
59
|
+
end
|
61
60
|
end
|
62
61
|
|
63
62
|
def empty?
|
64
|
-
|
63
|
+
buffer_cache.empty?
|
65
64
|
end
|
66
65
|
|
67
66
|
def got_new_records?
|
68
|
-
!
|
67
|
+
!keys_to_send.empty?
|
69
68
|
end
|
70
|
-
|
69
|
+
|
71
70
|
def size
|
72
|
-
|
73
|
-
end
|
71
|
+
buffer_cache.values.map {|o| o.values}.flatten.size
|
72
|
+
end # TODO: make it less painful
|
74
73
|
|
75
74
|
private
|
75
|
+
def invalid_char?(char)
|
76
|
+
IGNORING_CHARS.include?(char)
|
77
|
+
end
|
78
|
+
|
79
|
+
def cache_set(time, key, value)
|
80
|
+
buffer_cache[time][key] = (buffer_cache[time][key] + value.to_f).round(FLOATS_ROUND_BY)
|
81
|
+
keys_to_send[time].push(key) unless keys_to_send[time].include?(key)
|
82
|
+
end
|
83
|
+
|
84
|
+
def cache_get(time, key, as)
|
85
|
+
metric = [prefix + key,buffer_cache[time][key],time]
|
86
|
+
as == :string ? metric.join(" ") : metric
|
87
|
+
end
|
88
|
+
|
89
|
+
def build_metric key, value, time
|
90
|
+
{ :metric => { key => value },:time => Time.at(time.to_i) }
|
91
|
+
end
|
92
|
+
|
76
93
|
def clear
|
77
|
-
|
78
|
-
|
94
|
+
keys_to_send.clear
|
95
|
+
buffer_cache.clear unless reanimation_mode
|
79
96
|
end
|
80
97
|
|
81
|
-
def valid
|
98
|
+
def valid data
|
82
99
|
data =~ /^[\w|\.]+ \d+(?:\.|\d)* \d+$/
|
83
100
|
end
|
84
101
|
|
85
102
|
def prefix
|
86
|
-
@prefix ||= options[:prefix].empty? ?
|
103
|
+
@prefix ||= options[:prefix].empty? ? '' : prefix_to_s
|
87
104
|
end
|
88
105
|
|
89
106
|
def prefix_to_s
|
90
|
-
|
107
|
+
Array(options[:prefix]).join('.') << '.'
|
91
108
|
end
|
92
109
|
|
93
|
-
def
|
94
|
-
@
|
110
|
+
def buffer_cache
|
111
|
+
@buffer_cache ||= Hash.new {|h,k| h[k] = Hash.new {|h1,k1| h1[k1] = 0}}
|
95
112
|
end
|
96
113
|
|
97
|
-
def clean
|
98
|
-
[
|
114
|
+
def clean age
|
115
|
+
[buffer_cache,keys_to_send].each {|o| o.delete_if {|t,k| now - t > age}}
|
99
116
|
end
|
100
117
|
|
101
118
|
def now
|
@@ -103,7 +120,7 @@ module GraphiteAPI
|
|
103
120
|
end
|
104
121
|
|
105
122
|
def start_cleaner
|
106
|
-
Scheduler.every(options[:cleaner_interval]) { clean
|
123
|
+
Scheduler.every(options[:cleaner_interval]) { clean(options[:cache_exp]) }
|
107
124
|
end
|
108
125
|
|
109
126
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
module GraphiteAPI
|
3
|
+
class CLI
|
4
|
+
|
5
|
+
def self.parse(argv, options)
|
6
|
+
OptionParser.new do |opts|
|
7
|
+
opts.banner = "GraphiteAPI Middleware Server"
|
8
|
+
opts.define_head "Usage: graphite-middleware [options]"
|
9
|
+
opts.define_head ""
|
10
|
+
|
11
|
+
opts.on("-g", "--graphite HOST:PORT","graphite host, in HOST:PORT format (can be specified multiple times)") do |graphite|
|
12
|
+
options[:backends] << Utils::expand_host(graphite)
|
13
|
+
end
|
14
|
+
|
15
|
+
opts.on("-p", "--port PORT","listening port (default #{options[:port]})") do |port|
|
16
|
+
options[:port] = port
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.on("-l", "--log-file FILE","log file") do |file|
|
20
|
+
options[:log_file] = File::expand_path(file)
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on("-L", "--log-level LEVEL","log level (default warn)") do |level|
|
24
|
+
options[:log_level] = level
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on("-P", "--pid-file FILE","pid file (default #{options[:pid]})") do |pid_file|
|
28
|
+
options[:pid] = pid_file
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on("-d", "--daemonize","run in background") do
|
32
|
+
options[:daemonize] = true
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on("-i", "--interval INT","report every X seconds (default #{options[:interval]})") do |x_seconds|
|
36
|
+
options[:interval] = x_seconds.to_i if x_seconds.to_i > 0
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on("-s", "--slice SECONDS","send to graphite in X seconds slices (default #{options[:slice]})") do |slice|
|
40
|
+
options[:slice] = slice.to_i if slice.to_i >= 0
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on("-r", "--reanimation HOURS","reanimate records that are younger than X hours, please see README") do |exp|
|
44
|
+
(options[:reanimation_exp] = exp.to_i * 3600) if exp.to_i > 0
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.separator ""
|
48
|
+
opts.separator "More Info @ https://github.com/kontera-technologies/graphite-api"
|
49
|
+
opts.separator ""
|
50
|
+
|
51
|
+
opts.define_tail ""
|
52
|
+
opts.define_tail ""
|
53
|
+
end.parse! argv
|
54
|
+
options
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/graphite-api/client.rb
CHANGED
@@ -5,8 +5,7 @@
|
|
5
5
|
# Usage
|
6
6
|
#
|
7
7
|
# client = GraphiteAPI::Client.new(
|
8
|
-
# :
|
9
|
-
# :port => 2003,
|
8
|
+
# :graphite => "graphite.example.com:2003",
|
10
9
|
# :prefix => ["example","prefix"], # add example.prefix to each key
|
11
10
|
# :interval => 60 # send to graphite every 60 seconds
|
12
11
|
# )
|
@@ -32,12 +31,14 @@
|
|
32
31
|
# -----------------------------------------------------
|
33
32
|
module GraphiteAPI
|
34
33
|
class Client
|
35
|
-
|
34
|
+
include Utils
|
36
35
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
@
|
36
|
+
attr_reader :options,:buffer,:connectors
|
37
|
+
|
38
|
+
def initialize opt
|
39
|
+
@options = build_options opt.clone
|
40
|
+
@buffer = GraphiteAPI::Buffer.new(options)
|
41
|
+
@connectors = GraphiteAPI::ConnectorGroup.new(options)
|
41
42
|
start_scheduler
|
42
43
|
end
|
43
44
|
|
@@ -46,7 +47,7 @@ module GraphiteAPI
|
|
46
47
|
end
|
47
48
|
|
48
49
|
def join
|
49
|
-
sleep
|
50
|
+
sleep while buffer.got_new_records?
|
50
51
|
end
|
51
52
|
|
52
53
|
def stop
|
@@ -58,13 +59,23 @@ module GraphiteAPI
|
|
58
59
|
end
|
59
60
|
|
60
61
|
protected
|
62
|
+
|
61
63
|
def start_scheduler
|
62
|
-
Scheduler.every(options[:interval]) {send_metrics}
|
64
|
+
Scheduler.every(options[:interval]) { send_metrics }
|
63
65
|
end
|
64
66
|
|
65
67
|
def send_metrics
|
66
|
-
|
67
|
-
end
|
68
|
+
connectors.publish buffer.pull(:string)
|
69
|
+
end
|
70
|
+
|
71
|
+
def build_options opt
|
72
|
+
default_options.tap do |options_hash|
|
73
|
+
options_hash[:backends] << expand_host(opt.delete(:host)) if opt.has_key? :host
|
74
|
+
options_hash[:backends] << expand_host(opt.delete(:graphite))
|
75
|
+
options_hash.merge! opt
|
76
|
+
options_hash.freeze
|
77
|
+
end
|
78
|
+
end
|
68
79
|
|
69
80
|
end
|
70
|
-
end
|
81
|
+
end
|
@@ -9,33 +9,39 @@
|
|
9
9
|
# => my.metric 1092 1232123231\n
|
10
10
|
# -----------------------------------------------------
|
11
11
|
require 'socket'
|
12
|
+
|
12
13
|
module GraphiteAPI
|
13
14
|
class Connector
|
14
|
-
|
15
|
+
|
16
|
+
attr_reader :options, :host, :port
|
15
17
|
|
16
18
|
def initialize(host,port)
|
17
19
|
@host = host
|
18
20
|
@port = port
|
19
21
|
end
|
20
22
|
|
21
|
-
def puts
|
23
|
+
def puts message
|
22
24
|
begin
|
23
|
-
Logger.debug
|
24
|
-
socket.puts
|
25
|
+
Logger.debug [:connector,:puts,[host,port].join(":"),message]
|
26
|
+
socket.puts message
|
25
27
|
rescue Errno::EPIPE
|
26
28
|
@socket = nil
|
27
29
|
retry
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
33
|
+
def inspect
|
34
|
+
"#{self.class} #{@host}:#{@port}"
|
35
|
+
end
|
36
|
+
|
31
37
|
protected
|
32
38
|
|
33
39
|
def socket
|
34
40
|
if @socket.nil? || @socket.closed?
|
35
|
-
@socket = TCPSocket.new(
|
41
|
+
@socket = ::TCPSocket.new(host,port)
|
36
42
|
end
|
37
43
|
@socket
|
38
44
|
end
|
39
45
|
|
40
46
|
end
|
41
|
-
end
|
47
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module GraphiteAPI
|
2
|
+
class ConnectorGroup
|
3
|
+
|
4
|
+
attr_reader :options, :connectors
|
5
|
+
|
6
|
+
def initialize options
|
7
|
+
@options = options
|
8
|
+
@connectors = options[:backends].map { |o| Connector.new(*o) }
|
9
|
+
end
|
10
|
+
|
11
|
+
def publish messages
|
12
|
+
Logger.debug [:connector_group,:publish,messages.size, @connectors]
|
13
|
+
Array(messages).each { |msg| connectors.map {|c| c.puts msg} }
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -15,8 +15,9 @@
|
|
15
15
|
# prefix add prefix to each key
|
16
16
|
# interval report to graphite every X seconds (default is 60)
|
17
17
|
# slice send to graphite in X seconds slices (default is 60)
|
18
|
-
# log_level info
|
18
|
+
# log_level info
|
19
19
|
# -----------------------------------------------------
|
20
|
+
|
20
21
|
require 'rubygems'
|
21
22
|
require 'eventmachine'
|
22
23
|
require 'logger'
|
@@ -27,45 +28,45 @@ module GraphiteAPI
|
|
27
28
|
|
28
29
|
attr_reader :logger,:buffer,:client_id
|
29
30
|
|
30
|
-
def initialize
|
31
|
+
def initialize logger, buffer
|
31
32
|
@logger = logger
|
32
33
|
@buffer = buffer
|
33
34
|
super
|
34
35
|
end
|
35
36
|
|
36
37
|
def post_init
|
37
|
-
|
38
|
-
|
38
|
+
@client_id = Socket.unpack_sockaddr_in(get_peername).reverse.join(":")
|
39
|
+
logger.debug [:middleware,:connecting,client_id]
|
39
40
|
end
|
40
41
|
|
41
|
-
def receive_data
|
42
|
-
|
42
|
+
def receive_data data
|
43
|
+
logger.debug [:middleware,:message,client_id,data]
|
44
|
+
buffer.stream data, client_id
|
43
45
|
end
|
44
46
|
|
45
47
|
def unbind
|
46
|
-
logger.debug
|
48
|
+
logger.debug [:middleware,:disconnecting,client_id]
|
47
49
|
end
|
48
50
|
|
49
|
-
def self.start
|
51
|
+
def self.start options
|
50
52
|
EventMachine.run do
|
51
53
|
# Resources
|
52
54
|
GraphiteAPI::Logger.logger = ::Logger.new(options[:log_file] || STDOUT)
|
53
55
|
GraphiteAPI::Logger.level = eval "::Logger::#{options[:log_level].to_s.upcase}"
|
56
|
+
# TODO: move logger logic to runner
|
54
57
|
|
55
58
|
buffer = GraphiteAPI::Buffer.new(options)
|
56
|
-
|
59
|
+
connectors = GraphiteAPI::ConnectorGroup.new(options)
|
57
60
|
|
58
61
|
# Starting server
|
59
|
-
EventMachine.start_server('0.0.0.0',options[:
|
60
|
-
GraphiteAPI::Logger.info "Server running on port #{options[:
|
62
|
+
EventMachine.start_server('0.0.0.0',options[:port],self,GraphiteAPI::Logger.logger,buffer)
|
63
|
+
GraphiteAPI::Logger.info "Server running on port #{options[:port]}"
|
61
64
|
|
62
65
|
# Send metrics to graphite every X seconds
|
63
|
-
GraphiteAPI::Scheduler.every(options[:interval]) do
|
64
|
-
if buffer.got_new_records?
|
65
|
-
Logger.debug "Sending #{buffer.size} records to graphite (@#{options[:graphite_host]}:#{options[:graphite_port]})"
|
66
|
-
buffer.each { |arr| connector.puts arr.join(" ") }
|
67
|
-
end # if got_new_records?
|
66
|
+
GraphiteAPI::Scheduler.every( options[:interval] ) do
|
67
|
+
connectors.publish buffer.pull(:string) if buffer.got_new_records?
|
68
68
|
end # every
|
69
|
+
|
69
70
|
end # run
|
70
71
|
end # start
|
71
72
|
end # Middleware
|
data/lib/graphite-api/runner.rb
CHANGED
@@ -2,68 +2,29 @@ require 'optparse'
|
|
2
2
|
|
3
3
|
module GraphiteAPI
|
4
4
|
class Runner
|
5
|
-
attr_reader :options
|
6
5
|
|
7
6
|
def initialize(argv)
|
8
|
-
|
7
|
+
GraphiteAPI::CLI::parse(argv,options)
|
9
8
|
validate_options
|
10
9
|
end
|
11
10
|
|
12
11
|
def run
|
13
|
-
|
14
|
-
fork do
|
15
|
-
Process.setsid
|
16
|
-
exit if fork
|
17
|
-
Dir.chdir('/tmp')
|
18
|
-
STDOUT.reopen('/dev/null','a')
|
19
|
-
STDIN.reopen('/dev/null')
|
20
|
-
STDERR.reopen('/dev/null','a')
|
21
|
-
write_pid
|
22
|
-
run!
|
23
|
-
end
|
24
|
-
else
|
25
|
-
run!
|
26
|
-
end
|
12
|
+
options[:daemonize] ? daemonize(options[:pid]) { run! } : run!
|
27
13
|
end
|
28
14
|
|
29
15
|
private
|
30
|
-
def options
|
31
|
-
@options ||= Utils.default_options
|
32
|
-
end
|
33
16
|
|
34
|
-
def write_pid
|
35
|
-
begin
|
36
|
-
File.open(options[:pid], 'w') { |f| f.write(Process.pid) }
|
37
|
-
rescue Exception
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
17
|
def run!
|
42
|
-
|
18
|
+
Middleware::start options
|
43
19
|
end
|
44
20
|
|
45
|
-
def
|
46
|
-
|
21
|
+
def options
|
22
|
+
@options ||= Utils::default_options
|
47
23
|
end
|
48
24
|
|
49
|
-
def
|
50
|
-
|
51
|
-
opts.banner = "Graphite Middleware Server"
|
52
|
-
opts.define_head "Usage: graphite-middleware [options]"
|
53
|
-
opts.define_head ""
|
54
|
-
opts.on("-g", "--graphite HOST","graphite host") {|v| options[:graphite_host] = v}
|
55
|
-
opts.on("-p", "--port PORT","listening port (default 2003)"){|v| options[:listening_port] = v}
|
56
|
-
opts.on("-l", "--log-file FILE","log file") {|v| options[:log_file] = File.expand_path(v)}
|
57
|
-
opts.on("-L", "--log-level LEVEL","log level (default warn)") {|v|options[:log_level] = v}
|
58
|
-
opts.on("-P", "--pid-file FILE","pid file (default /var/run/graphite-middleware.pid)"){|v|options[:pid] = v}
|
59
|
-
opts.on("-d", "--daemonize","run in background"){options[:daemonize] = true}
|
60
|
-
opts.on("-i", "--interval INT","report every X seconds (default 60)"){|v|options[:interval] = v.to_i unless v.to_i == 0}
|
61
|
-
opts.on("-s", "--slice SECONDS","send to graphite in X seconds slices (default is 60)") {|v| options[:slice] = v.to_i unless v.to_i == 0}
|
62
|
-
opts.on("-c", "--cache HOURS","cache expiration time in hours (default is 12 hours)") {|v| (options[:cache_exp] = v.to_i * 3600) unless v.to_i == 0}
|
63
|
-
opts.define_tail ""
|
64
|
-
opts.define_tail ""
|
65
|
-
end
|
25
|
+
def validate_options
|
26
|
+
abort "You must specify at least one graphite host" if options[:backends].empty?
|
66
27
|
end
|
67
28
|
|
68
29
|
end
|
69
|
-
end
|
30
|
+
end
|
data/lib/graphite-api/utils.rb
CHANGED
@@ -1,20 +1,26 @@
|
|
1
1
|
module GraphiteAPI
|
2
2
|
module Utils
|
3
|
+
|
3
4
|
module_function
|
4
5
|
|
5
6
|
def normalize_time(time,slice = 60)
|
6
7
|
((time || Time.now).to_i / slice * slice).to_i
|
7
8
|
end
|
8
|
-
|
9
|
+
|
10
|
+
def expand_host host
|
11
|
+
host,port = host.split(":")
|
12
|
+
port = port.nil? ? default_options[:port] : port.to_i
|
13
|
+
[host,port]
|
14
|
+
end
|
15
|
+
|
9
16
|
def default_options
|
10
17
|
{
|
18
|
+
:backends => [],
|
11
19
|
:cleaner_interval => 43200,
|
12
|
-
:
|
13
|
-
:listening_port => 2003,
|
20
|
+
:port => 2003,
|
14
21
|
:log_level => :info,
|
15
22
|
:cache_exp => nil,
|
16
23
|
:host => "localhost",
|
17
|
-
:port => 2003,
|
18
24
|
:prefix => [],
|
19
25
|
:interval => 60,
|
20
26
|
:slice => 60,
|
@@ -22,5 +28,20 @@ module GraphiteAPI
|
|
22
28
|
}
|
23
29
|
end
|
24
30
|
|
31
|
+
def daemonize pid
|
32
|
+
block_given? or raise ArgumentError.new "the block is missing..."
|
33
|
+
fork do
|
34
|
+
Process.setsid
|
35
|
+
exit if fork
|
36
|
+
Dir.chdir '/tmp'
|
37
|
+
STDIN.reopen('/dev/null')
|
38
|
+
STDOUT.reopen('/dev/null','a')
|
39
|
+
STDERR.reopen('/dev/null','a')
|
40
|
+
File.open(pid,'w') { |f| f.write(Process.pid) } rescue
|
41
|
+
yield
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
25
46
|
end
|
26
47
|
end
|
data/lib/graphite-api/version.rb
CHANGED
@@ -1,22 +1,5 @@
|
|
1
1
|
module GraphiteAPI
|
2
2
|
class Version
|
3
|
-
|
4
|
-
class << self
|
5
|
-
def string
|
6
|
-
[0,0,0,pre].join(".")
|
7
|
-
end
|
8
|
-
|
9
|
-
def increment_pre
|
10
|
-
new_version = pre + 1
|
11
|
-
File.open(PFILE,"w") { |f| f.puts new_version }
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
def pre
|
16
|
-
File.read(PFILE).to_i
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
20
|
-
|
3
|
+
VERSION = "0.0.0.24"
|
21
4
|
end
|
22
5
|
end
|
data/tasks/build.rake
CHANGED
@@ -27,23 +27,4 @@ end
|
|
27
27
|
task :install => [:gem] do
|
28
28
|
sh "gem install pkg/graphite-api"
|
29
29
|
Rake::Task['clobber_package'].execute
|
30
|
-
end
|
31
|
-
|
32
|
-
namespace :gem do
|
33
|
-
desc "Update GraphiteAPI gem version"
|
34
|
-
task :update_version do
|
35
|
-
GraphiteAPI::Version.increment_pre
|
36
|
-
msg "Incrementing version to #{GraphiteAPI::Version.string}..."
|
37
|
-
end
|
38
|
-
|
39
|
-
desc "Upload GraphiteAPI to rubygems.org"
|
40
|
-
task :upload => [:build] do
|
41
|
-
sh "gem push pkg/graphite-api-#{GraphiteAPI.version}.gem"
|
42
|
-
end
|
43
|
-
|
44
|
-
desc "Update GraphiteAPI gem version and build gem"
|
45
|
-
task :build => [:test,:update_version] do
|
46
|
-
sh "rake gem"
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
30
|
+
end
|
data/tasks/tests.rake
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphite-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.0.
|
4
|
+
version: 0.0.0.24
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-04-
|
12
|
+
date: 2012-04-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
16
|
-
requirement: &
|
16
|
+
requirement: &70342798606180 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: 0.3.3
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70342798606180
|
25
25
|
description: Graphite API - A Simple ruby client, aggregator daemon and API tools
|
26
26
|
email: eran@kontera.com
|
27
27
|
executables:
|
@@ -33,8 +33,10 @@ files:
|
|
33
33
|
- Rakefile
|
34
34
|
- bin/graphite-middleware
|
35
35
|
- lib/graphite-api/buffer.rb
|
36
|
+
- lib/graphite-api/cli.rb
|
36
37
|
- lib/graphite-api/client.rb
|
37
38
|
- lib/graphite-api/connector.rb
|
39
|
+
- lib/graphite-api/connector_group.rb
|
38
40
|
- lib/graphite-api/logger.rb
|
39
41
|
- lib/graphite-api/middleware.rb
|
40
42
|
- lib/graphite-api/runner.rb
|
@@ -64,7 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
66
|
version: '0'
|
65
67
|
requirements: []
|
66
68
|
rubyforge_project: graphite-api
|
67
|
-
rubygems_version: 1.8.
|
69
|
+
rubygems_version: 1.8.15
|
68
70
|
signing_key:
|
69
71
|
specification_version: 3
|
70
72
|
summary: Graphite Ruby Client
|