sqreen 1.20.1-java → 1.20.4-java

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.
Files changed (38) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +25 -0
  3. data/lib/sqreen/actions/block_user.rb +1 -1
  4. data/lib/sqreen/actions/redirect_ip.rb +1 -1
  5. data/lib/sqreen/actions/redirect_user.rb +1 -1
  6. data/lib/sqreen/attack_detected.html +1 -2
  7. data/lib/sqreen/condition_evaluator.rb +8 -2
  8. data/lib/sqreen/configuration.rb +1 -1
  9. data/lib/sqreen/deferred_logger.rb +50 -14
  10. data/lib/sqreen/deprecation.rb +38 -0
  11. data/lib/sqreen/events/request_record.rb +0 -1
  12. data/lib/sqreen/frameworks/generic.rb +9 -0
  13. data/lib/sqreen/frameworks/rails.rb +0 -7
  14. data/lib/sqreen/frameworks/request_recorder.rb +2 -0
  15. data/lib/sqreen/graft/call.rb +76 -18
  16. data/lib/sqreen/graft/callback.rb +1 -1
  17. data/lib/sqreen/graft/hook.rb +187 -85
  18. data/lib/sqreen/graft/hook_point.rb +1 -1
  19. data/lib/sqreen/legacy/instrumentation.rb +22 -10
  20. data/lib/sqreen/legacy/old_event_submission_strategy.rb +2 -1
  21. data/lib/sqreen/log.rb +3 -2
  22. data/lib/sqreen/log/loggable.rb +1 -0
  23. data/lib/sqreen/logger.rb +24 -0
  24. data/lib/sqreen/metrics_store.rb +11 -0
  25. data/lib/sqreen/null_logger.rb +22 -0
  26. data/lib/sqreen/remote_command.rb +1 -0
  27. data/lib/sqreen/rules.rb +8 -4
  28. data/lib/sqreen/rules/blacklist_ips_cb.rb +2 -2
  29. data/lib/sqreen/rules/custom_error_cb.rb +3 -3
  30. data/lib/sqreen/rules/rule_cb.rb +2 -0
  31. data/lib/sqreen/rules/waf_cb.rb +3 -3
  32. data/lib/sqreen/runner.rb +28 -2
  33. data/lib/sqreen/version.rb +1 -1
  34. data/lib/sqreen/weave/budget.rb +46 -0
  35. data/lib/sqreen/weave/legacy/instrumentation.rb +194 -103
  36. data/lib/sqreen/worker.rb +6 -2
  37. metadata +7 -6
  38. data/lib/sqreen/encoding_sanitizer.rb +0 -27
@@ -21,7 +21,7 @@ module Sqreen
21
21
  end
22
22
 
23
23
  def call(*args, &block)
24
- Sqreen::Graft.logger.debug { "[#{Process.pid}] Callback #{@name} disabled:#{disabled?}" }
24
+ # Sqreen::Graft.logger.debug { "[#{Process.pid}] Callback #{@name} disabled:#{disabled?}" } if Sqreen::Graft.logger.debug?
25
25
  return if @disabled
26
26
  @block.call(*args, &block)
27
27
  end
@@ -21,6 +21,13 @@ module Sqreen
21
21
  self[hook_point, strategy].add(&block)
22
22
  end
23
23
 
24
+ def self.ignore
25
+ Thread.current[:sqreen_hook_entered] = true
26
+ yield
27
+ ensure
28
+ Thread.current[:sqreen_hook_entered] = false
29
+ end
30
+
24
31
  attr_reader :point
25
32
 
26
33
  def initialize(hook_point, dependency_test = nil, strategy = :chain)
@@ -46,27 +53,31 @@ module Sqreen
46
53
  end
47
54
 
48
55
  def before(tag = nil, opts = {}, &block)
49
- return @before.sort_by(&:rank) if block.nil?
56
+ return @before if block.nil?
50
57
 
