tsd_metrics 0.2.0 → 0.2.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,289 @@
1
+ # Copyright 2014 Groupon.com
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'tsd_metrics/tsd_metric'
16
+ require 'timecop'
17
+ include TsdMetrics
18
+
19
+ describe "TsdMetric" do
20
+ let(:sink) { double(:sink, :record => nil) }
21
+ let(:mutexer) do
22
+ mock = double()
23
+ allow(mock).to receive(:synchronize).and_yield()
24
+ mock
25
+ end
26
+ let(:startTime) { Time.now }
27
+ let(:metric) { TsdMetric.new(startTime, sink, mutexer) }
28
+ def closeAndGetEvent(metric=metric)
29
+ event = nil
30
+ allow(sink).to receive(:record) do |metricEvent|
31
+ event = metricEvent
32
+ end
33
+ metric.close()
34
+ event
35
+ end
36
+ def pickValuesFrom(metric)
37
+ metric.map do |sample|
38
+ sample[:value]
39
+ end
40
+ end
41
+ def pickUnitsFrom(metric)
42
+ metric.map do |sample|
43
+ sample[:unit]
44
+ end
45
+ end
46
+
47
+ context "with the minumum state" do
48
+ let(:gaugeName) { "myGauge"}
49
+ let(:simplestMetric) do
50
+ metric.tap{|m| m.setGauge(gaugeName, 100)}
51
+ end
52
+
53
+ it "outputs the minimum state for a metric" do
54
+ event = nil
55
+ expect(sink).to receive(:record) do |metricEvent|
56
+ event = metricEvent
57
+ end
58
+ beforeCloseTime = Time.now()
59
+ simplestMetric.close()
60
+ afterCloseTime = Time.now()
61
+
62
+ event.should_not be_nil
63
+ annotations = event.annotations
64
+ annotations.should_not be_nil
65
+ annotations[:initTimestamp].should == startTime
66
+ annotations[:finalTimestamp].should >= beforeCloseTime
67
+ annotations[:finalTimestamp].should <= afterCloseTime
68
+ end
69
+
70
+ it "throws an exception if any operations are done after closing" do
71
+ closeAndGetEvent(simplestMetric)
72
+ expect { simplestMetric.setGauge("something", 1) }.to raise_error TsdMetrics::Error
73
+ end
74
+
75
+ describe "open?" do
76
+ it "works as expected" do
77
+ expect { closeAndGetEvent(simplestMetric) }
78
+ .to change(simplestMetric, :open?).from(true).to(false)
79
+ end
80
+ end
81
+ end
82
+
83
+ describe "gauges" do
84
+ let(:gaugeName) { "myGauge"}
85
+ let(:gaugeName2) { "myOtherGauge"}
86
+ it "can be set" do
87
+ metric.setGauge(gaugeName, 100, :byte)
88
+ metric.setGauge(gaugeName, 307, :byte)
89
+
90
+ metric.setGauge(gaugeName2, 50, :week)
91
+ metric.setGauge(gaugeName2, 23, :week)
92
+
93
+ event = closeAndGetEvent
94
+ event.gauges[gaugeName].should =~ [{value: 100, unit: :byte}, {value: 307, unit: :byte}]
95
+ event.gauges[gaugeName2].should =~ [{value: 50, unit: :week}, {value: 23, unit: :week}]
96
+ end
97
+ end
98
+
99
+ describe "timers" do
100
+ let(:timerName) { :myTimer }
101
+ it "can be started and stopped multiple times" do
102
+ Timecop.freeze(Time.now)
103
+ metric.startTimer(timerName)
104
+ Timecop.freeze(Time.now + 10)
105
+ metric.stopTimer(timerName)
106
+ Timecop.freeze(Time.now + 5)
107
+ metric.startTimer(timerName)
108
+ Timecop.freeze(Time.now + 25)
109
+ metric.stopTimer(timerName)
110
+ Timecop.return
111
+
112
+ event = closeAndGetEvent
113
+ values = pickValuesFrom(event.timers[timerName])
114
+ values.should include(10000000000)
115
+ values.should include(25000000000)
116
+ end
117
+
118
+ its "default unit is nanoseconds" do
119
+ Timecop.freeze(Time.now)
120
+ metric.startTimer(timerName)
121
+ Timecop.freeze(Time.now + 12)
122
+ metric.stopTimer(timerName)
123
+ Timecop.return
124
+ event = closeAndGetEvent
125
+ event.timers[timerName].should == [{value: 12e9, unit: :nanosecond}]
126
+ end
127
+
128
+ it "only record timers that are stopped when metric is closed" do
129
+ Timecop.freeze(Time.now)
130
+ metric.startTimer(timerName)
131
+ Timecop.freeze(Time.now + 10)
132
+ metric.stopTimer(timerName)
133
+ Timecop.freeze(Time.now + 5)
134
+ metric.startTimer(timerName)
135
+ Timecop.freeze(Time.now + 25)
136
+
137
+ event = closeAndGetEvent
138
+ values = pickValuesFrom(event.timers[timerName])
139
+ values.should == [10e9]
140
+
141
+ Timecop.return
142
+ end
143
+
144
+ it "can be added manually" do
145
+ metric.setTimer(timerName, 20.5, :second)
146
+ event = closeAndGetEvent
147
+ pickValuesFrom(event.timers[timerName]).should include(20.5)
148
+ end
149
+
150
+ it "keeps whole sample groups when no samples are stopped" do
151
+ otherTimerName = "myOtherTimer"
152
+ metric.startTimer(timerName)
153
+ metric.startTimer(otherTimerName)
154
+
155
+ metric.stopTimer(otherTimerName)
156
+
157
+ event = closeAndGetEvent
158
+ event.timers.should include(timerName)
159
+ end
160
+
161
+ it "orders timer samples by start time" do
162
+ # Procedural interface first
163
+ Timecop.freeze(Time.now)
164
+ metric.startTimer(timerName)
165
+
166
+ # Then start an OO one
167
+ Timecop.freeze(Time.now + 2)
168
+ timerSample1 = metric.createTimer(timerName)
169
+
170
+ # And another
171
+ Timecop.freeze(Time.now + 4)
172
+ timerSample2 = metric.createTimer(timerName)
173
+
174
+ # Now stop them in reverse order
175
+ Timecop.freeze(Time.now + 8)
176
+ timerSample2.stop()
177
+ Timecop.freeze(Time.now + 16)
178
+ timerSample1.stop()
179
+ Timecop.freeze(Time.now + 32)
180
+ metric.stopTimer(timerName)
181
+
182
+ # Ensure the order of samples is the same as the timer-starting order
183
+ event = closeAndGetEvent
184
+ values = pickValuesFrom(event.timers[timerName])
185
+ values.should == [62e9, 28e9, 8e9]
186
+
187
+ Timecop.return
188
+
189
+ end
190
+
191
+ it "can be obtained as an object" do
192
+ timerSample = nil
193
+ expect { timerSample = metric.createTimer(:timerName) }.not_to raise_error
194
+ timerSample.should_not == nil
195
+ end
196
+
197
+ it "contains the unit for each timer" do
198
+ metric.setTimer(timerName, 17.5, :second)
199
+ event = closeAndGetEvent
200
+ pickUnitsFrom(event.timers[timerName]).should include(:second)
201
+ [{:a => "b"}].should include({:a => "b"})
202
+ end
203
+
204
+ describe "OO interface" do
205
+ let(:ooTimer) { metric.createTimer(timerName) }
206
+ it "can be created and stopped to record a sample" do
207
+ Timecop.freeze(Time.now)
208
+ # Causes creation of the timer; 'let' is lazy
209
+ ooTimer
210
+ Timecop.freeze(Time.now + 12)
211
+ ooTimer.stop
212
+ Timecop.return
213
+ event = closeAndGetEvent
214
+ pickValuesFrom(event.timers[timerName]).should include(12000000000)
215
+ end
216
+
217
+ describe "stopped?" do
218
+ it "works as expected" do
219
+ expect { ooTimer.stop }.to change(ooTimer, :stopped?).from(false).to(true)
220
+ end
221
+ end
222
+ end
223
+ end
224
+
225
+ describe "counters" do
226
+ let(:counterName) { :myCounter }
227
+
228
+ it "default to start at 0" do
229
+ metric.incrementCounter(counterName)
230
+ event = closeAndGetEvent
231
+ pickValuesFrom(event.counters[counterName]).should == [1]
232
+ end
233
+
234
+ it "can be incremented and decremented" do
235
+ metric.resetCounter(counterName)
236
+ 12.times { metric.decrementCounter(counterName) }
237
+ # Now at -12
238
+ 2.times { metric.decrementCounter(counterName, 3) }
239
+ # Now at -18
240
+ 2.times { metric.incrementCounter(counterName) }
241
+ # Now at -16
242
+ 3.times { metric.incrementCounter(counterName, 23) }
243
+ # Now at 53
244
+ event = closeAndGetEvent
245
+ pickValuesFrom(event.counters[counterName]).should include 53
246
+ end
247
+
248
+ it "can be reset to create multiple values" do
249
+ metric.resetCounter(counterName)
250
+ metric.incrementCounter(counterName)
251
+ metric.resetCounter(counterName)
252
+ 3.times { metric.incrementCounter(counterName) }
253
+ metric.resetCounter(counterName)
254
+ # Test for existance of array items without testing order
255
+ pickValuesFrom(closeAndGetEvent.counters[counterName]).should =~ [3,0,1]
256
+ end
257
+
258
+ it "can be obtained as an object" do
259
+ counterSample = nil
260
+ expect { counterSample = metric.createCounter(:counterName) }.not_to raise_error
261
+ counterSample.should_not == nil
262
+ end
263
+
264
+ describe "OO interface" do
265
+ let(:ooCounter) { metric.createCounter(counterName) }
266
+ it "can be incremented and decremented to produce a sample" do
267
+ 12.times { ooCounter.decrement() }
268
+ # Now at -12
269
+ 2.times { ooCounter.decrement(3) }
270
+ # Now at -18
271
+ 2.times { ooCounter.increment() }
272
+ # Now at -16
273
+ 3.times { ooCounter.increment(23) }
274
+ # Now at 53
275
+ event = closeAndGetEvent
276
+ pickValuesFrom(event.counters[counterName]).should include 53
277
+ end
278
+ end
279
+ end
280
+
281
+ describe "annotations" do
282
+ let(:annotationName) { :myAnnotation }
283
+ let(:annotationString) { "it was the worst of times, it was the blurst of times" }
284
+ it "can be set on the metric" do
285
+ metric.annotate(annotationName, annotationString)
286
+ closeAndGetEvent.annotations[annotationName].should == annotationString
287
+ end
288
+ end
289
+ end
@@ -0,0 +1,22 @@
1
+ # Copyright 2014 Groupon.com
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'tsd_metrics'
16
+ describe "TsdMetrics" do
17
+ it "can build a TsdMetric object" do
18
+ metric = TsdMetrics.buildMetric
19
+ metric.should_not be_nil
20
+ metric.should be_a(TsdMetric)
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ # Copyright 2014 Groupon.com
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'tsd_metrics/units'
16
+
17
+ describe "UnitsUtils" do
18
+ describe "isValidUnitValue?" do
19
+ it "allows noUnit as a valid value" do
20
+ TsdMetrics::UnitsUtils.isValidUnitValue?(:noUnit).should be_true
21
+ end
22
+ it "validates a valid unit" do
23
+ TsdMetrics::UnitsUtils.isValidUnitValue?(:second).should be_true
24
+ end
25
+ it "invalidates a non-valid unit" do
26
+ TsdMetrics::UnitsUtils.isValidUnitValue?(:parsecs).should be_false
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ # Copyright 2014 Groupon.com
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ Gem::Specification.new do |s|
16
+ s.name = 'tsd_metrics'
17
+ s.version = '0.2.8'
18
+ s.date = '2015-01-15'
19
+ s.summary = "TSD Metrics client for Ruby"
20
+ s.description = "A client for logging to TSD-Aggregation-logging-compatible JSON files"
21
+ s.authors = ["Matthew Hayter"]
22
+ s.email = 'matthewhayter@gmail.com'
23
+ s.licenses = ['Apache License, Version 2.0']
24
+
25
+ s.files = `git ls-files`.split($/)
26
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
27
+ s.test_files = s.files.grep(%r{^spec/})
28
+ s.require_paths = ["lib", "resources"]
29
+
30
+ s.add_development_dependency 'rspec', '2.14.1'
31
+ s.add_development_dependency 'timecop', '0.7.1'
32
+ s.add_development_dependency 'pry', '0.9.12.6'
33
+ s.add_development_dependency 'json-schema', '2.4.1'
34
+ s.add_development_dependency 'rake', '10.4.2'
35
+ end
metadata CHANGED
@@ -1,109 +1,151 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: tsd_metrics
3
- version: !ruby/object:Gem::Version
4
- hash: 23
5
- prerelease:
6
- segments:
7
- - 0
8
- - 2
9
- - 0
10
- version: 0.2.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.8
11
5
  platform: ruby
