metriks 0.8.4 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -283,6 +283,36 @@ since the process started.
283
283
 
284
284
  How to get metrics out of the process.
285
285
 
286
+ ## Graphite Reporter
287
+
288
+ Sends metrics to Graphite every 60 seconds.
289
+
290
+ ```ruby
291
+ reporter = Metriks::Reporter::Graphite.new 'localhost', 3004
292
+ reporter.start
293
+ ```
294
+
295
+
296
+ ## Logger Reporter
297
+
298
+ Send metrics to a logger every 60 seconds.
299
+
300
+ ```
301
+ reporter = Metriks::Reporter::Logger.new(:logger => Logger.new('log/metrics.log'))
302
+ reporter.start
303
+ ```
304
+
305
+
306
+ ## Librato Metrics Reporter
307
+
308
+ Send metrics to Librato Metrics every 60 seconds.
309
+
310
+ ```
311
+ reporter = Metriks::Reporter::LibratoMetrics.new('email', 'token')
312
+ reporter.start
313
+ ```
314
+
315
+
286
316
  ## Proc Title Reporter
287
317
 
288
318
  Provides a simple way to get up-to-date statistics from a process by
@@ -290,11 +320,9 @@ updating the proctitle every 5 seconds (default).
290
320
 
291
321
  ```ruby
292
322
  reporter = Metriks::Reporter::ProcTitle.new :interval => 5
293
-
294
323
  reporter.add 'reqs', 'sec' do
295
324
  Metriks.meter('rack.requests').one_minute_rate
296
325
  end
297
-
298
326
  reporter.start
299
327
  ```
300
328
 
@@ -312,9 +340,7 @@ An incomplete list of things I would like to see added:
312
340
  * Rack middleware to measure utilization, throughput and worker time
313
341
  * Basic reporters:
314
342
  * Rack endpoint returning JSON
