metriks-addons 2.1.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.
@@ -0,0 +1,182 @@
1
+ require 'metriks/time_tracker'
2
+ require 'rest-client'
3
+ require 'logger'
4
+
5
+ module Metriks
6
+ class SignalFxReporter
7
+ attr_accessor :prefix, :source, :data, :hostname, :tags, :logger
8
+
9
+ def initialize(h, token, id, tags, options = {})
10
+ @hostname = h
11
+ @x_sf_token = token
12
+ @orgid = id
13
+ @tags = tags
14
+
15
+ @prefix = options[:prefix]
16
+ @source = options[:source]
17
+
18
+ @logger = options[:logger] || nil
19
+ @batch_size = options[:batch_size] || 50
20
+ @registry = options[:registry] || Metriks::Registry.default
21
+ @interval = options[:interval] || 60
22
+ @time_tracker = Metriks::TimeTracker.new(@interval)
23
+ @on_error = options[:on_error] || proc { |ex| }
24
+
25
+ if options[:percentiles]
26
+ @percentiles = options[:percentiles]
27
+ else
28
+ @percentiles = [ 0.95, 0.99]
29
+ end
30
+
31
+ @mutex = Mutex.new
32
+ @running = false
33
+ end
34
+
35
+ def log(level, msg)
36
+ if !@logger.nil?
37
+ @logger.send level, msg
38
+ end
39
+ end
40
+
41
+ def start
42
+ if @thread && @thread.alive?
43
+ return
44
+ end
45
+
46
+ @running = true
47
+ @thread = Thread.new do
48
+ while @running
49
+ @time_tracker.sleep
50
+
51
+ Thread.new do
52
+ flush
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ def stop
59
+ @running = false
60
+
61
+ if @thread
62
+ @thread.join
63
+ @thread = nil
64
+ end
65
+ end
66
+
67
+ def restart
68
+ stop
69
+ start
70
+ end
71
+
72
+ def flush
73
+ begin
74
+ @mutex.synchronize do
75
+ log "debug", "Flushing metrics"
76
+ submit get_datapoints
77
+ end
78
+ rescue Exception => ex
79
+ log "error",ex.message
80
+ @on_error[ex] rescue nil
81
+ end
82
+ end
83
+
84
+ def submit(datapoints)
85
+ return if datapoints.empty?
86
+
87
+ jsonstr = datapoints.to_json
88
+ response = RestClient.post "#{@hostname}?orgid=#{@orgid}",
89
+ jsonstr,
90
+ :content_type => :json, :accept => :json, :'X-SF-TOKEN' => @x_sf_token
91
+ log "info", "Sent #{datapoints.size} metrics"
92
+ end
93
+
94
+ def get_datapoints
95
+ time = @time_tracker.now_floored
96
+
97
+ datapoints = {}
98
+ counter = []
99
+ gauge = []
100
+ @registry.each do |name, metric|
101
+ next if name.nil? || name.empty?
102
+ name = name.to_s.gsub(/ +/, '_')
103
+ if @prefix
104
+ name = "#{@prefix}.#{name}"
105
+ end
106
+
107
+ case metric
108
+ when Metriks::Meter
109
+ counter |= create_datapoints name, metric, time, [
110
+ :count, :one_minute_rate, :five_minute_rate,
111
+ :fifteen_minute_rate, :mean_rate
112
+ ]
113
+ when Metriks::Counter
114
+ counter |= create_datapoints name, metric, time, [
115
+ :count
116
+ ]
117
+ when Metriks::Gauge
118
+ gauge |= create_datapoints name, metric, time, [
119
+ :value
120
+ ]
121
+ when Metriks::UtilizationTimer
122
+ counter |= create_datapoints name, metric, time, [
123
+ :count, :one_minute_rate, :five_minute_rate,
124
+ :fifteen_minute_rate, :mean_rate,
125
+ :min, :max, :mean, :stddev,
126
+ :one_minute_utilization, :five_minute_utilization,
127
+ :fifteen_minute_utilization, :mean_utilization,
128
+ ], [
129
+ :median, :get_95th_percentile
130
+ ]
131
+
132
+ when Metriks::Timer
133
+ counter |= create_datapoints name, metric, time, [
134
+ :count, :one_minute_rate, :five_minute_rate,
135
+ :fifteen_minute_rate, :mean_rate,
136
+ :min, :max, :mean, :stddev
137
+ ], [
138
+ :median, :get_95th_percentile
139
+ ]
140
+ when Metriks::Histogram
141
+ counter |= create_datapoints name, metric, time, [
142
+ :count, :min, :max, :mean, :stddev
143
+ ], [
144
+ :median, :get_95th_percentile
145
+ ]
146
+ end
147
+ end
148
+
149
+ datapoints[:counter] = counter if counter.any?
150
+ datapoints[:gauge] = gauge if gauge.any?
151
+
152
+ datapoints
153
+ end
154
+
155
+ def create_datapoints(base_name, metric, time, keys, snapshot_keys = [])
156
+ datapoints = []
157
+ keys.flatten.each do |key|
158
+ name = key.to_s.gsub(/^get_/, '')
159
+ datapoints << {
160
+ :metric => "#{base_name}.#{name}",
161
+ :timestamp => time,
162
+ :value => metric.send(key),
163
+ :dimensions => @tags
164
+ }
165
+ end
166
+
167
+ unless snapshot_keys.empty?
168
+ snapshot = metric.snapshot
169
+ snapshot_keys.flatten.each do |key|
170
+ name = key.to_s.gsub(/^get_/, '')
171
+ datapoints << {
172
+ :metric => "#{base_name}.#{name}",
173
+ :timestamp => time,
174
+ :value => snapshot.send(key),
175
+ :dimensions => @tags
176
+ }
177
+ end
178
+ end
179
+ datapoints
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,77 @@
1
+ ## This is the rakegem gemspec template. Make sure you read and understand
2
+ ## all of the comments. Some sections require modification, and others can
3
+ ## be deleted if you don't need them. Once you understand the contents of
4
+ ## this file, feel free to delete any comments that begin with two hash marks.
5
+ ## You can find comprehensive Gem::Specification documentation, at
6
+ ## http://docs.rubygems.org/read/chapter/20
7
+ Gem::Specification.new do |s|
8
+ s.specification_version = 2 if s.respond_to? :specification_version=
9
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
10
+ s.rubygems_version = '1.3.5'
11
+
12
+ ## Leave these as is they will be modified for you by the rake gemspec task.
13
+ ## If your rubyforge_project name is different, then edit it and comment out
14
+ ## the sub! line in the Rakefile
15
+ s.name = 'metriks-addons'
16
+ s.version = '2.1.0'
17
+ s.date = '2015-08-18'
18
+
19
+ ## Make sure your summary is short. The description may be as long
20
+ ## as you like.
21
+ s.summary = "Reporters for Metriks."
22
+ s.description = "OpenTSDB and SignalFX reporter for Metriks."
23
+
24
+ ## List the primary authors. If there are a bunch of authors, it's probably
25
+ ## better to set the email to an email list or something. If you don't have
26
+ ## a custom homepage, consider using your GitHub URL or the like.
27
+ s.authors = ["Rajat Venkatesh"]
28
+ s.email = 'rvenkatesh@qubole.com'
29
+ s.homepage = 'https://github.com/vrajat/metriks-addons'
30
+
31
+ ## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as
32
+ ## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'
33
+ s.require_paths = %w[lib]
34
+
35
+ ## This sections is only necessary if you have C extensions.
36
+ # s.require_paths << 'ext'
37
+ # s.extensions = %w[ext/extconf.rb]
38
+
39
+ ## If your gem includes any executables, list them here.
40
+ # s.executables = ["name"]
41
+
42
+ ## Specify any RDoc options here. You'll want to add your README and
43
+ ## LICENSE files to the extra_rdoc_files list.
44
+ s.rdoc_options = ["--charset=UTF-8"]
45
+ s.extra_rdoc_files = %w[README.md LICENSE]
46
+
47
+ ## List your runtime dependencies here. Runtime dependencies are those
48
+ ## that are needed for an end user to actually USE your code.
49
+ s.add_runtime_dependency 'metriks', '~> 0.9', '>= 0.9.9.7'
50
+ s.add_runtime_dependency 'rest-client', '>= 1.6.7'
51
+
52
+ ## List your development dependencies here. Development dependencies are
53
+ ## those that are only needed during development
54
+ s.add_development_dependency('mocha', ['~> 0.10'])
55
+
56
+ ## Leave this section as-is. It will be automatically generated from the
57
+ ## contents of your Git repository via the gemspec task. DO NOT REMOVE
58
+ ## THE MANIFEST COMMENTS, they are used as delimiters by the task.
59
+ # = MANIFEST =
60
+ s.files = %w[
61
+ Gemfile
62
+ LICENSE
63
+ README.md
64
+ Rakefile
65
+ lib/metriks-addons.rb
66
+ lib/metriks/opentsdb_reporter.rb
67
+ lib/metriks/signalfx_reporter.rb
68
+ metriks-addons.gemspec
69
+ spec/opentsdb_spec.rb
70
+ spec/signalfx_spec.rb
71
+ ]
72
+ # = MANIFEST =
73
+
74
+ ## Test files will be grabbed from the file list. Make sure the path glob
75
+ ## matches what you actually use.
76
+ s.test_files = s.files.select { |path| path =~ /^test\/.*_test\.rb/ }
77
+ end
@@ -0,0 +1,238 @@
1
+ require 'webmock/rspec'
2
+ require 'metriks'
3
+ require 'metriks/opentsdb_reporter'
4
+
5
+ describe "Smoke test" do
6
+ before(:all) do
7
+ stub_request(:any, "http://localhost:4242")
8
+ end
9
+
10
+ before(:each) do
11
+ @registry = Metriks::Registry.new
12
+ @reporter = Metriks::OpenTSDBReporter.new(
13
+ 'http://localhost:4242',
14
+ {:env => "test"},
15
+ { :registry => @registry })
16
+ end
17
+
18
+ after(:each) do
19
+ @reporter.stop
20
+ @registry.stop
21
+ end
22
+
23
+ it "meter" do
24
+ @registry.meter('meter.testing').mark
25
+ datapoints = @reporter.get_datapoints
26
+ expect(datapoints.size).to eql(5)
27
+ expect(datapoints[0][:metric]).to eql("meter.testing.count")
28
+ expect(datapoints[0][:value]).to eql(1)
29
+ expect(datapoints[0][:tags]).to include(:env => "test")
30
+ expect(datapoints[0][:timestamp]).not_to be_nil
31
+
32
+ expect(datapoints[1][:metric]).to eql("meter.testing.one_minute_rate")
33
+ expect(datapoints[1][:value]).not_to be_nil
34
+ expect(datapoints[1][:tags]).to include(:env => "test")
35
+ expect(datapoints[1][:timestamp]).not_to be_nil
36
+
37
+ expect(datapoints[2][:metric]).to eql("meter.testing.five_minute_rate")
38
+ expect(datapoints[2][:value]).to eql(0.0)
39
+ expect(datapoints[2][:tags]).to include(:env => "test")
40
+ expect(datapoints[2][:timestamp]).not_to be_nil
41
+
42
+ expect(datapoints[3][:metric]).to eql("meter.testing.fifteen_minute_rate")
43
+ expect(datapoints[3][:value]).to eql(0.0)
44
+ expect(datapoints[3][:tags]).to include(:env => "test")
45
+ expect(datapoints[3][:timestamp]).not_to be_nil
46
+
47
+ expect(datapoints[4][:metric]).to eql("meter.testing.mean_rate")
48
+ expect(datapoints[4][:value]).not_to be_nil
49
+ expect(datapoints[4][:tags]).to include(:env => "test")
50
+ expect(datapoints[4][:timestamp]).not_to be_nil
51
+ end
52
+
53
+ it "counter" do
54
+ @registry.counter('counter.testing').increment
55
+ datapoints = @reporter.get_datapoints
56
+ expect(datapoints.size).to eql(1)
57
+ expect(datapoints[0][:metric]).to eql("counter.testing.count")
58
+ expect(datapoints[0][:value]).to eql(1)
59
+ expect(datapoints[0][:tags]).to include(:env => "test")
60
+ expect(datapoints[0][:timestamp]).not_to be_nil
61
+ end
62
+
63
+ it "timer" do
64
+ @registry.timer('timer.testing').update(1.5)
65
+ datapoints = @reporter.get_datapoints
66
+ expect(datapoints.size).to eql(11)
67
+ expect(datapoints[0][:metric]).to eql("timer.testing.count")
68
+ expect(datapoints[0][:value]).to eql(1)
69
+ expect(datapoints[0][:tags]).to include(:env => "test")
70
+ expect(datapoints[0][:timestamp]).not_to be_nil
71
+
72
+ expect(datapoints[1][:metric]).to eql("timer.testing.one_minute_rate")
73
+ expect(datapoints[1][:value]).not_to be_nil
74
+ expect(datapoints[1][:tags]).to include(:env => "test")
75
+ expect(datapoints[1][:timestamp]).not_to be_nil
76
+
77
+ expect(datapoints[2][:metric]).to eql("timer.testing.five_minute_rate")
78
+ expect(datapoints[2][:value]).to eql(0.0)
79
+ expect(datapoints[2][:tags]).to include(:env => "test")
80
+ expect(datapoints[2][:timestamp]).not_to be_nil
81
+
82
+ expect(datapoints[3][:metric]).to eql("timer.testing.fifteen_minute_rate")
83
+ expect(datapoints[3][:value]).to eql(0.0)
84
+ expect(datapoints[3][:tags]).to include(:env => "test")
85
+ expect(datapoints[3][:timestamp]).not_to be_nil
86
+
87
+ expect(datapoints[4][:metric]).to eql("timer.testing.mean_rate")
88
+ expect(datapoints[4][:value]).not_to be_nil
89
+ expect(datapoints[4][:tags]).to include(:env => "test")
90
+ expect(datapoints[4][:timestamp]).not_to be_nil
91
+
92
+ expect(datapoints[5][:metric]).to eql("timer.testing.min")
93
+ expect(datapoints[5][:value]).not_to be_nil
94
+ expect(datapoints[5][:tags]).to include(:env => "test")
95
+ expect(datapoints[5][:timestamp]).not_to be_nil
96
+
97
+ expect(datapoints[6][:metric]).to eql("timer.testing.max")
98
+ expect(datapoints[6][:value]).not_to be_nil
99
+ expect(datapoints[6][:tags]).to include(:env => "test")
100
+ expect(datapoints[6][:timestamp]).not_to be_nil
101
+
102
+ expect(datapoints[7][:metric]).to eql("timer.testing.mean")
103
+ expect(datapoints[7][:value]).not_to be_nil
104
+ expect(datapoints[7][:tags]).to include(:env => "test")
105
+ expect(datapoints[7][:timestamp]).not_to be_nil
106
+
107
+ expect(datapoints[8][:metric]).to eql("timer.testing.stddev")
108
+ expect(datapoints[8][:value]).not_to be_nil
109
+ expect(datapoints[8][:tags]).to include(:env => "test")
110
+ expect(datapoints[8][:timestamp]).not_to be_nil
111
+
112
+ expect(datapoints[9][:metric]).to eql("timer.testing.median")
113
+ expect(datapoints[9][:value]).not_to be_nil
114
+ expect(datapoints[9][:tags]).to include(:env => "test")
115
+ expect(datapoints[9][:timestamp]).not_to be_nil
116
+
117
+ expect(datapoints[10][:metric]).to eql("timer.testing.95th_percentile")
118
+ expect(datapoints[10][:value]).not_to be_nil
119
+ expect(datapoints[10][:tags]).to include(:env => "test")
120
+ expect(datapoints[10][:timestamp]).not_to be_nil
121
+ end
122
+
123
+ it "histogram" do
124
+ @registry.histogram('histogram.testing').update(1.5)
125
+ datapoints = @reporter.get_datapoints
126
+ expect(datapoints.size).to eql(7)
127
+ expect(datapoints[0][:metric]).to eql("histogram.testing.count")
128
+ expect(datapoints[0][:value]).to eql(1)
129
+ expect(datapoints[0][:tags]).to include(:env => "test")
130
+ expect(datapoints[0][:timestamp]).not_to be_nil
131
+
132
+ expect(datapoints[1][:metric]).to eql("histogram.testing.min")
133
+ expect(datapoints[1][:value]).not_to be_nil
134
+ expect(datapoints[1][:tags]).to include(:env => "test")
135
+ expect(datapoints[1][:timestamp]).not_to be_nil
136
+
137
+ expect(datapoints[2][:metric]).to eql("histogram.testing.max")
138
+ expect(datapoints[2][:value]).not_to be_nil
139
+ expect(datapoints[2][:tags]).to include(:env => "test")
140
+ expect(datapoints[2][:timestamp]).not_to be_nil
141
+
142
+ expect(datapoints[3][:metric]).to eql("histogram.testing.mean")
143
+ expect(datapoints[3][:value]).not_to be_nil
144
+ expect(datapoints[3][:tags]).to include(:env => "test")
145
+ expect(datapoints[3][:timestamp]).not_to be_nil
146
+
147
+ expect(datapoints[4][:metric]).to eql("histogram.testing.stddev")
148
+ expect(datapoints[4][:value]).not_to be_nil
149
+ expect(datapoints[4][:tags]).to include(:env => "test")
150
+ expect(datapoints[4][:timestamp]).not_to be_nil
151
+
152
+ expect(datapoints[5][:metric]).to eql("histogram.testing.median")
153
+ expect(datapoints[5][:value]).not_to be_nil
154
+ expect(datapoints[5][:tags]).to include(:env => "test")
155
+ expect(datapoints[5][:timestamp]).not_to be_nil
156
+
157
+ expect(datapoints[6][:metric]).to eql("histogram.testing.95th_percentile")
158
+ expect(datapoints[6][:value]).not_to be_nil
159
+ expect(datapoints[6][:tags]).to include(:env => "test")
160
+ expect(datapoints[6][:timestamp]).not_to be_nil
161
+ end
162
+
163
+ it "gauge" do
164
+ @registry.gauge('gauge.testing') { 123 }
165
+ datapoints = @reporter.get_datapoints
166
+ expect(datapoints.size).to eql(1)
167
+ expect(datapoints[0][:metric]).to eql("gauge.testing.value")
168
+ expect(datapoints[0][:value]).to eql(123)
169
+ expect(datapoints[0][:tags]).to include(:env => "test")
170
+ expect(datapoints[0][:timestamp]).not_to be_nil
171
+ end
172
+ end
173
+
174
+ describe "Rest Client" do
175
+ before(:each) do
176
+ @registry = Metriks::Registry.new
177
+ @reporter = Metriks::OpenTSDBReporter.new(
178
+ 'http://localhost:4242',
179
+ {:env => "test"},
180
+ { :registry => @registry, :batch_size => 3})
181
+ stub_request(:post, "http://localhost:4242/api/put").
182
+ with(:body => /^\[.*\]$/).
183
+ to_return(:status => 200, :body => "", :headers => {})
184
+ end
185
+
186
+ it "Send a single metric" do
187
+ @registry.counter('counter.testing').increment
188
+ @reporter.submit @reporter.get_datapoints
189
+ #TODO Also check body
190
+ body = [{
191
+ :metric => "counter.testing",
192
+ :timestamp => 1438446900,
193
+ :value => 1,
194
+ :tags => { :env => "test"}}
195
+ ]
196
+ expect(a_request(:post, "http://localhost:4242/api/put")).to have_been_made
197
+ end
198
+
199
+ it "Send a three metric" do
200
+ for i in 0..2 do
201
+ @registry.counter("counter.testing.#{i}").increment
202
+ end
203
+ @reporter.submit @reporter.get_datapoints
204
+ expect(a_request(:post, "http://localhost:4242/api/put")).to have_been_made
205
+ end
206
+
207
+ it "Send a four metric" do
208
+ for i in 0..3 do
209
+ @registry.counter("counter.testing.#{i}").increment
210
+ end
211
+ @reporter.submit @reporter.get_datapoints
212
+ expect(a_request(:post, "http://localhost:4242/api/put")).to have_been_made.times(2)
213
+ end
214
+
215
+ it "Send a five metric" do
216
+ for i in 0..4 do
217
+ @registry.counter("counter.testing.#{i}").increment
218
+ end
219
+ @reporter.submit @reporter.get_datapoints
220
+ expect(a_request(:post, "http://localhost:4242/api/put")).to have_been_made.times(2)
221
+ end
222
+
223
+ it "Send a six metric" do
224
+ for i in 0..5 do
225
+ @registry.counter("counter.testing.#{i}").increment
226
+ end
227
+ @reporter.submit @reporter.get_datapoints
228
+ expect(a_request(:post, "http://localhost:4242/api/put")).to have_been_made.times(2)
229
+ end
230
+
231
+ it "Send a seven metric" do
232
+ for i in 0..7 do
233
+ @registry.counter("counter.testing.#{i}").increment
234
+ end
235
+ @reporter.submit @reporter.get_datapoints
236
+ expect(a_request(:post, "http://localhost:4242/api/put")).to have_been_made.times(3)
237
+ end
238
+ end