graphite-api 0.0.0.23 → 0.0.0.24
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/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
|