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 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 Client and Utilities For Ruby
5
+ Graphite client and utilities for ruby
6
6
 
7
7
  * **Simple** client for ruby.
8
- * Graphite **MiddleWare Server**, a lightweight, event-driven, aggregator daemon.
9
- * Light-Weight, only **ONE** depenency (event-machine)
10
- * **Utilities** like scheduling and caching.
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
- :host => "graphite.example.com",
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
- Graphite Middleware Server
48
+ [root@someplace]# graphite-middleware --help
49
+
50
+ GraphiteAPI Middleware Server
48
51
 
49
52
  Usage: graphite-middleware [options]
50
- -g, --graphite HOST 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 is 60)
58
- -c, --cache HOURS cache expiration time in hours (default is 12 hours)
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
@@ -2,10 +2,5 @@ $:.unshift File.join(File.dirname(__FILE__), 'lib')
2
2
  Dir.chdir File.dirname __FILE__
3
3
 
4
4
  require 'graphite-api'
5
- require 'bundler/setup'
6
-
7
- def msg m
8
- $stderr.puts "[*] #{m}"
9
- end
10
5
 
11
6
  Dir['tasks/**/*.rake'].each { |rake| load rake }
@@ -1,18 +1,20 @@
1
1
  module GraphiteAPI
2
2
  ROOT = File.expand_path(File.dirname(__FILE__))
3
3
 
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 :Buffer, "#{ROOT}/graphite-api/buffer"
12
- autoload :Logger, "#{ROOT}/graphite-api/logger"
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.string
17
+ GraphiteAPI::Version::VERSION
16
18
  end
17
19
 
18
- end
20
+ end
@@ -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
- def initialize(options)
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
- @leftovers = Hash.new {|h,k| h[k] = Array.new}
26
- @new_records = []
27
- @in_cache_mode = !options[:cache_exp].nil?
28
- start_cleaner if in_cache_mode
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 << hash
32
- time = Utils.normalize_time(hash[:time],options[:slice])
33
- hash[:metric].each do |k,v|
34
- buffer[time][k] += v.to_f
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
- def stream(data,client_id = nil)
40
- got_leftovers = data[-1,1] != "\n"
41
- data = data.split(/\n/)
42
- unless leftovers[client_id].empty?
43
- if (valid leftovers[client_id].last + data.first rescue nil)
44
- data.unshift(leftovers[client_id].pop + data.shift)
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 each
58
- new_records.uniq.each do |time,key|
59
- yield [prefix + key,buffer[time][key],time]
60
- end and clear
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
- buffer.empty?
63
+ buffer_cache.empty?
65
64
  end
66
65
 
67
66
  def got_new_records?
68
- !new_records.empty?
67
+ !keys_to_send.empty?
69
68
  end
70
-
69
+
71
70
  def size
72
- buffer.values.map(&:values).flatten.size
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
- new_records.clear
78
- buffer.clear unless in_cache_mode
94
+ keys_to_send.clear
95
+ buffer_cache.clear unless reanimation_mode
79
96
  end
80
97
 
81
- def valid(data)
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? ? String.new : prefix_to_s
103
+ @prefix ||= options[:prefix].empty? ? '' : prefix_to_s
87
104
  end
88
105
 
89
106
  def prefix_to_s
90
- [options[:prefix]].flatten.join('.') << "."
107
+ Array(options[:prefix]).join('.') << '.'
91
108
  end
92
109
 
93
- def buffer
94
- @buffer ||= Hash.new {|h,k| h[k] = Hash.new {|h1,k1| h1[k1] = 0}}
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(age)
98
- [buffer,new_records].each {|o| o.delete_if {|t,k| now - t > age}}
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 options[:cache_exp] }
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
@@ -5,8 +5,7 @@
5
5
  # Usage
6
6
  #
7
7
  # client = GraphiteAPI::Client.new(
8
- # :host => "graphite.example.com",
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
- attr_reader :options,:buffer,:connector
34
+ include Utils
36
35
 
37
- def initialize(opt)
38
- @options = GraphiteAPI::Utils.default_options.merge opt
39
- @buffer = GraphiteAPI::Buffer.new(options)
40
- @connector = GraphiteAPI::Connector.new(*options.values_at(:host,:port))
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 1 while buffer.got_new_records?
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
- buffer.each {|arr| connector.puts arr.join(" ")}
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
- attr_reader :options
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(msg)
23
+ def puts message
22
24
  begin
23
- Logger.debug msg
24
- socket.puts(msg)
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(@host,@port)
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 # TODO: move this to runner.rb
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(logger,buffer)
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
- logger.debug "Client connecting"
38
- @client_id = Socket.unpack_sockaddr_in(get_peername).join(":")
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(data)
42
- buffer.stream(data,client_id)
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 "Client disconnecting"
48
+ logger.debug [:middleware,:disconnecting,client_id]
47
49
  end
48
50
 
49
- def self.start(options)
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
- connector = GraphiteAPI::Connector.new(*options.values_at(:graphite_host,:graphite_port))
59
+ connectors = GraphiteAPI::ConnectorGroup.new(options)
57
60
 
58
61
  # Starting server
59
- EventMachine.start_server('0.0.0.0',options[:listening_port],self,GraphiteAPI::Logger.logger,buffer)
60
- GraphiteAPI::Logger.info "Server running on port #{options[:listening_port]}"
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
@@ -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
- parser.parse! argv
7
+ GraphiteAPI::CLI::parse(argv,options)
9
8
  validate_options
10
9
  end
11
10
 
12
11
  def run
13
- if options[:daemonize]
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
- GraphiteAPI::Middleware.start(options)
18
+ Middleware::start options
43
19
  end
44
20
 
45
- def validate_options
46
- abort "You must specify graphite host" if options[:graphite_host].nil?
21
+ def options
22
+ @options ||= Utils::default_options
47
23
  end
48
24
 
49
- def parser
50
- OptionParser.new do |opts|
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
@@ -14,7 +14,7 @@ module GraphiteAPI
14
14
  end
15
15
 
16
16
  def stop
17
- timers.map(&:cancel)
17
+ timers.map {|t| t.cancel}
18
18
  wrapper and EventMachine.stop
19
19
  end
20
20
 
@@ -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
- :graphite_port => 2003,
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
@@ -1,22 +1,5 @@
1
1
  module GraphiteAPI
2
2
  class Version
3
- PFILE = File.join(GraphiteAPI::ROOT,"..",".pre_version")
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
@@ -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
@@ -1,3 +1,8 @@
1
- task :test do
2
- # tests here
3
- end
1
+ require "rake/testtask"
2
+
3
+ Rake::TestTask.new(:test) do |t|
4
+ t.libs << "tests"
5
+ t.pattern = "tests/**/*_test.rb"
6
+ end
7
+
8
+ task :default => :test
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.23
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-17 00:00:00.000000000Z
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: &2152423400 !ruby/object:Gem::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: *2152423400
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.17
69
+ rubygems_version: 1.8.15
68
70
  signing_key:
69
71
  specification_version: 3
70
72
  summary: Graphite Ruby Client