12
- authors:
6
+ authors:
13
7
  - Matthew Hayter
14
8
  autorequire:
15
9
  bindir: bin
16
10
  cert_chain: []
17
-
18
- date: 2014-02-18 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
11
+ date: 2015-01-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
21
14
  name: rspec
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
- none: false
25
- requirements:
26
- - - ~>
27
- - !ruby/object:Gem::Version
28
- hash: 31
29
- segments:
30
- - 2
31
- - 14
32
- version: "2.14"
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 2.14.1
33
20
  type: :development
34
- version_requirements: *id001
35
- - !ruby/object:Gem::Dependency
36
- name: timecop
37
21
  prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
39
- none: false
40
- requirements:
41
- - - ~>
42
- - !ruby/object:Gem::Version
43
- hash: 5
44
- segments:
45
- - 0
46
- - 7
47
- version: "0.7"
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 2.14.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: timecop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.7.1
48
34
  type: :development
49
- version_requirements: *id002
50
- - !ruby/object:Gem::Dependency
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.7.1
41
+ - !ruby/object:Gem::Dependency
51
42
  name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.9.12.6
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.9.12.6
55
+ - !ruby/object:Gem::Dependency
56
+ name: json-schema
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 2.4.1
62
+ type: :development
52
63
  prerelease: false