51
58
  @before << Callback.new(callback_name(:before, tag), opts, &block)
59
+ @before.sort_by!(&:rank)
52
60
  end
53
61
 
54
62
  def after(tag = nil, opts = {}, &block)
55
- return @after.sort_by(&:rank) if block.nil?
63
+ return @after if block.nil?
56
64
 
57
65
  @after << Callback.new(callback_name(:after, tag), opts, &block)
66
+ @after.sort_by!(&:rank)
58
67
  end
59
68
 
60
69
  def raised(tag = nil, opts = {}, &block)
61
- return @raised.sort_by(&:rank) if block.nil?
70
+ return @raised if block.nil?
62
71
 
63
72
  @raised << Callback.new(callback_name(:raised, tag), opts, &block)
73
+ @raised.sort_by!(&:rank)
64
74
  end
65
75
 
66
76
  def ensured(tag = nil, opts = {}, &block)
67
- return @ensured.sort_by(&:rank) if block.nil?
77
+ return @ensured if block.nil?
68
78
 
69
79
  @ensured << Callback.new(callback_name(:ensured, tag), opts, &block)
80
+ @ensured.sort_by!(&:rank)
70
81
  end
71
82
 
72
83
  def depends_on(&block)
@@ -109,11 +120,25 @@ module Sqreen
109
120
  @before = []
110
121
  @after = []
111
122
  @raised = []
123
+ @ensured = []
112
124
  end
113
125
 
114
126
  def self.wrapper(hook)
127
+ timed_hooks_proc = proc do |t|
128
+ if (request = Thread.current[:sqreen_http_request])
129
+ request[:timed_hooks] << t if request[:timed_level] >= 1
130
+ end
131
+ end
132
+ timed_callbacks_proc = proc do |t|
133
+ if (request = Thread.current[:sqreen_http_request])
134
+ request[:timed_callbacks] << t if request[:timed_level] >= 1
135
+ end
136
+ end
137
+
115
138
  Proc.new do |*args, &block|
116
- if Thread.current[:sqreen_hook_entered] || Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:time_budget_expended]
139
+ request = Thread.current[:sqreen_http_request]
140
+
141
+ if Thread.current[:sqreen_hook_entered]
117
142
  if hook.point.super?
118
143
  return super(*args, &block)
119
144
  else
@@ -121,49 +146,88 @@ module Sqreen
121
146
  end
122
147
  end
123
148
 
124
- Timer.new(hook.point) do |t|
125
- Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_hooks] << t
126
- end.measure do |chrono|
127
- Sqreen::Graft.logger.debug { "[#{Process.pid}] Hook #{hook.point} disabled:#{hook.disabled?} caller:#{Kernel.caller[2].inspect}" }
149
+ if request && request[:time_budget_expended] && (hook.before + hook.after + hook.raised + hook.ensured).none?(&:mandatory)
150
+ if request[:timed_level] >= 2
151
+ begin
152
+ request[:skipped_callbacks].concat(hook.before)
153
+
154
+ if hook.point.super?
155
+ return super(*args, &block)
156
+ else
157
+ return hook.point.apply(self, 'sqreen_hook', *args, &block)
158
+ end
159
+ rescue ::Exception # rubocop:disable Lint/RescueException
160
+ request[:skipped_callbacks].concat(hook.raised)
161
+ raise
162
+ else
163
+ request[:skipped_callbacks].concat(hook.after)
164
+ ensure
165
+ request[:skipped_callbacks].concat(hook.ensured)
166
+ end
167
+ else
168
+ if hook.point.super? # rubocop:disable Style/IfInsideElse
169
+ return super(*args, &block)
170
+ else
171
+ return hook.point.apply(self, 'sqreen_hook', *args, &block)
172
+ end
173
+ end
174
+ end
175
+
176
+ hook_point_super = hook.point.super?
177
+ logger = Sqreen::Graft.logger
178
+ logger_debug = Sqreen::Graft.logger.debug?
179
+
180
+ Timer.new(hook.point, &timed_hooks_proc).measure do |chrono|
181
+ # budget implies request
182
+ # TODO: make budget depend on a generic context (currently "request")
183
+ budget = request[:time_budget] if request
184
+ if budget
185
+ budget_threshold = request[:time_budget_threshold]
186
+ budget_ratio = request[:time_budget_ratio]
187
+ sqreen_timer = request[:sqreen_timer]
188
+ request_timer = request[:request_timer]
189
+ end
128
190
 
