metriks-addons 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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