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 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