129
- budget = Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:time_budget]
130
- timer = Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timer] if budget
131
191
  hooked_call = HookedCall.new(self, args)
132
192
 
133
193
  begin
134
- timer.start if timer
135
- Thread.current[:sqreen_hook_entered] = true
136
-
137
- # TODO: make Call have #ball to throw by cb
138
- # TODO: can Call be the ball? r = catch(Call.new, &c)
139
- # TODO: is catch return value a Call? a #dispatch?
140
- # TODO: make before/after/raised return a CallbackCollection << Array (or extend with module)
141
- # TODO: add CallbackCollection#each_with_call(instance, args) { |call| ... } ?
142
- # TODO: HookCall x CallbackCollection#each_with_call x Flow
143
- # TODO: TimedHookCall TimedCallbackCall
144
- # TODO: TimeBoundHookCall TimeBoundCallbackCall TimeBoundFlow?
145
-
146
- Timer.new("#{hook.point}@before") do |t|
147
- Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_hooks_before] << t
148
- end.measure do |before_chrono|
194
+ begin
195
+ sqreen_timer.start if budget
196
+ Thread.current[:sqreen_hook_entered] = true
197
+
198
+ # TODO: make Call have #ball to throw by cb
199
+ # TODO: can Call be the ball? r = catch(Call.new, &c)
200
+ # TODO: is catch return value a Call? a #dispatch?
201
+ # TODO: make before/after/raised return a CallbackCollection << Array (or extend with module)
202
+ # TODO: add CallbackCollection#each_with_call(instance, args) { |call| ... } ?
203
+ # TODO: HookCall x CallbackCollection#each_with_call x Flow
204
+ # TODO: TimedHookCall TimedCallbackCall
205
+ # TODO: TimeBoundHookCall TimeBoundCallbackCall TimeBoundFlow?
206
+
207
+ request_elapsed = request_timer.elapsed if budget
208
+
149
209
  hook.before.each do |c|
150
210
  next if c.ignore && c.ignore.call
151
211
 
152
- if timer && !c.mandatory
153
- remaining = budget - timer.elapsed
212
+ if budget && !c.mandatory
213
+ sqreen_elapsed = sqreen_timer.elapsed
214
+ if budget_ratio && !request[:time_budget_expended]
215
+ fixed_budget = budget_threshold
216
+ proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
217
+ remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
218
+ else
219
+ remaining = budget_threshold - sqreen_elapsed
220
+ end
154
221
  unless remaining > 0
155
- Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:skipped_callbacks] << c && Thread.current[:sqreen_http_request][:time_budget_expended] = true
222
+ request[:skipped_callbacks] << c
223
+ request[:time_budget_expended] = true
156
224
  next
157
225
  end
158
226
  end
159
227
 
160
228
  flow = catch(Ball.new) do |ball|
161
- Timer.new(c.name) do |t|
162
- Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_callbacks] << t
163
- end.measure do
164
- before_chrono.ignore do
165
- c.call(CallbackCall.new(c, remaining, hooked_call.instance, hooked_call.args_passed), ball)
166
- end
229
+ Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
230
+ c.call(CallbackCall.new(c, remaining, hooked_call.instance, hooked_call.args_passed), ball)
167
231
  end
168
232
  end
169
233
 
@@ -173,52 +237,59 @@ module Sqreen
173
237
  hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
174
238
  break if flow.break?
175
239
  end unless hook.disabled?