53
- requirement: &id003 !ruby/object:Gem::Requirement
54
- none: false
55
- requirements:
56
- - - ~>
57
- - !ruby/object:Gem::Version
58
- hash: 25
59
- segments:
60
- - 0
61
- - 9
62
- version: "0.9"
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 2.4.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 10.4.2
63
76
  type: :development
64
- version_requirements: *id003
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 10.4.2
65
83
  description: A client for logging to TSD-Aggregation-logging-compatible JSON files
66
84
  email: matthewhayter@gmail.com
67
85
  executables: []
68
-
69
86
  extensions: []
70
-
71
87
  extra_rdoc_files: []
72
-
73
- files:
88
+ files:
89
+ - .gitignore
90
+ - .travis.yml
91
+ - CONTRIBUTORS.md
92
+ - DEPENDENCIES.md
93
+ - Gemfile
94
+ - LICENSE
95
+ - README.md
96
+ - Rakefile
97
+ - demo.rb
98
+ - doc/QUERY_LOG_FORMAT.md
99
+ - doc/query-log-schema-2c.json
100
+ - doc/query-log-schema-2d.json
101
+ - doc/query-log-schema-2e.json
74
102
  - lib/tsd_metrics.rb
103
+ - lib/tsd_metrics/async_queue_writer.rb
104
+ - lib/tsd_metrics/counter.rb
105
+ - lib/tsd_metrics/counter_sample.rb
106
+ - lib/tsd_metrics/exceptions.rb
107
+ - lib/tsd_metrics/json_formatting_sink.rb
108
+ - lib/tsd_metrics/metric_builder_for_single_struct_receiver.rb
109
+ - lib/tsd_metrics/queue_writer.rb
110
+ - lib/tsd_metrics/timer.rb
111
+ - lib/tsd_metrics/timer_sample.rb
112
+ - lib/tsd_metrics/tsd_metric.rb
113
+ - lib/tsd_metrics/units.rb
114
+ - resources/LICENSE
115
+ - spec/integration_spec.rb
116
+ - spec/json_formatter_spec.rb
117
+ - spec/tsd_metric_spec.rb
118
+ - spec/tsd_metrics_spec.rb
119
+ - spec/units_utils_spec.rb
120
+ - tsd_metrics.gemspec
75
121
  homepage:
