tsd_metrics 0.2.0 → 0.2.8

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,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