240
+ rescue StandardError => e
241
+ Sqreen::Weave.logger.debug { "exception:#{e.class} message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
242
+ Sqreen::RemoteException.record(e) if Sqreen.queue
176
243
  end
177
244
 
178
245
  raise hooked_call.raise if hooked_call.raising
179
246
  return hooked_call.return if hooked_call.returning
180
247
  ensure
181
248
  Thread.current[:sqreen_hook_entered] = false
182
- timer.stop if timer
183
- end
249
+ sqreen_timer.stop if budget
250
+ end unless hook.before.empty?
184
251
 
185
252
  begin
186
253
  chrono.ignore do
187
- if hook.point.super?
254
+ if hook_point_super
188
255
  hooked_call.returned = super(*(hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed), &block)
189
256
  else
190
257
  hooked_call.returned = hook.point.apply(hooked_call.instance, 'sqreen_hook', *(hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed), &block)
191
258
  end
192
259
  end
193
260
  rescue ::Exception => e # rubocop:disable Lint/RescueException
194
- timer.start if timer
195
- Thread.current[:sqreen_hook_entered] = true
196
- hooked_call.raised = e
261
+ begin
262
+ sqreen_timer.start if budget
263
+ Thread.current[:sqreen_hook_entered] = true
264
+ hooked_call.raised = e
197
265
 
198
- Sqreen::Graft.logger.debug { "[#{Process.pid}] Hook #{hook.point} disabled:#{hook.disabled?} exception:#{e}" }
199
- raise if hook.raised.empty?
266
+ logger.debug { "[#{Process.pid}] Hook #{hook.point} disabled:#{hook.disabled?} exception:#{e}" } if logger_debug
267
+ raise if hook.raised.empty?
268
+
269
+ request_elapsed = request_timer.elapsed if budget
200
270
 
201
- Timer.new("#{hook.point}@raised") do |t|
202
- Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_hooks_raised] << t
203
- end.measure do |raised_chrono|
204
271
  hook.raised.each do |c|
205
272
  next if c.ignore && c.ignore.call
206
273
 
207
- if timer && !c.mandatory
208
- remaining = budget - timer.elapsed
274
+ if budget && !c.mandatory
275
+ sqreen_elapsed = sqreen_timer.elapsed
276
+ if budget_ratio && !request[:time_budget_expended]
277
+ fixed_budget = budget_threshold
278
+ proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
279
+ remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
280
+ else
281
+ remaining = budget_threshold - sqreen_elapsed
282
+ end
209
283
  unless remaining > 0
210
- Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:skipped_callbacks] << c && Thread.current[:sqreen_http_request][:time_budget_expended] = true
284
+ request[:skipped_callbacks] << c
285
+ request[:time_budget_expended] = true
211
286
  next
212
287
  end
213
288
  end
214
289
 
215
290
  flow = catch(Ball.new) do |ball|
216
- Timer.new(c.name) do |t|
217
- Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_callbacks] << t
218
- end.measure do
219
- raised_chrono.ignore do
220
- c.call(CallbackCall.new(c, remaining, hooked_call.instance, hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed, hooked_call.raised), ball)
221
- end
291
+ Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
292
+ c.call(CallbackCall.new(c, remaining, hooked_call.instance, hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed, hooked_call.raised), ball)
222
293
  end
223
294
  end
224
295
 
@@ -228,6 +299,9 @@ module Sqreen
228
299
  hooked_call.retrying = true if flow.retry?
229
300
  break if flow.break?
230
301
  end unless hook.disabled?