76
- licenses: []
77
-
122
+ licenses:
123
+ - Apache License, Version 2.0
124
+ metadata: {}
78
125
  post_install_message:
79
126
  rdoc_options: []
80
-
81
- require_paths:
127
+ require_paths:
82
128
  - lib
83
- required_ruby_version: !ruby/object:Gem::Requirement
84
- none: false
85
- requirements:
86
- - - ">="
87
- - !ruby/object:Gem::Version
88
- hash: 3
89
- segments:
90
- - 0
91
- version: "0"
92
- required_rubygems_version: !ruby/object:Gem::Requirement
93
- none: false
94
- requirements:
95
- - - ">="
96
- - !ruby/object:Gem::Version
97
- hash: 3
98
- segments:
99
- - 0
100
- version: "0"
129
+ - resources
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ! '>='
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ! '>='
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
101
140
  requirements: []
102
-
103
141
  rubyforge_project:
104
- rubygems_version: 1.8.28
142
+ rubygems_version: 2.4.5
105
143
  signing_key:
106
- specification_version: 3
144
+ specification_version: 4
107
145
  summary: TSD Metrics client for Ruby
108
- test_files: []
109
-
146
+ test_files:
147
+ - spec/integration_spec.rb
148
+ - spec/json_formatter_spec.rb
149
+ - spec/tsd_metric_spec.rb
150
+ - spec/tsd_metrics_spec.rb
151
+ - spec/units_utils_spec.rb