right_support 2.7.0 → 2.8.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.
- data/Gemfile +1 -1
- data/Gemfile.lock +23 -21
- data/Rakefile +6 -1
- data/VERSION +1 -1
- data/lib/right_support/net/request_balancer.rb +2 -2
- data/lib/right_support/stats/activity.rb +103 -44
- data/lib/right_support/stats/exceptions.rb +54 -17
- data/lib/right_support/stats/helpers.rb +108 -125
- data/right_support.gemspec +8 -12
- data/spec/stats/activity_spec.rb +234 -122
- data/spec/stats/exceptions_spec.rb +172 -66
- data/spec/stats/helpers_spec.rb +57 -0
- metadata +25 -25
data/spec/stats/activity_spec.rb
CHANGED
@@ -43,151 +43,263 @@ describe RightSupport::Stats::Activity do
|
|
43
43
|
@stats = RightSupport::Stats::Activity.new
|
44
44
|
end
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
context :initialize do
|
47
|
+
it "initializes stats data" do
|
48
|
+
@stats.instance_variable_get(:@interval).should == 0.0
|
49
|
+
@stats.instance_variable_get(:@last_start_time).should == @now
|
50
|
+
@stats.instance_variable_get(:@avg_duration).should be_nil
|
51
|
+
@stats.instance_variable_get(:@total).should == 0
|
52
|
+
@stats.instance_variable_get(:@count_per_type).should == {}
|
53
|
+
end
|
52
54
|
end
|
53
55
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
56
|
+
context :update do
|
57
|
+
it "updates count and interval information" do
|
58
|
+
flexmock(Time).should_receive(:now).and_return(1000010)
|
59
|
+
@stats.update
|
60
|
+
@stats.instance_variable_get(:@interval).should == 1.0
|
61
|
+
@stats.instance_variable_get(:@last_start_time).should == @now + 10
|
62
|
+
@stats.instance_variable_get(:@avg_duration).should be_nil
|
63
|
+
@stats.instance_variable_get(:@total).should == 1
|
64
|
+
@stats.instance_variable_get(:@count_per_type).should == {}
|
65
|
+
end
|
63
66
|
|
64
|
-
|
65
|
-
|
67
|
+
it "updates counts per type when type provided" do
|
68
|
+
flexmock(Time).should_receive(:now).and_return(1000010)
|
69
|
+
@stats.update("test")
|
70
|
+
@stats.instance_variable_get(:@interval).should == 1.0
|
71
|
+
@stats.instance_variable_get(:@last_start_time).should == @now + 10
|
72
|
+
@stats.instance_variable_get(:@avg_duration).should be_nil
|
73
|
+
@stats.instance_variable_get(:@total).should == 1
|
74
|
+
@stats.instance_variable_get(:@count_per_type).should == {"test" => 1}
|
75
|
+
end
|
66
76
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
77
|
+
it "doesn't update counts when type contains 'stats'" do
|
78
|
+
flexmock(Time).should_receive(:now).and_return(1000010)
|
79
|
+
@stats.update("my stats")
|
80
|
+
@stats.instance_variable_get(:@interval).should == 0.0
|
81
|
+
@stats.instance_variable_get(:@last_start_time).should == @now
|
82
|
+
@stats.instance_variable_get(:@avg_duration).should be_nil
|
83
|
+
@stats.instance_variable_get(:@total).should == 0
|
84
|
+
@stats.instance_variable_get(:@count_per_type).should == {}
|
85
|
+
end
|
76
86
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
@stats.instance_variable_get(:@count_per_type).should == {}
|
85
|
-
end
|
87
|
+
it "limits length of type string when submitting update" do
|
88
|
+
flexmock(Time).should_receive(:now).and_return(1000010)
|
89
|
+
@stats.update("test 12345678901234567890123456789012345678901234567890123456789")
|
90
|
+
@stats.instance_variable_get(:@total).should == 1
|
91
|
+
@stats.instance_variable_get(:@count_per_type).should ==
|
92
|
+
{"test 1234567890123456789012345678901234567890123456789012..." => 1}
|
93
|
+
end
|
86
94
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
95
|
+
it "doesn't convert symbol or boolean to string when submitting update" do
|
96
|
+
flexmock(Time).should_receive(:now).and_return(1000010)
|
97
|
+
@stats.update(:test)
|
98
|
+
@stats.update(true)
|
99
|
+
@stats.update(false)
|
100
|
+
@stats.instance_variable_get(:@total).should == 3
|
101
|
+
@stats.instance_variable_get(:@count_per_type).should == {:test => 1, true => 1, false => 1}
|
102
|
+
end
|
94
103
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
104
|
+
it "converts arbitrary type value to limited-length string when submitting update" do
|
105
|
+
flexmock(Time).should_receive(:now).and_return(1000010)
|
106
|
+
@stats.update({1 => 11, 2 => 22})
|
107
|
+
@stats.update({1 => 11, 2 => 22, 3 => 12345678901234567890123456789012345678901234567890123456789})
|
108
|
+
@stats.instance_variable_get(:@total).should == 2
|
109
|
+
@stats.instance_variable_get(:@count_per_type).should == {"{1=>11, 2=>22}" => 1,
|
110
|
+
"{1=>11, 2=>22, 3=>123456789012345678901234567890123456789..." => 1}
|
111
|
+
end
|
103
112
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
113
|
+
it "doesn't measure rate if disabled" do
|
114
|
+
@stats = RightSupport::Stats::Activity.new(false)
|
115
|
+
flexmock(Time).should_receive(:now).and_return(1000010)
|
116
|
+
@stats.update
|
117
|
+
@stats.instance_variable_get(:@interval).should == 0.0
|
118
|
+
@stats.instance_variable_get(:@last_start_time).should == @now + 10
|
119
|
+
@stats.instance_variable_get(:@avg_duration).should be_nil
|
120
|
+
@stats.instance_variable_get(:@total).should == 1
|
121
|
+
@stats.instance_variable_get(:@count_per_type).should == {}
|
122
|
+
end
|
111
123
|
end
|
112
124
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
125
|
+
context :finish do
|
126
|
+
it "updates duration when finish using internal start time by default" do
|
127
|
+
flexmock(Time).should_receive(:now).and_return(1000010)
|
128
|
+
@stats.avg_duration.should be_nil
|
129
|
+
@stats.finish
|
130
|
+
@stats.instance_variable_get(:@interval).should == 0.0
|
131
|
+
@stats.instance_variable_get(:@last_start_time).should == @now
|
132
|
+
@stats.instance_variable_get(:@avg_duration).should == 1.0
|
133
|
+
@stats.instance_variable_get(:@total).should == 0
|
134
|
+
@stats.instance_variable_get(:@count_per_type).should == {}
|
135
|
+
end
|
124
136
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
137
|
+
it "updates duration when finish using specified start time" do
|
138
|
+
flexmock(Time).should_receive(:now).and_return(1000030)
|
139
|
+
@stats.avg_duration.should be_nil
|
140
|
+
@stats.finish(1000010)
|
141
|
+
@stats.instance_variable_get(:@interval).should == 0.0
|
142
|
+
@stats.instance_variable_get(:@last_start_time).should == @now
|
143
|
+
@stats.instance_variable_get(:@avg_duration).should == 2.0
|
144
|
+
@stats.instance_variable_get(:@total).should == 0
|
145
|
+
@stats.instance_variable_get(:@count_per_type).should == {}
|
146
|
+
end
|
133
147
|
end
|
134
148
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
@stats.instance_variable_get(:@count_per_type).should == {}
|
149
|
+
context :avg_rate do
|
150
|
+
it "converts interval to rate" do
|
151
|
+
flexmock(Time).should_receive(:now).and_return(1000020)
|
152
|
+
@stats.avg_rate.should be_nil
|
153
|
+
@stats.update
|
154
|
+
@stats.instance_variable_get(:@interval).should == 2.0
|
155
|
+
@stats.avg_rate.should == 0.5
|
156
|
+
end
|
144
157
|
end
|
145
158
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
159
|
+
context :last do
|
160
|
+
it "reports number of seconds since last update or nil if no updates" do
|
161
|
+
flexmock(Time).should_receive(:now).and_return(1000010)
|
162
|
+
@stats.last.should be_nil
|
163
|
+
@stats.update
|
164
|
+
@stats.last.should == {"elapsed" => 0}
|
165
|
+
end
|
166
|
+
|
167
|
+
it "reports number of seconds since last update and last type" do
|
168
|
+
@stats.update("test")
|
169
|
+
flexmock(Time).should_receive(:now).and_return(1000010)
|
170
|
+
@stats.last.should == {"elapsed" => 10, "type" => "test"}
|
171
|
+
end
|
172
|
+
|
173
|
+
it "reports whether last activity is still active" do
|
174
|
+
@stats.update("test", "token")
|
175
|
+
flexmock(Time).should_receive(:now).and_return(1000010)
|
176
|
+
@stats.last.should == {"elapsed" => 10, "type" => "test", "active" => true}
|
177
|
+
@stats.finish(@now - 10, "token")
|
178
|
+
@stats.last.should == {"elapsed" => 10, "type" => "test", "active" => false}
|
179
|
+
@stats.instance_variable_get(:@avg_duration).should == 2.0
|
180
|
+
end
|
152
181
|
end
|
153
182
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
183
|
+
context :percentage do
|
184
|
+
it "converts count per type to percentages" do
|
185
|
+
flexmock(Time).should_receive(:now).and_return(1000010)
|
186
|
+
@stats.update("foo")
|
187
|
+
@stats.instance_variable_get(:@total).should == 1
|
188
|
+
@stats.instance_variable_get(:@count_per_type).should == {"foo" => 1}
|
189
|
+
@stats.percentage.should == {"total" => 1, "percent" => {"foo" => 100.0}}
|
190
|
+
@stats.update("bar")
|
191
|
+
@stats.instance_variable_get(:@total).should == 2
|
192
|
+
@stats.instance_variable_get(:@count_per_type).should == {"foo" => 1, "bar" => 1}
|
193
|
+
@stats.percentage.should == {"total" => 2, "percent" => {"foo" => 50.0, "bar" => 50.0}}
|
194
|
+
@stats.update("foo")
|
195
|
+
@stats.update("foo")
|
196
|
+
@stats.instance_variable_get(:@total).should == 4
|
197
|
+
@stats.instance_variable_get(:@count_per_type).should == {"foo" => 3, "bar" => 1}
|
198
|
+
@stats.percentage.should == {"total" => 4, "percent" => {"foo" => 75.0, "bar" => 25.0}}
|
199
|
+
end
|
159
200
|
end
|
160
201
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
202
|
+
context :all do
|
203
|
+
it "returns all activity aspects that were measured except duration" do
|
204
|
+
flexmock(Time).should_receive(:now).and_return(1000010)
|
205
|
+
@stats.update
|
206
|
+
@stats.all.should == {"last" => {"elapsed" => 0}, "total" => 1, "rate" => 1.0}
|
207
|
+
end
|
208
|
+
|
209
|
+
it "excludes rate if it is not being measured" do
|
210
|
+
@stats = RightSupport::Stats::Activity.new(false)
|
211
|
+
flexmock(Time).should_receive(:now).and_return(1000010)
|
212
|
+
@stats.update
|
213
|
+
@stats.all.should == {"last" => {"elapsed" => 0}, "total" => 1}
|
214
|
+
end
|
215
|
+
|
216
|
+
it "includes percentage breakdown when update recorded per type" do
|
217
|
+
@stats = RightSupport::Stats::Activity.new(false)
|
218
|
+
flexmock(Time).should_receive(:now).and_return(1000010, 1000010, 1000020, 1000020, 1000030, 1000030, 1000040)
|
219
|
+
@stats.update("foo")
|
220
|
+
@stats.all.should == {"last" => {"elapsed" => 0, "type" => "foo"}, "total" => 1, "percent" => {"foo" => 100.0}}
|
221
|
+
@stats.update("bar")
|
222
|
+
@stats.all.should == {"last" => {"elapsed" => 0, "type" => "bar"}, "total" => 2, "percent" => {"foo" => 50.0, "bar" => 50.0}}
|
223
|
+
@stats.update("bar")
|
224
|
+
@stats.update("foo")
|
225
|
+
@stats.all.should == {"last" => {"elapsed" => 10, "type" => "foo"}, "total" => 4, "percent" => {"foo" => 50.0, "bar" => 50.0}}
|
226
|
+
end
|
227
|
+
|
228
|
+
it "returns nil if there was no activity" do
|
229
|
+
@stats.all.should be_nil
|
230
|
+
end
|
165
231
|
end
|
166
232
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
233
|
+
context :average do
|
234
|
+
it "weights average toward past activity" do
|
235
|
+
@stats.send(:average, 0.0, 10.0).should == 1.0
|
236
|
+
@stats.send(:average, 1.0, 10.0).should == 1.9
|
237
|
+
@stats.send(:average, 1.9, 10.0).should == 2.71
|
238
|
+
@stats.send(:average, 2.71, 10.0).should == 3.439
|
239
|
+
@stats.send(:average, 3.439, 10.0).should == 4.0951
|
240
|
+
@stats.send(:average, 4.0951, 10.0).should == 4.68559
|
241
|
+
@stats.send(:average, 4.68559, 10.0).should == 5.217031
|
242
|
+
end
|
174
243
|
end
|
175
244
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
245
|
+
describe "class methods" do
|
246
|
+
|
247
|
+
context :all do
|
248
|
+
it "aggregates stats from multiple all calls" do
|
249
|
+
stats = [{"last" => {"elapsed" => 10, "type" => "foo"}, "total" => 1, "percent" => {"foo" => 100.0}},
|
250
|
+
{"last" => {"elapsed" => 20, "type" => "bar"}, "total" => 4, "percent" => {"bar" => 100.0}}]
|
251
|
+
RightSupport::Stats::Activity.all(stats).should ==
|
252
|
+
{"last" => {"elapsed" => 10, "type" => "foo"}, "total" => 5, "percent" => {"foo" => 20.0, "bar" => 80.0}}
|
253
|
+
end
|
254
|
+
|
255
|
+
it "includes rate if provided" do
|
256
|
+
stats = [{"last" => {"elapsed" => 10, "type" => "foo"}, "total" => 1, "percent" => {"foo" => 100.0}, "rate" => 0.5},
|
257
|
+
{"last" => {"elapsed" => 20, "type" => "bar"}, "total" => 4, "percent" => {"bar" => 100.0}, "rate" => 0.1}]
|
258
|
+
RightSupport::Stats::Activity.all(stats).should ==
|
259
|
+
{"last" => {"elapsed" => 10, "type" => "foo"}, "total" => 5, "percent" => {"foo" => 20.0, "bar" => 80.0}, "rate" => 0.18}
|
260
|
+
end
|
261
|
+
|
262
|
+
it "returns nil if there are no stats" do
|
263
|
+
RightSupport::Stats::Activity.all([]).should be_nil
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
context :percentage do
|
268
|
+
it "aggregates multiple percentage stats" do
|
269
|
+
stats = [{"total" => 1, "percent" => {"foo" => 100.0}},
|
270
|
+
{"total" => 5, "percent" => {"bar" => 100.0}},
|
271
|
+
{"total" => 4, "percent" => {"foo" => 75.0, "bar" => 25.0}}]
|
272
|
+
RightSupport::Stats::Activity.percentage(stats, 10).should == {"total" => 10, "percent" => {"foo" => 40.0, "bar" => 60.0}}
|
273
|
+
end
|
274
|
+
|
275
|
+
it "returns only total if there is no data" do
|
276
|
+
RightSupport::Stats::Activity.percentage([], 0).should == {"total" => 0}
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
context :avg_rate do
|
281
|
+
it "computes average rate from multiple average rates" do
|
282
|
+
stats = [{"total" => 1, "rate" => 0.5},
|
283
|
+
{"total" => 5, "rate" => 0.1},
|
284
|
+
{"total" => 4, "rate" => 0.2}]
|
285
|
+
RightSupport::Stats::Activity.avg_rate(stats, 10).should == 0.18
|
286
|
+
end
|
287
|
+
|
288
|
+
it "returns nil if there is no data" do
|
289
|
+
RightSupport::Stats::Activity.avg_rate([], 0).should be_nil
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
context :last do
|
294
|
+
it "determines last activity from multiple last activity stats" do
|
295
|
+
stats = [{"last" => {"elapsed" => 0, "type" => "foo"}, "total" => 1, "percent" => {"foo" => 100.0}},
|
296
|
+
{"last" => {"elapsed" => 0, "type" => "foo"}, "total" => 1, "percent" => {"foo" => 100.0}}]
|
297
|
+
end
|
298
|
+
|
299
|
+
it "returns nil if there is no data" do
|
300
|
+
RightSupport::Stats::Activity.last([]).should be_nil
|
301
|
+
end
|
302
|
+
end
|
191
303
|
end
|
192
304
|
|
193
305
|
end # RightSupport::Stats::Activity
|
@@ -33,90 +33,196 @@ describe RightSupport::Stats::Exceptions do
|
|
33
33
|
@exception = Exception.new("Test error")
|
34
34
|
end
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
context :initialize do
|
37
|
+
it "initializes stats data" do
|
38
|
+
@stats.stats.should be_nil
|
39
|
+
@stats.instance_variable_get(:@callback).should be_nil
|
40
|
+
end
|
39
41
|
end
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
context :reset do
|
44
|
+
it "clears any currently stored exceptions" do
|
45
|
+
@stats.track("testing", @exception)
|
46
|
+
@stats.stats.should_not be_nil
|
47
|
+
@stats.reset
|
48
|
+
@stats.stats.should be_nil
|
49
|
+
end
|
46
50
|
end
|
47
51
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
52
|
+
context :track do
|
53
|
+
it "tracks submitted exception information by category" do
|
54
|
+
@stats.track("testing", @exception)
|
55
|
+
@stats.stats.should == {"testing" => {"total" => 1,
|
56
|
+
"recent" => [{"count" => 1, "type" => "Exception", "message" => "Test error",
|
57
|
+
"when" => @now, "where" => nil}]}}
|
58
|
+
end
|
59
|
+
|
60
|
+
it "tracks where exception occurred if it has a backtrace" do
|
57
61
|
begin
|
58
|
-
raise
|
62
|
+
raise Exception.new("Test error")
|
59
63
|
rescue Exception => e
|
60
|
-
|
61
|
-
@stats.track(category, e)
|
62
|
-
backtrace.shift(2) if i == 1
|
63
|
-
category = "testing" if i == 2
|
64
|
+
@exception = e
|
64
65
|
end
|
66
|
+
@stats.track("testing", @exception)
|
67
|
+
@stats.stats["testing"]["recent"][0]["where"].should =~ /right_support\/spec\/stats\/exceptions_spec.rb/
|
68
|
+
end
|
69
|
+
|
70
|
+
it "recognizes and counts repeated exceptions" do
|
71
|
+
@stats.track("testing", @exception)
|
72
|
+
@stats.stats.should == {"testing" => {"total" => 1,
|
73
|
+
"recent" => [{"count" => 1, "type" => "Exception", "message" => "Test error",
|
74
|
+
"when" => @now, "where" => nil}]}}
|
75
|
+
flexmock(Time).should_receive(:now).and_return(1000010)
|
76
|
+
category = "another"
|
77
|
+
backtrace = ["here", "and", "there"]
|
78
|
+
4.times do |i|
|
79
|
+
begin
|
80
|
+
raise ArgumentError, "badarg"
|
81
|
+
rescue Exception => e
|
82
|
+
flexmock(e).should_receive(:backtrace).and_return(backtrace)
|
83
|
+
@stats.track(category, e)
|
84
|
+
backtrace.shift(2) if i == 1
|
85
|
+
category = "testing" if i == 2
|
86
|
+
end
|
87
|
+
end
|
88
|
+
@stats.stats.should == {"testing" => {"total" => 2,
|
89
|
+
"recent" => [{"count" => 1, "type" => "Exception", "message" => "Test error",
|
90
|
+
"when" => @now, "where" => nil},
|
91
|
+
{"count" => 1, "type" => "ArgumentError", "message" => "badarg",
|
92
|
+
"when" => @now + 10, "where" => "there"}]},
|
93
|
+
"another" => {"total" => 3,
|
94
|
+
"recent" => [{"count" => 2, "type" => "ArgumentError", "message" => "badarg",
|
95
|
+
"when" => @now + 10, "where" => "here"},
|
96
|
+
{"count" => 1, "type" => "ArgumentError", "message" => "badarg",
|
97
|
+
"when" => @now + 10, "where" => "there"}]}}
|
98
|
+
end
|
99
|
+
|
100
|
+
it "limits the number of exceptions stored by eliminating older exceptions" do
|
101
|
+
(RightSupport::Stats::Exceptions::MAX_RECENT_EXCEPTIONS + 1).times do |i|
|
102
|
+
begin
|
103
|
+
raise ArgumentError, "badarg"
|
104
|
+
rescue Exception => e
|
105
|
+
flexmock(e).should_receive(:backtrace).and_return([i.to_s])
|
106
|
+
@stats.track("testing", e)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
stats = @stats.stats
|
110
|
+
stats["testing"]["total"].should == RightSupport::Stats::Exceptions::MAX_RECENT_EXCEPTIONS + 1
|
111
|
+
stats["testing"]["recent"].size.should == RightSupport::Stats::Exceptions::MAX_RECENT_EXCEPTIONS
|
112
|
+
stats["testing"]["recent"][0]["where"].should == "1"
|
113
|
+
end
|
114
|
+
|
115
|
+
it "makes callback if callback and message defined" do
|
116
|
+
called = 0
|
117
|
+
callback = lambda do |exception, message, server|
|
118
|
+
called += 1
|
119
|
+
exception.should == @exception
|
120
|
+
message.should == "message"
|
121
|
+
server.should == "server"
|
122
|
+
end
|
123
|
+
@stats = RightSupport::Stats::Exceptions.new("server", callback)
|
124
|
+
@stats.track("testing", @exception, "message")
|
125
|
+
@stats.stats.should == {"testing" => {"total" => 1,
|
126
|
+
"recent" => [{"count" => 1, "type" => "Exception", "message" => "Test error",
|
127
|
+
"when" => @now, "where" => nil}]}}
|
128
|
+
called.should == 1
|
65
129
|
end
|
66
|
-
@stats.stats.should == {"testing" => {"total" => 2,
|
67
|
-
"recent" => [{"count" => 1, "type" => "Exception", "message" => "Test error",
|
68
|
-
"when" => @now, "where" => nil},
|
69
|
-
{"count" => 1, "type" => "ArgumentError", "message" => "badarg",
|
70
|
-
"when" => @now + 10, "where" => "there"}]},
|
71
|
-
"another" => {"total" => 3,
|
72
|
-
"recent" => [{"count" => 2, "type" => "ArgumentError", "message" => "badarg",
|
73
|
-
"when" => @now + 10, "where" => "here"},
|
74
|
-
{"count" => 1, "type" => "ArgumentError", "message" => "badarg",
|
75
|
-
"when" => @now + 10, "where" => "there"}]}}
|
76
|
-
end
|
77
130
|
|
78
|
-
|
79
|
-
(RightSupport::Stats::Exceptions::MAX_RECENT_EXCEPTIONS + 1).times do |i|
|
131
|
+
it "catches any exceptions raised internally and log them" do
|
80
132
|
begin
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
@
|
133
|
+
logger = flexmock("logger")
|
134
|
+
logger.should_receive(:error).with(/Failed to track exception 'Test error' \(Exception: bad IN/).once
|
135
|
+
RightSupport::Log::Mixin.default_logger = logger
|
136
|
+
flexmock(@exception).should_receive(:backtrace).and_raise(Exception.new("bad"))
|
137
|
+
@stats = RightSupport::Stats::Exceptions.new
|
138
|
+
@stats.track("testing", @exception, "message")
|
139
|
+
@stats.stats["testing"]["total"].should == 1
|
140
|
+
ensure
|
141
|
+
RightSupport::Log::Mixin.default_logger = nil
|
85
142
|
end
|
86
143
|
end
|
87
|
-
stats = @stats.stats
|
88
|
-
stats["testing"]["total"].should == RightSupport::Stats::Exceptions::MAX_RECENT_EXCEPTIONS + 1
|
89
|
-
stats["testing"]["recent"].size.should == RightSupport::Stats::Exceptions::MAX_RECENT_EXCEPTIONS
|
90
|
-
stats["testing"]["recent"][0]["where"].should == "1"
|
91
144
|
end
|
92
145
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
exception.should == @exception
|
98
|
-
message.should == "message"
|
99
|
-
server.should == "server"
|
100
|
-
end
|
101
|
-
@stats = RightSupport::Stats::Exceptions.new("server", callback)
|
102
|
-
@stats.track("testing", @exception, "message")
|
103
|
-
@stats.stats.should == {"testing" => {"total" => 1,
|
146
|
+
context :all do
|
147
|
+
it "returns current stats" do
|
148
|
+
@stats.track("testing", @exception)
|
149
|
+
@stats.all.should == {"testing" => {"total" => 1,
|
104
150
|
"recent" => [{"count" => 1, "type" => "Exception", "message" => "Test error",
|
105
151
|
"when" => @now, "where" => nil}]}}
|
106
|
-
|
152
|
+
end
|
153
|
+
|
154
|
+
it "returns nil if there are no stats" do
|
155
|
+
@stats.all.should be_nil
|
156
|
+
end
|
107
157
|
end
|
108
158
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
159
|
+
describe "class methods" do
|
160
|
+
|
161
|
+
context :all do
|
162
|
+
before(:each) do
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
it "aggregates the stats from multiple all calls" do
|
167
|
+
stats = [{"testing" => {"total" => 3,
|
168
|
+
"recent" => [{"count" => 1, "type" => "Exception", "message" => "Test error",
|
169
|
+
"when" => @now, "where" => nil},
|
170
|
+
{"count" => 1, "type" => "ArgumentError", "message" => "badarg",
|
171
|
+
"when" => @now + 20, "where" => "there"},
|
172
|
+
{"count" => 1, "type" => "ArgumentError", "message" => "badarg",
|
173
|
+
"when" => @now + 30, "where" => "everywhere"}]}},
|
174
|
+
{"testing" => {"total" => 5,
|
175
|
+
"recent" => [{"count" => 2, "type" => "ArgumentError", "message" => "badarg",
|
176
|
+
"when" => @now + 10, "where" => "here"},
|
177
|
+
{"count" => 1, "type" => "ArgumentError", "message" => "badarg",
|
178
|
+
"when" => @now + 15, "where" => "there"},
|
179
|
+
{"count" => 2, "type" => "ArgumentError", "message" => "badarg",
|
180
|
+
"when" => @now + 30, "where" => "everywhere"}]}},
|
181
|
+
{"testing" => {"total" => 1,
|
182
|
+
"recent" => [{"count" => 1, "type" => "ArgumentError", "message" => "awfularg",
|
183
|
+
"when" => @now + 10, "where" => "here"}]}},
|
184
|
+
{"another" => {"total" => 3,
|
185
|
+
"recent" => [{"count" => 2, "type" => "ArgumentError", "message" => "badarg",
|
186
|
+
"when" => @now + 10, "where" => "here"},
|
187
|
+
{"count" => 1, "type" => "ArgumentError", "message" => "badarg",
|
188
|
+
"when" => @now + 10, "where" => "there"}]}}]
|
189
|
+
|
190
|
+
RightSupport::Stats::Exceptions.all(stats).should ==
|
191
|
+
{"testing" => {"total" => 9,
|
192
|
+
"recent" => [{"count" => 1, "type" => "Exception", "message" => "Test error",
|
193
|
+
"when" => @now, "where" => nil},
|
194
|
+
{"count" => 2, "type" => "ArgumentError", "message" => "badarg",
|
195
|
+
"when" => @now + 10, "where" => "here"},
|
196
|
+
{"count" => 1, "type" => "ArgumentError", "message" => "awfularg",
|
197
|
+
"when" => @now + 10, "where" => "here"},
|
198
|
+
{"count" => 1, "type" => "ArgumentError", "message" => "badarg",
|
199
|
+
"when" => @now + 15, "where" => "there"},
|
200
|
+
{"count" => 1, "type" => "ArgumentError", "message" => "badarg",
|
201
|
+
"when" => @now + 20, "where" => "there"},
|
202
|
+
{"count" => 3, "type" => "ArgumentError", "message" => "badarg",
|
203
|
+
"when" => @now + 30, "where" => "everywhere"}]},
|
204
|
+
"another" => {"total" => 3,
|
205
|
+
"recent" => [{"count" => 2, "type" => "ArgumentError", "message" => "badarg",
|
206
|
+
"when" => @now + 10, "where" => "here"},
|
207
|
+
{"count" => 1, "type" => "ArgumentError", "message" => "badarg",
|
208
|
+
"when" => @now + 10, "where" => "there"}]}}
|
209
|
+
end
|
210
|
+
|
211
|
+
it "does not allow max exceptions to exceed set limit" do
|
212
|
+
max = RightSupport::Stats::Exceptions::MAX_RECENT_EXCEPTIONS
|
213
|
+
stats = []
|
214
|
+
((max / 2) + 1).times do |i|
|
215
|
+
stats << {"testing" => {"total" => 1,
|
216
|
+
"recent" => [{"count" => 1, "type" => "Exception", "message" => "Test error",
|
217
|
+
"when" => @now + i, "where" => nil},
|
218
|
+
{"count" => 2, "type" => "ArgumentError", "message" => "badarg",
|
219
|
+
"when" => @now + i, "where" => "here"}]}}
|
220
|
+
end
|
221
|
+
all = RightSupport::Stats::Exceptions.all(stats)
|
222
|
+
all["testing"]["recent"].size.should == max
|
223
|
+
all["testing"]["recent"][max - 1].should == {"count" => 2, "type" => "ArgumentError", "message" => "badarg",
|
224
|
+
"when" => @now + (max / 2), "where" => "here"}
|
225
|
+
end
|
120
226
|
end
|
121
227
|
end
|
122
228
|
|