302
+ rescue StandardError => e
303
+ Sqreen::Weave.logger.debug { "exception:#{e.class} message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
304
+ Sqreen::RemoteException.record(e) if Sqreen.queue
231
305
  end
232
306
 
233
307
  retry if hooked_call.retrying
@@ -235,30 +309,37 @@ module Sqreen
235
309
  return hooked_call.return if hooked_call.returning
236
310
  raise
237
311
  else
238
- timer.start if timer
239
- Thread.current[:sqreen_hook_entered] = true
312
+ begin
313
+ sqreen_timer.start if budget
314
+ Thread.current[:sqreen_hook_entered] = true
315
+
316
+ # TODO: hooked_call.returning should be always false here?
317
+ return hooked_call.returning ? hooked_call.return : hooked_call.returned if hook.after.empty?
318
+
319
+ request_elapsed = request_timer.elapsed if budget
240
320
 
241
- Timer.new("#{hook.point}@after") do |t|
242
- Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_hooks_after] << t
243
- end.measure do |after_chrono|
244
321
  hook.after.each do |c|
245
322
  next if c.ignore && c.ignore.call
246
323
 
247
- if timer && !c.mandatory
248
- remaining = budget - timer.elapsed
324
+ if budget && !c.mandatory
325
+ sqreen_elapsed = sqreen_timer.elapsed
326
+ if budget_ratio && !request[:time_budget_expended]
327
+ fixed_budget = budget_threshold
328
+ proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
329
+ remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
330
+ else
331
+ remaining = budget_threshold - sqreen_elapsed
332
+ end
249
333
  unless remaining > 0
250
- Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:skipped_callbacks] << c && Thread.current[:sqreen_http_request][:time_budget_expended] = true
334
+ request[:skipped_callbacks] << c
335
+ request[:time_budget_expended] = true
251
336
  next
252
337
  end
253
338
  end
254
339
 
255
340
  flow = catch(Ball.new) do |ball|
256
- Timer.new(c.name) do |t|
257
- Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_callbacks] << t
258
- end.measure do
259
- after_chrono.ignore do
260
- c.call(CallbackCall.new(c, remaining, hooked_call.instance, hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed, nil, hooked_call.returned), ball)
261
- end
341
+ Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
342
+ c.call(CallbackCall.new(c, remaining, hooked_call.instance, hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed, nil, hooked_call.returned), ball)
262
343
  end
263
344
  end
264
345
 
@@ -267,34 +348,48 @@ module Sqreen
267
348
  hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
268
349
  break if flow.break?
269
350
  end unless hook.disabled?
351
+ rescue StandardError => e
352
+ Sqreen::Weave.logger.debug { "exception:#{e.class} message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
353
+ Sqreen::RemoteException.record(e) if Sqreen.queue
270
354
  end
271
355
 
272
356
  raise hooked_call.raise if hooked_call.raising
273
357
  return hooked_call.returning ? hooked_call.return : hooked_call.returned
274
358
  ensure
275
- # TODO: timer.start if someone has thrown?
359
+ begin
360
+ # TODO: sqreen_timer.start if someone has thrown?
361
+ # TODO: sqreen_timer.stop at end of rescue+else?
362
+ # TODO: Thread.current[:sqreen_hook_entered] = true if neither rescue nor else ie thrown?
363
+ # TODO: Thread.current[:sqreen_hook_entered] = false at end of rescue+else? (risky?)
364
+
365
+ # TODO: uniform early bail out? (but don't forget sqreen_hook_entered and sqreen_timer)
366
+ # return hooked_call.returning ? hooked_call.return : hooked_call.returned if hook.ensured.empty?
367
+
368
+ # done at either rescue or else
369
+ # request_elapsed = request_timer.elapsed if budget # && !hook.ensured.empty?
276
370
 
277
- Timer.new("#{hook.point}@ensured") do |t|
278
- Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_hooks_ensured] << t
279
- end.measure do |ensured_chrono|
280
371
  hook.ensured.each do |c|
281
372
  next if c.ignore && c.ignore.call
282
373
 
283
- if timer && !c.mandatory
284
- remaining = budget - timer.elapsed
374
+ if budget && !c.mandatory
375
+ sqreen_elapsed = sqreen_timer.elapsed
376
+ if budget_ratio && !request[:time_budget_expended]
377
+ fixed_budget = budget_threshold
378
+ proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
379
+ remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
380
+ else
381
+ remaining = budget_threshold - sqreen_elapsed
382
+ end
285
383
  unless remaining > 0
