graphite-api 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d3795d731081bacee9b35120d168a33510344cfb
4
- data.tar.gz: 7ca9645cdc7ba74fd7d4cd0c9acca2339d08ff51
3
+ metadata.gz: e7f26c3b31d445c4e11d556609f0c3b600c60fe7
4
+ data.tar.gz: 657df67acbef98202c7278e473994b4bd47841f9
5
5
  SHA512:
6
- metadata.gz: c406a9bd20f842267022b51ce627e30850f1ccf1fbc35b49ac7522a02a1e6b64c3f6ca7e514287c5ddd053985ef1d2f5aa89cc3c616b2378213d4c90b1c1212a
7
- data.tar.gz: 34d55c38be10cefbaa253889d6294941116612857a404145ae17bb1ba447149f381d0eef336bc999b046afd3cd04da4f3201ad563d2968917ae91b2db4762658
6
+ metadata.gz: 4e464886000e85f88d21dd6351d60e3160a3dc9b4e66b4d98f72f7fd3ae1f50556494843c21b3430d1d26aa11ed2c7c9d23dcf36bd8b7177a1dc804e07811d91
7
+ data.tar.gz: 8cba41ac302987b045af13b8ef025ff08b9c93e49353442e38edbd7b8db66ec70c4f538ec4f6aff4d125e2f777972aef5bc1b7b5a20b2104863e1803573e183a
data/README.md CHANGED
@@ -1,13 +1,9 @@
1
- # GraphiteAPI
2
- A Ruby API toolkit for [Graphite](http://graphite.wikidot.com/).
3
-
4
- ## Description
1
+ # Description
5
2
  **GraphiteAPI** provides two ways for interacting with **Graphite's Carbon Daemon**, the first is for Ruby applications using the **GraphiteAPI::Client**, the second is through **GraphiteAPI-Middleware** daemon, both methods implements Graphite's [plaintext protocol](http://graphite.readthedocs.org/en/1.0/feeding-carbon.html).
6
3
 
7
4
  ## Package Content
8
5
  * Includes a **simple** client for ruby.
9
6
  * Ships with a **GraphiteAPI-Middleware**, which is a lightweight, event-driven, aggregator daemon.
10
- * Only one gem dependency ( EventMachine ).
11
7
  * Utilities like scheduling and caching.
12
8
 
13
9
  ## Key Features
@@ -51,12 +47,12 @@ Creating a new client instance
51
47
  require 'graphite-api'
52
48
 
53
49
  GraphiteAPI.new(
54
- graphite: "graphite.example.com:2003", # required argument
55
- prefix: ["example","prefix"], # add example.prefix to each key
56
- slice: 60, # results are aggregated in 60 seconds slices
57
- interval: 60, # send to graphite every 60 seconds
58
- # default is 0 ( direct send )
59
- cache: 4 * 60 * 60 # set the max age in seconds for records reanimation
50
+ graphite: "udp://graphite.example.com:2003", # required argument
51
+ prefix: ["example","prefix"], # add example.prefix to each key
52
+ slice: 60, # results are aggregated in 60 seconds slices
53
+ interval: 60, # send to graphite every 60 seconds
54
+ # default is 0 ( direct send )
55
+ cache: 4 * 60 * 60 # set the max age in seconds for records reanimation
60
56
  )
61
57
  ```
62
58
 
@@ -64,7 +60,7 @@ Adding simple metrics
64
60
  ```ruby
65
61
  require 'graphite-api'
66
62
 
67
- client = GraphiteAPI.new( graphite: 'graphite:2003' )
63
+ client = GraphiteAPI.new( graphite: 'tcp://graphite:2003' )
68
64
 
69
65
  client.metrics "webServer.web01.loadAvg" => 10.7
70
66
  # => webServer.web01.loadAvg 10.7 time.now.to_i
@@ -81,7 +77,7 @@ Adding metrics with timestamp
81
77
  ```ruby
82
78
  require 'graphite-api'
83
79
 
84
- client = GraphiteAPI.new( graphite: 'graphite:2003' )
80
+ client = GraphiteAPI.new( graphite: 'udp://graphite:2003' )
85
81
 
86
82
  client.metrics({
87
83
  "webServer.web01.loadAvg" => 10.7,
@@ -95,7 +91,7 @@ Increment records
95
91
  ```ruby
96
92
  require 'graphite-api'
97
93
 
98
- client = GraphiteAPI.new( graphite: 'graphite:2003' )
94
+ client = GraphiteAPI.new( graphite: 'tcp://graphite:2003' )
99
95
 
100
96
  client.increment("jobs_in_queue", "num_errors")
101
97
  # => jobs_in_queue 1 Time.now.to_i
@@ -115,7 +111,7 @@ Built-in timers support
115
111
  ```ruby
116
112
  require 'graphite-api'
117
113
 
118
- client = GraphiteAPI.new( graphite: 'graphite:2003' )
114
+ client = GraphiteAPI.new( graphite: 'tcp://graphite:2003' )
119
115
 
120
116
  # lets send the metric every 120 seconds
121
117
  client.every(120) do |c|
@@ -128,7 +124,7 @@ Built-in extension for time declarations stuff, like 2.minutes, 3.hours etc...
128
124
  require 'graphite-api'
129
125
  require 'graphite-api/core_ext/numeric'
130
126
 
131
- client = GraphiteAPI.new( graphite: 'graphite:2003' )
127
+ client = GraphiteAPI.new( graphite: 'udp://graphite:2003' )
132
128
 
133
129
  client.every 10.seconds do |c|
134
130
  c.metrics("webServer.web01.uptime" => `uptime`.split.first.to_i)
@@ -144,7 +140,7 @@ Make your own custom metrics daemons, using `client#join`
144
140
  require 'graphite-api'
145
141
  require 'graphite-api/core_ext/numeric'
146
142
 
147
- client = GraphiteAPI.new( graphite: 'graphite:2003' )
143
+ client = GraphiteAPI.new( graphite: 'udp://graphite:2003' )
148
144
 
149
145
  client.every 26.minutes do |c|
150
146
  c.metrics("webServer.shuki.stats" => 10)
@@ -220,7 +216,7 @@ example.middleware.value2 99 1334929231
220
216
 
221
217
  ```ruby
222
218
  require 'graphite-api'
223
- client = GraphiteAPI.new(:graphite => 'graphite-middleware-node:2005')
219
+ client = GraphiteAPI.new(:graphite => 'tcp://graphite-middleware-node:2005')
224
220
  client.example.middleware.value 10.2
225
221
  client.example.middleware.value2 27
226
222
  client.bla.bla.value2 27
@@ -2,5 +2,72 @@
2
2
  $:.unshift File.expand_path('../../lib',__FILE__)
3
3
 
4
4
  require 'graphite-api'
5
+ require 'optparse'
5
6
 
6
- GraphiteAPI::Runner.new(ARGV).run
7
+ options = GraphiteAPI::Client.default_options.merge interval: 60
8
+
9
+ OptionParser.new do |opts|
10
+ opts.banner = "GraphiteAPI Middleware Server"
11
+ opts.define_head "Usage: graphite-middleware [options]"
12
+ opts.define_head ""
13
+
14
+ opts.on("-g", "--graphite [udp,tcp]://host:port", "can be specified multiple times") do |graphite|
15
+ options[:backends].push graphite
16
+ end
17
+
18
+ opts.on("-p", "--port PORT","listening port (default #{options[:port]})") do |port|
19
+ options[:port] = port
20
+ end
21
+
22
+ opts.on("-l", "--log-file FILE","log file") do |file|
23
+ options[:log_file] = File::expand_path(file)
24
+ end
25
+
26
+ opts.on("-L", "--log-level LEVEL","log level (default warn)") do |level|
27
+ options[:log_level] = level
28
+ end
29
+
30
+ opts.on("-P", "--pid-file FILE","pid file (default #{options[:pid]})") do |pid_file|
31
+ options[:pid] = pid_file
32
+ end
33
+
34
+ opts.on("-d", "--daemonize","run in background") do
35
+ options[:daemonize] = true
36
+ end
37
+
38
+ opts.on("-i", "--interval INT","report every X seconds (default #{options[:interval]})") do |x_seconds|
39
+ options[:interval] = x_seconds.to_i if x_seconds.to_i > 0
40
+ end
41
+
42
+ opts.on("-s", "--slice SECONDS","send to graphite in X seconds slices (default #{options[:slice]})") do |slice|
43
+ options[:slice] = slice.to_i if slice.to_i >= 0
44
+ end
45
+
46
+ opts.on("-r", "--reanimation HOURS","reanimate records that are younger than X hours, please see README") do |exp|
47
+ (options[:cache] = exp.to_i * 3600) if exp.to_i > 0
48
+ end
49
+
50
+ opts.on("-v", "--version","Show version and exit") do |exp|
51
+ puts "Version #{GraphiteAPI.version}"
52
+ exit
53
+ end
54
+
55
+ opts.separator ""
56
+ opts.separator "More Info @ https://github.com/kontera-technologies/graphite-api"
57
+ opts.separator ""
58
+
59
+ opts.define_tail ""
60
+ opts.define_tail ""
61
+ end.parse! ARGV
62
+
63
+ abort "You must specify at least one graphite host" if options[:backends].empty?
64
+
65
+ GraphiteAPI::Logger.init Hash[[:dev,:level].zip options.values_at(:log_file, :log_level) ]
66
+
67
+ begin
68
+ Process.daemon if options[:daemonize]
69
+ GraphiteAPI::Middleware.start options
70
+ rescue Interrupt
71
+ GraphiteAPI::Logger.info "Shutting down..."
72
+ Zscheduler.stop
73
+ end
@@ -1,20 +1,13 @@
1
1
  require 'zscheduler'
2
+ require 'graphite-api/version'
3
+ require 'graphite-api/client'
4
+ require 'graphite-api/cache'
5
+ require 'graphite-api/connector'
6
+ require 'graphite-api/middleware'
7
+ require 'graphite-api/buffer'
8
+ require 'graphite-api/logger'
2
9
 
3
10
  module GraphiteAPI
4
- ROOT = File.expand_path File.dirname __FILE__
5
-
6
- require "#{ROOT}/graphite-api/version"
7
-
8
- autoload :Version, "#{ROOT}/graphite-api/version"
9
- autoload :Client, "#{ROOT}/graphite-api/client"
10
- autoload :Cache, "#{ROOT}/graphite-api/cache"
11
- autoload :Connector, "#{ROOT}/graphite-api/connector"
12
- autoload :Middleware, "#{ROOT}/graphite-api/middleware"
13
- autoload :Runner, "#{ROOT}/graphite-api/runner"
14
- autoload :Utils, "#{ROOT}/graphite-api/utils"
15
- autoload :CLI, "#{ROOT}/graphite-api/cli"
16
- autoload :Buffer, "#{ROOT}/graphite-api/buffer"
17
- autoload :Logger, "#{ROOT}/graphite-api/logger"
18
11
 
19
12
  def self.version
20
13
  GraphiteAPI::VERSION
@@ -21,9 +21,8 @@ require 'set'
21
21
 
22
22
  module GraphiteAPI
23
23
  class Buffer
24
- include Utils
25
24
 
26
- CHARS_TO_BE_IGNORED = ["\r"]
25
+ IGNORE = ["\r"]
27
26
  END_OF_STREAM = "\n"
28
27
  VALID_MESSAGE = /^[\w|\.|-]+ \d+(?:\.|\d)* \d+$/
29
28
 
@@ -34,7 +33,7 @@ module GraphiteAPI
34
33
  @cache = Cache::Memory.new options if options[:cache]
35
34
  end
36
35
 
37
- private_reader :queue, :options, :streamer, :cache
36
+ attr_reader :queue, :options, :streamer, :cache
38
37
 
39
38
  # this method isn't thread safe
40
39
  # use #push for multiple threads support
@@ -44,7 +43,7 @@ module GraphiteAPI
44
43
  streamer[client_id] += char
45
44
 
46
45
  if closed_stream? streamer[client_id]
47
- if valid_stream_message? streamer[client_id]
46
+ if streamer[client_id] =~ VALID_MESSAGE
48
47
  push stream_message_to_obj streamer[client_id]
49
48
  end
50
49
  streamer.delete client_id
@@ -55,7 +54,7 @@ module GraphiteAPI
55
54
  # Add records to buffer
56
55
  # push({:metric => {'a' => 10},:time => Time.now})
57
56
  def push obj
58
- debug [:buffer,:add, obj]
57
+ Logger.debug [:buffer,:add, obj]
59
58
  queue.push obj
60
59
  nil
61
60
  end
@@ -63,14 +62,13 @@ module GraphiteAPI
63
62
  alias_method :<<, :push
64
63
 
65
64
  def pull format = nil
66
- data = nested_zero_hash
65
+ data = Hash.new {|h,k| h[k] = Hash.new {|h2,k2| h2[k2] = 0}}
67
66
 
68
67
  counter = 0
69
68
  while new_records?
70
69
  break if ( counter += 1 ) > 1_000_000 # TODO: fix this
71
70
  hash = queue.pop
72
- time = normalize_time(hash[:time],options[:slice])
73
- hash[:metric].each { |k,v| data[time][k] += v.to_f }
71
+ hash[:metric].each {|k,v| data[normalize_time(hash[:time],options[:slice])][k] += v.to_f}
74
72
  end
75
73
 
76
74
  data.map do |time, hash|
@@ -82,16 +80,21 @@ module GraphiteAPI
82
80
  end.flatten(1)
83
81
  end
84
82
 
85
- def new_records?
86
- !queue.empty?
87
- end
88
-
89
83
  def inspect
90
84
  "#<GraphiteAPI::Buffer:%s @quque#size=%s @streamer=%s>" %
91
85
  [ object_id, queue.size, streamer]
92
86
  end
93
87
 
88
+ def new_records?
89
+ !queue.empty?
90
+ end
91
+
94
92
  private
93
+
94
+ def normalize_time time, slice
95
+ slice = 60 if slice.nil?
96
+ ((time || Time.now).to_i / slice * slice).to_i
97
+ end
95
98
 
96
99
  def stream_message_to_obj message
97
100
  parts = message.split
@@ -99,16 +102,12 @@ module GraphiteAPI
99
102
  end
100
103
 
101
104
  def invalid_char? char
102
- CHARS_TO_BE_IGNORED.include? char
105
+ IGNORE.include? char
103
106
  end
104
107
 
105
108
  def closed_stream? string
106
109
  string[-1,1] == END_OF_STREAM
107
110
  end
108
-
109
- def valid_stream_message? message
110
- message =~ VALID_MESSAGE
111
- end
112
111
 
113
112
  def prefix
114
113
  @prefix ||= if options[:prefix] and !options[:prefix].empty?
@@ -1,5 +1,35 @@
1
1
  module GraphiteAPI
2
2
  module Cache
3
- autoload :Memory, File.expand_path("../cache/memory", __FILE__)
3
+ class Memory
4
+
5
+ def initialize options
6
+ Zscheduler.every(120) { clean(options[:cache]) }
7
+ end
8
+
9
+ def get time, key
10
+ cache[time.to_i][key]
11
+ end
12
+
13
+ def set time, key, value
14
+ cache[time.to_i][key] = value.to_f
15
+ end
16
+
17
+ def incr time, key, value
18
+ set(time, key, value.to_f + get(time, key))
19
+ end
20
+
21
+ private
22
+
23
+ def cache
24
+ @cache ||= Hash.new {|h,k| h[k] = Hash.new {|h2,k2| h2[k2] = 0}}
25
+ end
26
+
27
+ def clean max_age
28
+ Logger.debug [:MemoryCache, :before_clean, cache]
29
+ cache.delete_if {|t,k| Time.now.to_i - t > max_age }
30
+ Logger.debug [:MemoryCache, :after_clean, cache]
31
+ end
32
+
33
+ end
4
34
  end
5
- end
35
+ end
@@ -1,10 +1,13 @@
1
- require File.expand_path '../utils', __FILE__
2
-
1
+ require 'forwardable'
2
+
3
3
  module GraphiteAPI
4
4
  class Client
5
- include Utils
5
+ extend Forwardable
6
6
 
7
- private_reader :options, :buffer, :connectors
7
+ def_delegator Zscheduler, :loop, :join
8
+ def_delegator Zscheduler, :stop
9
+
10
+ attr_reader :options, :buffer, :connectors
8
11
 
9
12
  def initialize opt
10
13
  @options = build_options validate opt.clone
@@ -14,9 +17,6 @@ module GraphiteAPI
14
17
  Zscheduler.every(options[:interval]) { send_metrics } unless options[:direct]
15
18
  end
16
19
 
17
- def_delegator Zscheduler, :loop, :join
18
- def_delegator Zscheduler, :stop
19
-
20
20
  def every interval, &block
21
21
  Zscheduler.every( interval ) { block.arity == 1 ? block.call(self) : block.call }
22
22
  end
@@ -27,24 +27,12 @@ module GraphiteAPI
27
27
  send_metrics if options[:direct]
28
28
  end
29
29
 
30
- alias_method :add_metrics, :metrics
31
-
32
- # increment keys
33
- #
34
- # increment("key1","key2")
35
- # => metrics("key1" => 1, "key2" => 1)
36
- #
37
- # increment("key1","key2", {:by => 999})
38
- # => metrics("key1" => 999, "key2" => 999)
39
- #
40
- # increment("key1","key2", {:time => Time.at(123456)})
41
- # => metrics({"key1" => 1, "key2" => 1},Time.at(123456))
42
30
  def increment(*keys)
43
31
  opt = {}
44
32
  opt.merge! keys.pop if keys.last.is_a? Hash
45
33
  by = opt.fetch(:by,1)
46
34
  time = opt.fetch(:time,Time.now)
47
- metric = keys.inject({}) {|h,k| h.tap { h[k] = by}}
35
+ metric = keys.inject({}) {|h,k| h.merge k => by }
48
36
  metrics(metric, time)
49
37
  end
50
38
 
@@ -52,6 +40,21 @@ module GraphiteAPI
52
40
  sleep while buffer.new_records?
53
41
  end
54
42
 
43
+ def self.default_options
44
+ {
45
+ :backends => [],
46
+ :cleaner_interval => 43200,
47
+ :port => 2003,
48
+ :log_level => :info,
49
+ :cache => nil,
50
+ :host => "localhost",
51
+ :prefix => [],
52
+ :interval => 0,
53
+ :slice => 60,
54
+ :pid => "/tmp/graphite-middleware.pid"
55
+ }
56
+ end
57
+
55
58
  protected
56
59
 
57
60
  def validate options
@@ -61,8 +64,8 @@ module GraphiteAPI
61
64
  end
62
65
 
63
66
  def build_options opt
64
- default_options.tap do |options_hash|
65
- options_hash[:backends].push expand_host opt.delete :graphite
67
+ self.class.default_options.tap do |options_hash|
68
+ options_hash[:backends].push opt.delete :graphite
66
69
  options_hash.merge! opt
67
70
  options_hash[:direct] = options_hash[:interval] == 0
68
71
  options_hash[:slice] = 1 if options_hash[:direct]
@@ -71,8 +74,6 @@ module GraphiteAPI
71
74
 
72
75
  def send_metrics
73
76
  connectors.publish buffer.pull :string if buffer.new_records?
74
- rescue Exception => e
75
- Zscheduler.init_reactor? ? raise : Logger.error("Publish Error: #{e}\n#{e.backtrace.join("\n")}")
76
77
  end
77
78
 
78
79
  end
@@ -9,48 +9,108 @@
9
9
  # => my.metric 1092 1232123231\n
10
10
  # -----------------------------------------------------
11
11
  require 'socket'
12
+ require 'uri'
12
13
 
13
14
  module GraphiteAPI
14
15
  class Connector
16
+
17
+ class UDPSocket
18
+ def initialize uri
19
+ @uri = uri
20
+ @socket = ::UDPSocket.new
21
+ end
22
+
23
+ def puts message
24
+ @socket.send message, 0, @uri.host, @uri.port
25
+ end
26
+
27
+ def closed?
28
+ @socket.closed? rescue true
29
+ end
30
+ end
31
+
32
+ class TCPSocket
33
+ def initialize uri
34
+ @uri = uri
35
+ end
36
+
37
+ def puts message
38
+ socket.puts message
39
+ end
40
+
41
+ def closed?
42
+ socket.closed? rescue true
43
+ end
44
+
45
+ def socket
46
+ @socket ||= begin
47
+ host, port = @uri.host, @uri.port
48
+ timeout = Hash[URI.decode_www_form(@uri.query.to_s)].fetch("timeout", 1)
49
+ addr = Socket.getaddrinfo host, nil, :INET
50
+ sockaddr = Socket.pack_sockaddr_in port, addr[0][3]
51
+
52
+ sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
53
+ sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
54
+ begin
55
+ sock.connect_nonblock sockaddr
56
+ rescue IO::WaitWritable
57
+ if IO.select nil, [sock], nil, timeout
58
+ begin
59
+ sock.connect_nonblock sockaddr
60
+ rescue Errno::EISCONN # success
61
+ rescue
62
+ sock.close
63
+ raise
64
+ end
65
+ else
66
+ sock.close
67
+ raise "Connection timeout"
68
+ end
69
+ end
70
+ sock
71
+ end #begin
72
+
73
+ end # Socket
74
+ end # TCPSocket
75
+
15
76
  class Group
16
77
  def initialize options
17
- @connectors = options[:backends].map { |o| Connector.new(*o) }
78
+ @connectors = options[:backends].map(&Connector.method(:new))
18
79
  end
19
80
 
20
81
  def publish messages
21
- Logger.debug [:connector_group, :publish, messages.size, @connectors]
82
+ Logger.debug [:connector_group, :publish, messages, @connectors]
22
83
  Array(messages).each { |msg| @connectors.map {|c| c.puts msg} }
23
84
  end
24
85
  end
25
-
26
- def initialize host, port
27
- @host, @port = host, port
86
+
87
+ ##############################################################################
88
+
89
+ def initialize uri
90
+ @uri = URI.parse uri
91
+ @uri = @uri.host ? @uri : URI.parse("udp://#{uri}")
28
92
  end
29
-
93
+
30
94
  def puts message
31
95
  counter = 0
32
96
  begin
33
- Logger.debug [:connector,:puts,[@host, @port].join(":"),message]
97
+ Logger.debug [:connector, :puts, @uri, message]
34
98
  socket.puts message + "\n"
35
99
  rescue Exception
36
100
  @socket = nil
37
101
  (counter += 1) <= 5 ? retry : raise
38
102
  end
39
103
  end
40
-
41
- def inspect
42
- "#{self.class} #{@host}:#{@port}"
43
- end
44
-
45
- protected
46
-
104
+
105
+ private
106
+
47
107
  def socket
48
108
  if @socket.nil? || @socket.closed?
49
- Logger.debug [:connector,[@host,@port]]
50
- @socket = ::TCPSocket.new @host, @port
109
+ Logger.debug [:connector, :init, @uri]
110
+ @socket = @uri.scheme.eql?("tcp") ? TCPSocket.new(@uri) : UDPSocket.new(@uri)
51
111
  end
52
112
  @socket
53
113
  end
54
-
114
+
55
115
  end
56
116
  end
@@ -17,11 +17,9 @@ module GraphiteAPI
17
17
  class Logger
18
18
  class << self
19
19
  attr_accessor :logger
20
-
21
- # :level => :debug
22
- # :dev => out|err|file-name
23
- def init(options)
24
- self.logger = ::Logger.new options.fetch(:dev, STDOUT)
20
+
21
+ def init options
22
+ self.logger = ::Logger.new options[:dev] || STDOUT
25
23
  self.logger.level= ::Logger.const_get options[:level].to_s.upcase
26
24
  end
27
25
 
@@ -16,34 +16,30 @@
16
16
  # slice send to graphite in X seconds slices (default is 60)
17
17
  # log_level info
18
18
  # -----------------------------------------------------
19
-
20
19
  require 'eventmachine'
21
20
  require 'socket'
22
- require File.expand_path '../utils', __FILE__
23
21
 
24
22
  module GraphiteAPI
25
23
  class Middleware < EventMachine::Connection
26
24
 
27
- include Utils
28
-
29
25
  def initialize buffer
30
26
  @buffer = buffer and super
31
27
  end
32
28
 
33
- private_reader :buffer, :client_id
29
+ attr_reader :buffer, :client_id
34
30
 
35
31
  def post_init
36
32
  @client_id = peername
37
- debug [:middleware, :connecting, client_id]
33
+ Logger.debug [:middleware, :connecting, client_id]
38
34
  end
39
35
 
40
36
  def receive_data data
41
- debug [:middleware, :message, client_id, data]
37
+ Logger.debug [:middleware, :message, client_id, data]
42
38
  buffer.stream data, client_id
43
39
  end
44
40
 
45
41
  def unbind
46
- debug [:middleware, :disconnecting, client_id]
42
+ Logger.debug [:middleware, :disconnecting, client_id]
47
43
  end
48
44
 
49
45
  def peername
@@ -73,5 +69,5 @@ module GraphiteAPI
73
69
  end
74
70
  end
75
71
 
76
- end # Middleware
77
- end # GraphiteAPI
72
+ end
73
+ end
@@ -1,3 +1,3 @@
1
1
  module GraphiteAPI
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphite-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eran Barak Levi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-21 00:00:00.000000000 Z
11
+ date: 2017-12-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: eventmachine
@@ -52,15 +52,11 @@ files:
52
52
  - lib/graphite-api.rb
53
53
  - lib/graphite-api/buffer.rb
54
54
  - lib/graphite-api/cache.rb
55
- - lib/graphite-api/cache/memory.rb
56
- - lib/graphite-api/cli.rb
57
55
  - lib/graphite-api/client.rb
58
56
  - lib/graphite-api/connector.rb
59
57
  - lib/graphite-api/core_ext/numeric.rb
60
58
  - lib/graphite-api/logger.rb
61
59
  - lib/graphite-api/middleware.rb
62
- - lib/graphite-api/runner.rb
63
- - lib/graphite-api/utils.rb
64
60
  - lib/graphite-api/version.rb
65
61
  homepage: http://www.kontera.com
66
62
  licenses:
@@ -74,7 +70,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
74
70
  requirements:
75
71
  - - ">="
76
72
  - !ruby/object:Gem::Version
77
- version: 1.8.7
73
+ version: 1.9.3
78
74
  required_rubygems_version: !ruby/object:Gem::Requirement
79
75
  requirements:
80
76
  - - ">="
@@ -1,36 +0,0 @@
1
- module GraphiteAPI
2
- module Cache
3
- class Memory
4
- include Utils
5
-
6
- def initialize options
7
- Zscheduler.every(120) { clean(options[:cache]) }
8
- end
9
-
10
- def get time, key
11
- cache[time.to_i][key]
12
- end
13
-
14
- def set time, key, value
15
- cache[time.to_i][key] = value.to_f
16
- end
17
-
18
- def incr time, key, value
19
- set(time, key, value.to_f + get(time, key))
20
- end
21
-
22
- private
23
-
24
- def cache
25
- @cache ||= nested_zero_hash
26
- end
27
-
28
- def clean max_age
29
- debug [:MemoryCache, :before_clean, cache]
30
- cache.delete_if {|t,k| Time.now.to_i - t > max_age }
31
- debug [:MemoryCache, :after_clean, cache]
32
- end
33
-
34
- end
35
- end
36
- end
@@ -1,63 +0,0 @@
1
- require 'optparse'
2
-
3
- module GraphiteAPI
4
- class CLI
5
-
6
- def self.parse(argv, options)
7
- OptionParser.new do |opts|
8
- opts.banner = "GraphiteAPI Middleware Server"
9
- opts.define_head "Usage: graphite-middleware [options]"
10
- opts.define_head ""
11
-
12
- opts.on("-g", "--graphite HOST:PORT","graphite host, in HOST:PORT format (can be specified multiple times)") do |graphite|
13
- options[:backends] << Utils::expand_host(graphite)
14
- end
15
-
16
- opts.on("-p", "--port PORT","listening port (default #{options[:port]})") do |port|
17
- options[:port] = port
18
- end
19
-
20
- opts.on("-l", "--log-file FILE","log file") do |file|
21
- options[:log_file] = File::expand_path(file)
22
- end
23
-
24
- opts.on("-L", "--log-level LEVEL","log level (default warn)") do |level|
25
- options[:log_level] = level
26
- end
27
-
28
- opts.on("-P", "--pid-file FILE","pid file (default #{options[:pid]})") do |pid_file|
29
- options[:pid] = pid_file
30
- end
31
-
32
- opts.on("-d", "--daemonize","run in background") do
33
- options[:daemonize] = true
34
- end
35
-
36
- opts.on("-i", "--interval INT","report every X seconds (default #{options[:interval]})") do |x_seconds|
37
- options[:interval] = x_seconds.to_i if x_seconds.to_i > 0
38
- end
39
-
40
- opts.on("-s", "--slice SECONDS","send to graphite in X seconds slices (default #{options[:slice]})") do |slice|
41
- options[:slice] = slice.to_i if slice.to_i >= 0
42
- end
43
-
44
- opts.on("-r", "--reanimation HOURS","reanimate records that are younger than X hours, please see README") do |exp|
45
- (options[:cache] = exp.to_i * 3600) if exp.to_i > 0
46
- end
47
-
48
- opts.on("-v", "--version","Show version and exit") do |exp|
49
- puts "Version #{GraphiteAPI.version}"
50
- exit
51
- end
52
-
53
- opts.separator ""
54
- opts.separator "More Info @ https://github.com/kontera-technologies/graphite-api"
55
- opts.separator ""
56
-
57
- opts.define_tail ""
58
- opts.define_tail ""
59
- end.parse! argv
60
- options
61
- end
62
- end
63
- end
@@ -1,52 +0,0 @@
1
- require 'optparse'
2
-
3
- module GraphiteAPI
4
- class Runner
5
- include Utils
6
-
7
- def initialize argv
8
- CLI.parse argv, options
9
- validate_options
10
- end
11
-
12
- def run
13
- Logger.init Hash[[:std,:level].zip options.values_at(:log_file, :log_level) ]
14
- options[:daemonize] ? daemonize(options[:pid], &method(:run!)) : run!
15
- end
16
-
17
- private
18
-
19
- def daemonize pid, &block
20
- block_given? or raise ArgumentError.new "the block is missing..."
21
-
22
- fork do
23
- Process.setsid
24
- exit if fork
25
- Dir.chdir '/tmp'
26
- STDIN.reopen('/dev/null')
27
- STDOUT.reopen('/dev/null','a')
28
- STDERR.reopen('/dev/null','a')
29
- File.open(pid,'w') { |f| f.write(Process.pid) } rescue nil
30
- block.call
31
- end
32
- end
33
-
34
- def run!
35
- begin
36
- Middleware.start options
37
- rescue Interrupt
38
- Logger.info "Shutting down..."
39
- Zscheduler.stop
40
- end
41
- end
42
-
43
- def options
44
- @options ||= Utils.default_middleware_options
45
- end
46
-
47
- def validate_options
48
- abort "You must specify at least one graphite host" if options[:backends].empty?
49
- end
50
-
51
- end
52
- end
@@ -1,70 +0,0 @@
1
- require 'forwardable'
2
- require 'uri'
3
-
4
- module GraphiteAPI
5
- module Utils
6
-
7
- def self.included base
8
- base.extend ClassUtils
9
- base.extend Forwardable
10
- base.__send__ :include, LoggerUtils
11
- end
12
-
13
- module LoggerUtils
14
- [:info,:error,:warn,:debug].each do |m|
15
- define_method m do |*args,&block|
16
- Logger.send m, *args, &block
17
- end
18
- end
19
- end
20
-
21
- module ClassUtils
22
- def private_reader *args
23
- attr_reader *args
24
- private *args
25
- end
26
- end
27
-
28
- def normalize_time time, slice
29
- slice = 60 if slice.nil?
30
- ((time || Time.now).to_i / slice * slice).to_i
31
- end
32
-
33
- def nested_zero_hash
34
- Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = 0} }
35
- end
36
-
37
- module_function
38
-
39
- def expand_host host
40
- if host =~ /:\/\//
41
- uri = URI.parse host
42
- [ uri.host, uri.port || default_options[:port] ]
43
- else
44
- host, port = host.split(":")
45
- port = port.nil? ? default_options[:port] : port.to_i
46
- [ host, port]
47
- end
48
- end
49
-
50
- def default_options
51
- {
52
- :backends => [],
53
- :cleaner_interval => 43200,
54
- :port => 2003,
55
- :log_level => :info,
56
- :cache => nil,
57
- :host => "localhost",
58
- :prefix => [],
59
- :interval => 0,
60
- :slice => 60,
61
- :pid => "/tmp/graphite-middleware.pid"
62
- }
63
- end
64
-
65
- def default_middleware_options
66
- default_options.merge(:interval => 60)
67
- end
68
-
69
- end
70
- end