315
- * Logger reporter to output metrics on a time interval
316
343
  * [Statsd](https://github.com/etsy/statsd) reporter
317
- * [Librato Metrics](http://metrics.librato.com) reporter
318
344
  * Metaprogramming instrumentation hooks like [Shopify's statsd-instrument](https://github.com/Shopify/statsd-instrument)
319
345
 
320
346
 
@@ -72,10 +72,9 @@ module Metriks
72
72
  @mutex.synchronize do
73
73
  old_start_time = @start_time
74
74
  @start_time = Time.now
75
- keys = @values.keys
76
- keys.each do |key|
75
+ @values.keys.each do |key|
77
76
  value = @values.delete(key)
78
- @values[key* Math.exp(-@alpha * (@start_time - old_start_time))] = value
77
+ @values[key * Math.exp(-@alpha * (@start_time - old_start_time))] = value
79
78
  end
80
79
  end
81
80
  end
@@ -0,0 +1,97 @@
1
+
2
+ module Metriks::Reporter
3
+ class Graphite
4
+ def initialize(host, port, options = {})
5
+ @host = host
6
+ @port = port
7
+ @prefix = options[:prefix]
8
+ @registry = options[:registry] || Metriks::Registry.default
9
+ @interval = options[:interval] || 60
10
+ @on_errror = options[:on_error] || proc { |ex| }
11
+ end
12
+
13
+ def socket
14
+ @socket = nil if @socket.closed?
15
+ @socket ||= TCPSocket.new(host, port)
16
+ end
17
+
18
+ def start
19
+ @thread ||= Thread.new do
20
+ loop do
21
+ sleep @interval
22
+
23
+ Thread.new do
24
+ begin
25
+ write
26
+ rescue Exception => ex
27
+ @on_error[ex] rescue nil
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ def stop
35
+ @thread.kill if @thread
36
+ @thread = nil
37
+ end
38
+
39
+ def write
40
+ @registry.each do |name, metric|
41
+ case metric
42
+ when Metriks::Meter
43
+ write_metric name, metric, [
44
+ :count, :one_minute_rate, :five_minute_rate,
45
+ :fifteen_minute_rate, :mean_rate
46
+ ]
47
+ when Metriks::Counter
48
+ write_metric name, metric, [
49
+ :count
50
+ ]
51
+ when Metriks::UtilizationTimer
52
+ write_metric name, metric, [
53
+ :count, :one_minute_rate, :five_minute_rate,
54
+ :fifteen_minute_rate, :mean_rate,
55
+ :min, :max, :mean, :stddev,
56
+ :one_minute_utilization, :five_minute_utilization,
57
+ :fifteen_minute_utilization, :mean_utilization,
58
+ ], [
59
+ :median, :get_95th_percentile
60
+ ]
61
+ when Metriks::Timer
62
+ write_metric name, metric, [
63
+ :count, :one_minute_rate, :five_minute_rate,
64
+ :fifteen_minute_rate, :mean_rate,
65
+ :min, :max, :mean, :stddev
66
+ ], [
67
+ :median, :get_95th_percentile
68
+ ]
69
+ end
70
+ end
71
+ end
72
+
73
+ def write_metric(base_name, metric, keys, snapshot_keys = [])
74
+ time = Time.now.to_i
75
+
76
+ base_name = base_name.to_s.gsub(/ +/, '_')
77
+ if @prefix
78
+ base_name = "#{@prefix}.#{base_name}"
79
+ end
80
+
81
+ keys.flatten.each do |key|
82
+ name = key.to_s.gsub(/^get_/, '')
83
+ value = metric.send(key)
84
+ socket.write("#{base_name}.#{name} #{value} #{time}\n")
85
+ end
86
+
87
+ unless snapshot_keys.empty?
88
+ snapshot = metric.snapshot
89
+ snapshot_keys.flatten.each do |key|
90
+ name = key.to_s.gsub(/^get_/, '')
91
+ value = snapshot.send(key)
92
+ socket.write("#{base_name}.#{name} #{value} #{time}\n")
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,140 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require 'yajl'
4
+ require 'yajl/json_gem'
5
+
6
+ module Metriks::Reporter
7
+ class LibratoMetrics
8
+ def self.connection
9
+ @connection ||= Faraday::Connection.new('https://metrics-api.librato.com') do |b|
10
+ b.use FaradayMiddleware::EncodeJson
11
+ b.adapter Faraday.default_adapter
12
+ b.use Faraday::Response::RaiseError
13
+ b.use FaradayMiddleware::ParseJson, :content_type => /\bjson$/
14
+ end.tap do |c|
15
+ c.headers[:content_type] = 'application/json'
16
+ c.headers[:user_agent] = "Metriks/#{Metriks::VERSION} Faraday/#{Faraday::VERSION}"
17
+ end
18
+ end
19
+
20
+
21
+ def initialize(email, token, options = {})
22
+ @email = email
23
+ @token = token
24
+ @prefix = options[:prefix]
25
+ @registry = options[:registry] || Metriks::Registry.default
26
+ @interval = options[:interval] || 60
27
+ @on_errror = options[:on_error] || proc { |ex| }
28
+ @source = options[:source]
29
+ end
30
+
31
+ def start
32
+ @thread ||= Thread.new do
33
+ loop do
34
+ sleep @interval
35
+
36
+ Thread.new do
37
+ begin
38
+ write
39
+ rescue Exception => ex
40
+ @on_error[ex] rescue nil
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ def stop
48
+ @thread.kill if @thread
49
+ @thread = nil
50
+ end
51
+
52
+ def write
53
+ gauges = []
54
+ @registry.each do |name, metric|
55
+ gauges << case metric
56
+ when Metriks::Meter
57
+ prepare_metric name, metric, [
58
+ :count, :one_minute_rate, :five_minute_rate,
59
+ :fifteen_minute_rate, :mean_rate
60
+ ]
61
+ when Metriks::Counter
62
+ prepare_metric name, metric, [
63
+ :count
64
+ ]
65
+ when Metriks::UtilizationTimer
66
+ prepare_metric name, metric, [
67
+ :count, :one_minute_rate, :five_minute_rate,
68
+ :fifteen_minute_rate, :mean_rate,
69
+ :min, :max, :mean, :stddev,
70
+ :one_minute_utilization, :five_minute_utilization,
71
+ :fifteen_minute_utilization, :mean_utilization,
72
+ ], [
73
+ :median, :get_95th_percentile
74
+ ]
75
+ when Metriks::Timer
76
+ prepare_metric name, metric, [
77
+ :count, :one_minute_rate, :five_minute_rate,
78
+ :fifteen_minute_rate, :mean_rate,
79
+ :min, :max, :mean, :stddev
80
+ ], [
81
+ :median, :get_95th_percentile
82
+ ]
83
+ end
84
+ end
85
+
86
+ submit(:gauges => gauges.flatten)
87
+ end
88
+
89
+ def connection
90
+ @connection ||= self.class.connection.dup.tap do |c|
91
+ c.basic_auth @email, @token
92
+ end
93
+ end
94
+
95
+ def submit(body)
96
+ connection.post '/v1/metrics' do |req|
97
+ req.body = body
98
+ end
99
+ end
100
+
101
+ def prepare_metric(base_name, metric, keys, snapshot_keys = [])
102
+ results = []
103
+ time = Time.now.to_i
104
+
105
+ base_name = base_name.to_s.gsub(/ +/, '_')
106
+ if @prefix
107
+ base_name = "#{@prefix}.#{base_name}"
108
+ end
109
+
110
+ keys.flatten.each do |key|
111
+ name = key.to_s.gsub(/^get_/, '')
112
+ value = metric.send(key)
113
+
114
+ results << {
115
+ :name => "#{base_name}.#{name}",
116
+ :source => @source,
117
+ :time => time,
118
+ :value => value
119
+ }
120
+ end
121
+
122
+ unless snapshot_keys.empty?
123
+ snapshot = metric.snapshot
124
+ snapshot_keys.flatten.each do |key|
125
+ name = key.to_s.gsub(/^get_/, '')
126
+ value = snapshot.send(key)
127
+
128
+ results << {
129
+ :name => "#{base_name}.#{name}",
130
+ :source => @source,
131
+ :time => time,
132
+ :value => value
133
+ }
134
+ end
135
+ end
136
+
137
+ results
138
+ end
139
+ end
140
+ end
data/lib/metriks.rb CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Metriks
3
- VERSION = '0.8.4'
3
+ VERSION = '0.9.0'
4
4
 
5
5
  def self.get(name)
6
6
  Metriks::Registry.default.get(name)
data/metriks.gemspec CHANGED
@@ -13,8 +13,8 @@ Gem::Specification.new do |s|
13
13
  ## If your rubyforge_project name is different, then edit it and comment out
14
14
  ## the sub! line in the Rakefile
15
15
  s.name = 'metriks'
16
- s.version = '0.8.4'
17
- s.date = '2012-02-27'
16
+ s.version = '0.9.0'
17
+ s.date = '2012-03-03'
18
18
 
19
19
  ## Make sure your summary is short. The description may be as long
20
20
  ## as you like.
@@ -26,15 +26,12 @@ Gem::Specification.new do |s|
26
26
  ## a custom homepage, consider using your GitHub URL or the like.
27
27
  s.authors = ["Eric Lindvall"]
28
28
  s.email = 'eric@sevenscale.com'
29
- s.homepage = 'https://github.com/eric/metriks_client'
29
+ s.homepage = 'https://github.com/eric/metriks'
30
30
 
31
31
  ## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as
32
32
  ## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'
33
33
  s.require_paths = %w[lib]
34
34
 
35
- ## If your gem includes any executables, list them here.
36
- # s.executables = ["name"]
37
-
38
35
  ## Specify any RDoc options here. You'll want to add your README and
39
36
  ## LICENSE files to the extra_rdoc_files list.
40
37
  s.rdoc_options = ["--charset=UTF-8"]
@@ -45,10 +42,14 @@ Gem::Specification.new do |s|
45
42
  s.add_dependency('atomic', ["~> 1.0"])
46
43
  s.add_dependency('hitimes', [ "~> 1.1"])
47
44
  s.add_dependency('rbtree', [ "~> 0.3" ])
45
+ s.add_dependency('faraday', ['~> 0.7.0'])
46
+ s.add_dependency('faraday_middleware', ['~> 0.8.4'])
47
+ s.add_dependency('yajl-ruby')
48
48
 
49
49
  ## List your development dependencies here. Development dependencies are
50
50
  ## those that are only needed during development
51
51
  s.add_development_dependency('tomdoc', ["~> 0.2"])
52
+ s.add_development_dependency('mocha', ['~> 0.10'])
52
53
 
53
54
  ## Leave this section as-is. It will be automatically generated from the
54
55
  ## contents of your Git repository via the gemspec task. DO NOT REMOVE
@@ -67,6 +68,8 @@ Gem::Specification.new do |s|
67
68
  lib/metriks/histogram.rb
68
69
  lib/metriks/meter.rb
69
70
  lib/metriks/registry.rb
71
+ lib/metriks/reporter/graphite.rb
72
+ lib/metriks/reporter/librato_metrics.rb
70
73
  lib/metriks/reporter/logger.rb
71
74
  lib/metriks/reporter/proc_title.rb
72
75
  lib/metriks/simple_moving_average.rb
@@ -76,7 +79,9 @@ Gem::Specification.new do |s|
76
79
  lib/metriks/utilization_timer.rb
77
80
  metriks.gemspec
78
81
  test/counter_test.rb
82
+ test/graphite_reporter_test.rb
79
83
  test/histogram_test.rb
84
+ test/librato_metrics_reporter_test.rb
80
85
  test/logger_reporter_test.rb
81
86
  test/meter_test.rb
82
87
  test/metriks_test.rb
@@ -0,0 +1,29 @@
1
+ require 'test_helper'
2
+
3
+ require 'metriks/reporter/graphite'
4
+
5
+ class GraphiteReporterTest < Test::Unit::TestCase
6
+ def setup
7
+ @registry = Metriks::Registry.new
8
+ @reporter = Metriks::Reporter::Graphite.new('localhost', 3333, :registry => @registry)
9
+ @stringio = StringIO.new
10
+
11
+ @reporter.stubs(:socket).returns(@stringio)
12
+ end
13
+
14
+ def teardown
15
+ @reporter.stop
16
+ @registry.stop
17
+ end
18
+
19
+ def test_write
20
+ @registry.meter('meter.testing').mark
21
+ @registry.counter('counter.testing').increment
22
+ @registry.timer('timer.testing').update(1.5)
23
+ @registry.utilization_timer('utilization_timer.testing').update(1.5)
24
+
25
+ @reporter.write
26
+
27
+ assert_match /timer.testing.median \d/, @stringio.string
28
+ end
29
+ end
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+
3
+ require 'metriks/reporter/librato_metrics'
4
+
5
+ class LibratoMetricsReporterTest < Test::Unit::TestCase
6
+ def setup
7
+ @registry = Metriks::Registry.new
8
+ @reporter = Metriks::Reporter::LibratoMetrics.new('user', 'password', :registry => @registry)
9
+
10
+ @reporter.connection.builder.tap do |c|
11
+ c.swap 1, Faraday::Adapter::Test do |stub|
12
+ stub.post '/v1/metrics' do |env|
13
+ [ 200, {}, '' ]
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ def teardown
20
+ @reporter.stop
21
+ @registry.stop
22
+ end
23
+
24
+ def test_write
25
+ @registry.meter('meter.testing').mark
26
+ @registry.counter('counter.testing').increment
27
+ @registry.timer('timer.testing').update(1.5)
28
+ @registry.utilization_timer('utilization_timer.testing').update(1.5)
29
+
30
+ @reporter.write
31
+ end
32
+ end
data/test/test_helper.rb CHANGED
@@ -2,6 +2,7 @@ require 'test/unit'
2
2
  require 'pp'
3
3
 
4
4
  require 'turn'
5
+ require 'mocha'
5
6
 
6
7
  require 'metriks'
7
8
 
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 8
8
- - 4
9
- version: 0.8.4
7
+ - 9
8
+ - 0
9
+ version: 0.9.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Eric Lindvall
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2012-02-27 00:00:00 -08:00
17
+ date: 2012-03-03 00:00:00 -08:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -57,9 +57,49 @@ dependencies:
57
57
  type: :runtime
58
58
  version_requirements: *id003
59
59
  - !ruby/object:Gem::Dependency
60
- name: tomdoc
60
+ name: faraday
61
61
  prerelease: false
62
62
  requirement: &id004 !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ~>
65
+ - !ruby/object:Gem::Version
66
+ segments:
67
+ - 0
68
+ - 7
69
+ - 0
70
+ version: 0.7.0
71
+ type: :runtime
72
+ version_requirements: *id004
73
+ - !ruby/object:Gem::Dependency
74
+ name: faraday_middleware
75
+ prerelease: false
76
+ requirement: &id005 !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ~>
79
+ - !ruby/object:Gem::Version
80
+ segments:
81
+ - 0
82
+ - 8
83
+ - 4
84
+ version: 0.8.4
85
+ type: :runtime
86
+ version_requirements: *id005
87
+ - !ruby/object:Gem::Dependency
88
+ name: yajl-ruby
89
+ prerelease: false
90
+ requirement: &id006 !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ segments:
95
+ - 0
96
+ version: "0"
97
+ type: :runtime
98
+ version_requirements: *id006
99
+ - !ruby/object:Gem::Dependency
100
+ name: tomdoc
101
+ prerelease: false
102
+ requirement: &id007 !ruby/object:Gem::Requirement
63
103
  requirements:
64
104
  - - ~>
65
105
  - !ruby/object:Gem::Version
@@ -68,7 +108,20 @@ dependencies:
68
108
  - 2
69
109
  version: "0.2"
70
110
  type: :development
71
- version_requirements: *id004
111
+ version_requirements: *id007
112
+ - !ruby/object:Gem::Dependency
113
+ name: mocha
114
+ prerelease: false
115
+ requirement: &id008 !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ~>
118
+ - !ruby/object:Gem::Version
119
+ segments:
120
+ - 0
121
+ - 10
122
+ version: "0.10"
123
+ type: :development
124
+ version_requirements: *id008
72
125
  description: An experimental metrics client.
73
126
  email: eric@sevenscale.com
74
127
  executables: []
@@ -91,6 +144,8 @@ files:
91
144
  - lib/metriks/histogram.rb
92
145
  - lib/metriks/meter.rb
93
146
  - lib/metriks/registry.rb
147
+ - lib/metriks/reporter/graphite.rb
148
+ - lib/metriks/reporter/librato_metrics.rb
94
149
  - lib/metriks/reporter/logger.rb
95
150
  - lib/metriks/reporter/proc_title.rb
96
151
  - lib/metriks/simple_moving_average.rb
@@ -100,7 +155,9 @@ files:
100
155
  - lib/metriks/utilization_timer.rb
101
156
  - metriks.gemspec
102
157
  - test/counter_test.rb
158
+ - test/graphite_reporter_test.rb
103
159
  - test/histogram_test.rb
160
+ - test/librato_metrics_reporter_test.rb
104
161
  - test/logger_reporter_test.rb
105
162
  - test/meter_test.rb
106
163
  - test/metriks_test.rb
@@ -110,7 +167,7 @@ files:
110
167
  - test/timer_test.rb
111
168
  - test/utilization_timer_test.rb
112
169
  has_rdoc: true
113
- homepage: https://github.com/eric/metriks_client
170
+ homepage: https://github.com/eric/metriks
114
171
  licenses: []
115
172
 
116
173
  post_install_message:
@@ -141,7 +198,9 @@ specification_version: 2
141
198
  summary: An experimental metrics client
142
199
  test_files:
143
200
  - test/counter_test.rb
201
+ - test/graphite_reporter_test.rb
144
202
  - test/histogram_test.rb
203
+ - test/librato_metrics_reporter_test.rb
145
204
  - test/logger_reporter_test.rb
146
205
  - test/meter_test.rb
147
206
  - test/metriks_test.rb