286
- Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:skipped_callbacks] << c && Thread.current[:sqreen_http_request][:time_budget_expended] = true
384
+ request[:skipped_callbacks] << c
385
+ request[:time_budget_expended] = true
287
386
  next
288
387
  end
289
388
  end
290
389
 
291
390
  flow = catch(Ball.new) do |ball|
292
- Timer.new(c.name) do |t|
293
- Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_callbacks] << t
294
- end.measure do
295
- ensured_chrono.ignore do
296
- c.call(CallbackCall.new(c, remaining, hooked_call.instance, hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed, nil, hooked_call.returned), ball)
297
- end
391
+ Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
392
+ c.call(CallbackCall.new(c, remaining, hooked_call.instance, hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed, nil, hooked_call.returned), ball)
298
393
  end
299
394
  end
300
395
 
@@ -302,11 +397,18 @@ module Sqreen
302
397
  hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
303
398
  hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
304
399
  break if flow.break?
305
- end unless hook.disabled?
400
+ end unless hook.ensured.empty? || hook.disabled?
401
+
402
+ Thread.current[:sqreen_hook_entered] = false
403
+ sqreen_timer.stop if budget
404
+ rescue StandardError => e
405
+ Sqreen::Weave.logger.debug { "exception:#{e.class} message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
406
+ Sqreen::RemoteException.record(e) if Sqreen.queue
306
407
  end
307
408
 
308
- Thread.current[:sqreen_hook_entered] = false
309
- timer.stop if timer
409
+ # TODO: should we run the following?
410
+ # raise hooked_call.raise if hooked_call.raising
411
+ # return hooked_call.returning ? hooked_call.return : hooked_call.returned
310
412
  end
311
413
  end # chrono
312
414
  end
@@ -42,7 +42,7 @@ module Sqreen
42
42
  end
43
43
 
44
44
  def to_s
45
- "#{@klass_name}#{@method_kind == :instance_method ? '#' : '.'}#{@method_name}"
45
+ @to_s ||= "#{@klass_name}#{@method_kind == :instance_method ? '#' : '.'}#{@method_name}"
46
46
  end
47
47
 
48
48
  def exist?
@@ -109,7 +109,7 @@ module Legacy
109
109
  break if res.is_a?(Hash) && res[:skip_rem_cbs]
110
110
  rescue StandardError => e
111
111
  Sqreen.log.warn { "we catch an exception: #{e.inspect}" }
112
- Sqreen.log.debug e.backtrace
112
+ Sqreen.log.debug { e.backtrace }
113
113
  if cb.respond_to?(:record_exception)
114
114
  cb.record_exception(e)
115
115
  else
@@ -162,7 +162,7 @@ module Legacy
162
162
  returns << res
163
163
  rescue StandardError => e
164
164
  Sqreen.log.warn { "we catch an exception: #{e.inspect}" }
165
- Sqreen.log.debug e.backtrace
165
+ Sqreen.log.debug { e.backtrace }
166
166
  if cb.respond_to?(:record_exception)
167
167
  cb.record_exception(e)
168
168
  else
@@ -597,16 +597,25 @@ module Legacy
597
597
  method = cb.method
598
598
  key = [klass, method]
599
599
 
600
+ if (call_count = ENV['SQREEN_DEBUG_CALL_COUNT'])
601
+ call_count = JSON.parse(call_count)
602
+ if cb.respond_to?(:rule_name) && call_count.key?(cb.rule_name)
603
+ count = call_count[cb.rule_name]
604
+ Sqreen.log.debug { "override rule:#{cb.rule_name} call_count:#{count.inspect}" }
605
+ cb.instance_eval { @call_count_interval = call_count[cb.rule_name] }
606
+ end
607
+ end
608
+
600
609
  @@record_request_hookpoints << key if cb.is_a?(Sqreen::Rules::RecordRequestContext)
601
610
 
602
611
  already_overriden = @@overriden_methods.include? key
603
612
 
604
613
  if !already_overriden
605
614
  if is_class_method?(klass, method)
606
- Sqreen.log.debug "overriding class method for #{cb}"
615
+ Sqreen.log.debug { "overriding class method for #{cb}" }
607
616
  success = override_class_method(klass, method)
608
617
  elsif is_instance_method?(klass, method)
609
- Sqreen.log.debug "overriding instance method for #{cb}"
618
+ Sqreen.log.debug { "overriding instance method for #{cb}" }
610
619
  success = override_instance_method(klass, method)
611
620
  else
612
621
  # FIXME: Override define_method and other dynamic ways to
@@ -623,7 +632,7 @@ module Legacy
623
632
 
624
633
  @@overriden_methods += [key] if success
625
634
  else
626
- Sqreen.log.debug "#{key} was already overriden"
635
+ Sqreen.log.debug { "#{key} was already overriden" }
627
636
  end
628
637
 
629
638
  if klass != Object && klass != Kernel && !Sqreen.features['instrument_all_instances'] && !defined?(::JRUBY_VERSION)
@@ -638,7 +647,7 @@ module Legacy
638
647
  end
639
648
  end
640
649
 
641
- Sqreen.log.debug "Adding callback #{cb} for #{klass} #{method}"
650
+ Sqreen.log.debug { "Adding callback #{cb} for #{klass} #{method}" }
642
651
  @@registered_callbacks.add(cb)
643
652
  @@unovertimable_hookpoints << key unless cb.overtimeable
644
653
  @@instrumented_pid = Process.pid
@@ -659,7 +668,7 @@ module Legacy
659
668
 
660
669
  already_overriden = @@overriden_methods.include? key
661
670
  unless already_overriden
662
- Sqreen.log.debug "#{key} apparently not overridden"
671
+ Sqreen.log.debug { "#{key} apparently not overridden" }
663
672
  end
664
673
 
665
674
  defined_cbs = @@registered_callbacks.get(klass, method).flatten
@@ -667,17 +676,17 @@ module Legacy
667
676
  nb_removed = 0
668
677
  defined_cbs.each do |found_cb|
669
678
  if found_cb == cb
670
- Sqreen.log.debug "Removing callback #{found_cb}"
679
+ Sqreen.log.debug { "Removing callback #{found_cb}" }
671
680
  @@registered_callbacks.remove(found_cb)
672
681
  nb_removed += 1
673
682
  else
674
- Sqreen.log.debug "Not removing callback #{found_cb} (remains #{defined_cbs.size} cbs)"
683
+ Sqreen.log.debug { "Not removing callback #{found_cb} (remains #{defined_cbs.size} cbs)" }
675
684
  end
676
685
  end
677
686
 
678
687
  return unless nb_removed == defined_cbs.size
679
688
 
680
- Sqreen.log.debug "Removing overriden method #{key}"
689
+ Sqreen.log.debug { "Removing overriden method #{key}" }
681
690
  @@overriden_methods.delete(key)
682
691
 
683
692
  if is_class_method?(klass, method)
@@ -705,6 +714,7 @@ module Legacy
705
714
  remove_callback_no_lock(cb)
706
715
  end
707
716
  Sqreen.instrumentation_ready = false
717
+ Sqreen.log.info('Instrumentation deactivated')
708
718
  end
709
719
  end
710
720
 
@@ -757,6 +767,8 @@ module Legacy
757
767
  ### globally declare instrumentation ready
758
768
  ### from within instance method? not even thread local?
759
769
  Sqreen.instrumentation_ready = true
770
+
771
+ Sqreen.log.info('Instrumentation activated')
760
772
  end
761
773
 
762
774
  def initialize(metrics_engine = nil)