sqreen 1.21.0.beta2 → 1.21.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -7
  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/condition_evaluator.rb +9 -2
  7. data/lib/sqreen/conditionable.rb +24 -6
  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/ecosystem_integration.rb +7 -1
  12. data/lib/sqreen/ecosystem_integration/around_callbacks.rb +20 -10
  13. data/lib/sqreen/ecosystem_integration/instrumentation_service.rb +8 -4
  14. data/lib/sqreen/events/request_record.rb +0 -1
  15. data/lib/sqreen/frameworks/generic.rb +9 -0
  16. data/lib/sqreen/frameworks/rails.rb +0 -7
  17. data/lib/sqreen/frameworks/request_recorder.rb +2 -0
  18. data/lib/sqreen/graft/call.rb +99 -21
  19. data/lib/sqreen/graft/callback.rb +1 -1
  20. data/lib/sqreen/graft/hook.rb +212 -100
  21. data/lib/sqreen/graft/hook_point.rb +18 -11
  22. data/lib/sqreen/legacy/instrumentation.rb +22 -10
  23. data/lib/sqreen/legacy/old_event_submission_strategy.rb +2 -1
  24. data/lib/sqreen/log.rb +3 -2
  25. data/lib/sqreen/log/loggable.rb +1 -0
  26. data/lib/sqreen/logger.rb +24 -0
  27. data/lib/sqreen/metrics.rb +1 -0
  28. data/lib/sqreen/metrics/req_detailed.rb +41 -0
  29. data/lib/sqreen/metrics_store.rb +11 -0
  30. data/lib/sqreen/null_logger.rb +22 -0
  31. data/lib/sqreen/remote_command.rb +1 -0
  32. data/lib/sqreen/rules.rb +8 -4
  33. data/lib/sqreen/rules/blacklist_ips_cb.rb +2 -2
  34. data/lib/sqreen/rules/custom_error_cb.rb +3 -3
  35. data/lib/sqreen/rules/rule_cb.rb +4 -2
  36. data/lib/sqreen/rules/waf_cb.rb +3 -3
  37. data/lib/sqreen/runner.rb +46 -5
  38. data/lib/sqreen/version.rb +1 -1
  39. data/lib/sqreen/weave/budget.rb +35 -0
  40. data/lib/sqreen/weave/legacy/instrumentation.rb +274 -132
  41. data/lib/sqreen/worker.rb +6 -2
  42. metadata +22 -6
  43. data/lib/sqreen/encoding_sanitizer.rb +0 -27
@@ -103,13 +103,6 @@ module Sqreen
103
103
  run_in_test = sqreen_configuration.get(:run_in_test)
104
104
  return :rails_test if !run_in_test && (Rails.env.test? || Rails.env.cucumber?)
105
105
 
106
- # SQREEN-880 - prevent Sqreen startup on Sidekiq workers
107
- return :sidekiq_cli if defined?(Sidekiq::CLI)
108
- return :delayed_job if defined?(Delayed::Command)
109
-
110
- # Prevent Sqreen startup on rake tasks - unless this is a Sqreen test
111
- return :rake if !run_in_test && $0.end_with?('rake')
112
-
113
106
  return nil unless defined?(Rails::CommandsTasks)
114
107
  return nil if defined?(Rails::Server)
115
108
  return :rails_console if defined?(Rails::Console)
@@ -69,6 +69,8 @@ module Sqreen
69
69
 
70
70
  # signals require request section to be present
71
71
  payload_requests << 'request'
72
+ # for signals, response is optional, but the backend team wants them
73
+ payload_requests << 'response'
72
74
  payload = payload_creator.payload(payload_requests)
73
75
  payload[:observed] = observed_items
74
76
 
@@ -27,6 +27,10 @@ module Sqreen
27
27
  def raise(value)
28
28
  Flow.raise(value)
29
29
  end
30
+
31
+ def noop
32
+ Flow.noop
33
+ end
30
34
  end
31
35
 
32
36
  class Flow
@@ -46,12 +50,17 @@ module Sqreen
46
50
  def raise(value)
47
51
  new(:raise, value)
48
52
  end
53
+
54
+ def noop
55
+ new(:noop, nil)
56
+ end
49
57
  end
50
58
 
51
59
  def initialize(action, value, brk = false)
52
60
  @action = action
53
61
  @value = value
54
62
  @break = brk
63
+ @passed_conditions = false
55
64
  end
56
65
 
57
66
  def return?
@@ -91,51 +100,119 @@ module Sqreen
91
100
  def break?
92
101
  @break ? true : false
93
102
  end
103
+
104
+ def passed_conditions!
105
+ @passed_conditions = true
106
+
107
+ self
108
+ end
109
+
110
+ def passed_conditions?
111
+ @passed_conditions
112
+ end
94
113
  end
95
114
 
115
+ class TimerError < StandardError; end
116
+
96
117
  class Timer
97
118
  def self.read
98
119
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
99
120
  end
100
121
 
101
- attr_reader :tag
122
+ attr_reader :tag, :size
123
+ attr_accessor :conditions_passed
102
124
 
103
125
  def initialize(tag, &block)
104
126
  @tag = tag
105
- @blips = []
106
127
  @block = block
128
+ @tally = 0
129
+ @size = 0
107
130
  end
108
131
 
109
- def duration
110
- @blips.each_with_index.reduce(0) { |a, (e, i)| i.even? ? a - e : a + e }
132
+ def elapsed
133
+ raise(TimerError, 'Timer#elapsed when paused') if @size.even?
134
+
135
+ @tally + Timer.read
111
136
  end
112
137
 
113
- def elapsed
114
- @blips.each_with_index.reduce(0) { |a, (e, i)| i.even? ? a - e : a + e } + Timer.read
138
+ def duration
139
+ raise(TimerError, 'Timer#duration when running') if @size.odd?
140
+
141
+ @tally
115
142
  end
116
143
 
117
144
  def ignore
118
- @blips << Timer.read
145
+ raise(TimerError, 'Timer#ignore when paused') if @size.even?
146
+
147
+ @size += 1
148
+ @tally += Timer.read
119
149
  yield(self)
120
150
  ensure
121
- @blips << Timer.read
151
+ @size += 1
152
+ @tally -= Timer.read
122
153
  end
123
154
 
124
- def measure
125
- @blips << Timer.read
155
+ def measure(opts = nil)
156
+ raise(TimerError, 'Timer#measure when running') if @size.odd?
157
+
158
+ now = Timer.read
159
+
160
+ ignore = opts[:ignore] if opts
161
+ if ignore
162
+ ignore.size += 1
163
+ ignore.tally += now
164
+ end
165
+
166
+ @size += 1
167
+ @tally -= now
168
+
126
169
  yield(self)
127
170
  ensure
128
- @blips << Timer.read
171
+ now = Timer.read
172
+
173
+ if ignore
174
+ ignore.size += 1
175
+ ignore.tally -= now
176
+ end
177
+
178
+ @size += 1
179
+ @tally += now
180
+
129
181
  @block.call(self) if @block
130
- Sqreen::Graft.logger.debug { "#{@tag}: time=%.03fus" % (duration * 1_000_000) }
131
182
  end
132
183
 
133
- def start
134
- @blips << Timer.read
184
+ def start(at = Timer.read)
185
+ raise(TimerError, 'Timer#start when started') unless @size.even?
186
+
187
+ @size += 1
188
+ @tally -= at
189
+
190
+ at
191
+ end
192
+
193
+ def stop(at = Timer.read)
194
+ raise(TimerError, 'Timer#stop when unstarted') unless @size.odd?
195
+
196
+ @size += 1
197
+ @tally += at
198
+
199
+ at
135
200
  end
136
201
 
137
- def stop
138
- @blips << Timer.read
202
+ def started?
203
+ @size != 0 && @size.odd?
204
+ end
205
+
206
+ def stopped?
207
+ @size != 0 && @size.even?
208
+ end
209
+
210
+ def running?
211
+ @size.odd?
212
+ end
213
+
214
+ def paused?
215
+ @size.even?
139
216
  end
140
217
 
141
218
  def include_measurements(another_timer)
@@ -147,13 +224,14 @@ module Sqreen
147
224
  @blips
148
225
  end
149
226
 
150
- def size
151
- @blips.size
152
- end
153
-
154
227
  def to_s
155
- "#{@tag}: time=%.03fus" % (duration * 1_000_000)
228
+ "#{@tag}: time=%.03fus" % (@tally * 1_000_000)
156
229
  end
230
+
231
+ protected
232
+
233
+ attr_reader :tally
234
+ attr_writer :size, :tally
157
235
  end
158
236
  end
159
237
  end
@@ -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
@@ -7,23 +7,34 @@ require 'sqreen/graft'
7
7
  require 'sqreen/graft/call'
8
8
  require 'sqreen/graft/callback'
9
9
  require 'sqreen/graft/hook_point'
10
+ require 'sqreen/weave'
11
+ require 'sqreen/runner' # Sqreen.queue
10
12
 
11
13
  module Sqreen
12
14
  module Graft
13
15
  class Hook
16
+ DEFAULT_STRATEGY = Sqreen::Graft::HookPoint::DEFAULT_STRATEGY
17
+
14
18
  @hooks = {}
15
19
 
16
- def self.[](hook_point, strategy = :chain)
20
+ def self.[](hook_point, strategy = DEFAULT_STRATEGY)
17
21
  @hooks[hook_point] ||= new(hook_point, nil, strategy)
18
22
  end
19
23
 
20
- def self.add(hook_point, strategy = :chain, &block)
24
+ def self.add(hook_point, strategy = DEFAULT_STRATEGY, &block)
21
25
  self[hook_point, strategy].add(&block)
22
26
  end
23
27
 
28
+ def self.ignore
29
+ Thread.current[:sqreen_hook_entered] = true
30
+ yield
31
+ ensure
32
+ Thread.current[:sqreen_hook_entered] = false
33
+ end
34
+
24
35
  attr_reader :point
25
36
 
26
- def initialize(hook_point, dependency_test = nil, strategy = :chain)
37
+ def initialize(hook_point, dependency_test = nil, strategy = DEFAULT_STRATEGY)
27
38
  @disabled = false
28
39
  @point = hook_point.is_a?(HookPoint) ? hook_point : HookPoint.new(hook_point, strategy)
29
40
  @before = []
@@ -46,27 +57,31 @@ module Sqreen
46
57
  end
47
58
 
48
59
  def before(tag = nil, opts = {}, &block)
49
- return @before.sort_by(&:rank) if block.nil?
60
+ return @before if block.nil?
50
61
 
51
62
  @before << Callback.new(callback_name(:before, tag), opts, &block)
63
+ @before.sort_by!(&:rank)
52
64
  end
53
65
 
54
66
  def after(tag = nil, opts = {}, &block)
55
- return @after.sort_by(&:rank) if block.nil?
67
+ return @after if block.nil?
56
68
 
57
69
  @after << Callback.new(callback_name(:after, tag), opts, &block)
70
+ @after.sort_by!(&:rank)
58
71
  end
59
72
 
60
73
  def raised(tag = nil, opts = {}, &block)
61
- return @raised.sort_by(&:rank) if block.nil?
74
+ return @raised if block.nil?
62
75
 
63
76
  @raised << Callback.new(callback_name(:raised, tag), opts, &block)
77
+ @raised.sort_by!(&:rank)
64
78
  end
65
79
 
66
80
  def ensured(tag = nil, opts = {}, &block)
67
- return @ensured.sort_by(&:rank) if block.nil?
81
+ return @ensured if block.nil?
68
82
 
69
83
  @ensured << Callback.new(callback_name(:ensured, tag), opts, &block)
84
+ @ensured.sort_by!(&:rank)
70
85
  end
71
86
 
72
87
  def depends_on(&block)
@@ -109,11 +124,32 @@ module Sqreen
109
124
  @before = []
110
125
  @after = []
111
126
  @raised = []
127
+ @ensured = []
112
128
  end
113
129
 
114
130
  def self.wrapper(hook)
131
+ timed_hooks_proc = proc do |t|
132
+ if (request = Thread.current[:sqreen_http_request])
133
+ request[:timed_hooks] << t if request[:timed_level] >= 1
134
+ end
135
+ end
136
+ timed_callbacks_proc = proc do |t|
137
+ if (request = Thread.current[:sqreen_http_request])
138
+ request[:timed_callbacks] << t if request[:timed_level] >= 1
139
+ end
140
+ end
141
+ # very hacky, but the non-local control flow with throw-catch
142
+ # makes the solution non-obvious. needs to be revisited
143
+ conditions_passed_proc = proc do
144
+ if (request = Thread.current[:sqreen_http_request])
145
+ request[:timed_callbacks].last.conditions_passed = true
146
+ end
147
+ end
148
+
115
149
  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]
150
+ request = Thread.current[:sqreen_http_request]
151
+
152
+ if Thread.current[:sqreen_hook_entered]
117
153
  if hook.point.super?
118
154
  return super(*args, &block)
119
155
  else
@@ -121,113 +157,159 @@ module Sqreen
121
157
  end
122
158
  end
123
159
 
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}" }
160
+ if request && request[:time_budget_expended] && (hook.before + hook.after + hook.raised + hook.ensured).none?(&:mandatory)
161
+ if request[:timed_level] >= 2
162
+ begin
163
+ request[:skipped_callbacks].concat(hook.before)
164
+
165
+ if hook.point.super?
166
+ return super(*args, &block)
167
+ else
168
+ return hook.point.apply(self, 'sqreen_hook', *args, &block)
169
+ end
170
+ rescue ::Exception # rubocop:disable Lint/RescueException
171
+ request[:skipped_callbacks].concat(hook.raised)
172
+ raise
173
+ else
174
+ request[:skipped_callbacks].concat(hook.after)
175
+ ensure
176
+ request[:skipped_callbacks].concat(hook.ensured)
177
+ end
178
+ else
179
+ if hook.point.super? # rubocop:disable Style/IfInsideElse
180
+ return super(*args, &block)
181
+ else
182
+ return hook.point.apply(self, 'sqreen_hook', *args, &block)
183
+ end
184
+ end
185
+ end
186
+
187
+ hook_point_super = hook.point.super?
188
+ logger = Sqreen::Graft.logger
189
+ logger_debug = Sqreen::Graft.logger.debug?
190
+
191
+ Timer.new(hook.point, &timed_hooks_proc).measure do |chrono|
192
+ # budget implies request
193
+ # TODO: make budget depend on a generic context (currently "request")
194
+ budget = request[:time_budget] if request
195
+ if request && (budget || request[:timed_level] >= 1)
196
+ sqreen_timer = request[:sqreen_timer]
197
+ end
128
198
 
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
199
  hooked_call = HookedCall.new(self, args)
132
200
 
133
201
  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|
202
+ begin
203
+ sqreen_timer.start if sqreen_timer
204
+ Thread.current[:sqreen_hook_entered] = true
205
+
206
+ # TODO: make Call have #ball to throw by cb
207
+ # TODO: can Call be the ball? r = catch(Call.new, &c)
208
+ # TODO: is catch return value a Call? a #dispatch?
209
+ # TODO: make before/after/raised return a CallbackCollection << Array (or extend with module)
210
+ # TODO: add CallbackCollection#each_with_call(instance, args) { |call| ... } ?
211
+ # TODO: HookCall x CallbackCollection#each_with_call x Flow
212
+ # TODO: TimedHookCall TimedCallbackCall
213
+ # TODO: TimeBoundHookCall TimeBoundCallbackCall TimeBoundFlow?
214
+
149
215
  hook.before.each do |c|
150
216
  next if c.ignore && c.ignore.call
151
217
 
152
- if timer && !c.mandatory
153
- remaining = budget - timer.elapsed
154
- 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
156
- next
157
- end
218
+ if budget && !c.mandatory && request[:time_budget_expended]
219
+ request[:skipped_callbacks] << c
220
+ next
158
221
  end
159
222
 
223
+ remaining = budget - sqreen_timer.elapsed if budget
224
+
225
+ timer = nil
160
226
  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
227
+ timer = Timer.new(c.name, &timed_callbacks_proc)
228
+ timer.measure(ignore: chrono) do
229
+ c.call(CallbackCall.new(c, remaining, hooked_call.instance, hooked_call.args_passed), ball)
230
+ end
231
+ end
232
+
233
+ if budget && timer
234
+ remaining -= timer.duration
235
+ if remaining < 0.0
236
+ request[:time_budget_expended]
237
+ request[:overtime_cb] = c.name
167
238
  end
168
239
  end
169
240
 
170
241
  next unless c.flow && flow.is_a?(Flow)
242
+ conditions_passed_proc[] if flow.passed_conditions?
171
243
  hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
172
244
  hooked_call.args_pass = flow.args and hooked_call.args_passing = true if flow.args?
173
245
  hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
174
246
  break if flow.break?
175
247
  end unless hook.disabled?
248
+ rescue StandardError => e
249
+ Sqreen::Weave.logger.debug { "exception:#{e.class} message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
250
+ Sqreen::RemoteException.record(e) if Sqreen.queue
176
251
  end
177
252
 
178
253
  raise hooked_call.raise if hooked_call.raising
179
254
  return hooked_call.return if hooked_call.returning
180
255
  ensure
181
256
  Thread.current[:sqreen_hook_entered] = false
182
- timer.stop if timer
183
- end
257
+ sqreen_timer.stop if sqreen_timer
258
+ end unless hook.before.empty?
184
259
 
185
260
  begin
186
261
  chrono.ignore do
187
- if hook.point.super?
262
+ if hook_point_super
188
263
  hooked_call.returned = super(*(hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed), &block)
189
264
  else
190
265
  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
266
  end
192
267
  end
193
268
  rescue ::Exception => e # rubocop:disable Lint/RescueException
194
- timer.start if timer
195
- Thread.current[:sqreen_hook_entered] = true
196
- hooked_call.raised = e
269
+ begin
270
+ sqreen_timer.start if sqreen_timer
271
+ Thread.current[:sqreen_hook_entered] = true
272
+ hooked_call.raised = e
197
273
 
198
- Sqreen::Graft.logger.debug { "[#{Process.pid}] Hook #{hook.point} disabled:#{hook.disabled?} exception:#{e}" }
199
- raise if hook.raised.empty?
274
+ logger.debug { "[#{Process.pid}] Hook #{hook.point} disabled:#{hook.disabled?} exception:#{e}" } if logger_debug
275
+ raise if hook.raised.empty?
200
276
 
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
277
  hook.raised.each do |c|
205
278
  next if c.ignore && c.ignore.call
206
279
 
207
- if timer && !c.mandatory
208
- remaining = budget - timer.elapsed
209
- 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
211
- next
212
- end
280
+ if budget && !c.mandatory && request[:time_budget_expended]
281
+ request[:skipped_callbacks] << c
282
+ next
213
283
  end
214
284
 
285
+ remaining = budget - sqreen_timer.elapsed if budget
286
+
287
+ timer = nil
215
288
  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
289
+ timer = Timer.new(c.name, &timed_callbacks_proc)
290
+ timer.measure(ignore: chrono) do
291
+ 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)
292
+ end
293
+ end
294
+
295
+ if budget && timer
296
+ remaining -= timer.duration
297
+ if remaining < 0.0
298
+ request[:time_budget_expended]
299
+ request[:overtime_cb] = c.name
222
300
  end
223
301
  end
224
302
 
225
303
  next unless c.flow && flow.is_a?(Flow)
304
+ conditions_passed_proc[] if flow.passed_conditions?
226
305
  hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
227
306
  hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
228
307
  hooked_call.retrying = true if flow.retry?
229
308
  break if flow.break?
230
309
  end unless hook.disabled?
310
+ rescue StandardError => e
311
+ Sqreen::Weave.logger.debug { "exception:#{e.class} message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
312
+ Sqreen::RemoteException.record(e) if Sqreen.queue
231
313
  end
232
314
 
233
315
  retry if hooked_call.retrying
@@ -235,78 +317,108 @@ module Sqreen
235
317
  return hooked_call.return if hooked_call.returning
236
318
  raise
237
319
  else
238
- timer.start if timer
239
- Thread.current[:sqreen_hook_entered] = true
320
+ begin
321
+ sqreen_timer.start if sqreen_timer
322
+ Thread.current[:sqreen_hook_entered] = true
323
+
324
+ # TODO: hooked_call.returning should be always false here?
325
+ return hooked_call.returning ? hooked_call.return : hooked_call.returned if hook.after.empty?
240
326
 
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
327
  hook.after.each do |c|
245
328
  next if c.ignore && c.ignore.call
246
329
 
247
- if timer && !c.mandatory
248
- remaining = budget - timer.elapsed
249
- 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
251
- next
252
- end
330
+ if budget && !c.mandatory && request[:time_budget_expended]
331
+ request[:skipped_callbacks] << c
332
+ next
253
333
  end
254
334
 
335
+ remaining = budget - sqreen_timer.elapsed if budget
336
+
337
+ timer = nil
255
338
  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
339
+ timer = Timer.new(c.name, &timed_callbacks_proc)
340
+ timer.measure(ignore: chrono) do
341
+ 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)
342
+ end
343
+ end
344
+
345
+ if budget && timer
346
+ remaining -= timer.duration
347
+ if remaining < 0.0
348
+ request[:time_budget_expended]
349
+ request[:overtime_cb] = c.name
262
350
  end
263
351
  end
264
352
 
265
353
  next unless c.flow && flow.is_a?(Flow)
354
+ conditions_passed_proc[] if flow.passed_conditions?
266
355
  hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
267
356
  hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
268
357
  break if flow.break?
269
358
  end unless hook.disabled?
359
+ rescue StandardError => e
360
+ Sqreen::Weave.logger.debug { "exception:#{e.class} message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
361
+ Sqreen::RemoteException.record(e) if Sqreen.queue
270
362
  end
271
363
 
272
364
  raise hooked_call.raise if hooked_call.raising
273
365
  return hooked_call.returning ? hooked_call.return : hooked_call.returned
274
366
  ensure
275
- # TODO: timer.start if someone has thrown?
367
+ begin
368
+ # TODO: sqreen_timer.start if someone has thrown?
369
+ # TODO: sqreen_timer.stop at end of rescue+else?
370
+ # TODO: Thread.current[:sqreen_hook_entered] = true if neither rescue nor else ie thrown?
371
+ # TODO: Thread.current[:sqreen_hook_entered] = false at end of rescue+else? (risky?)
372
+
373
+ # TODO: uniform early bail out? (but don't forget sqreen_hook_entered and sqreen_timer)
374
+ # return hooked_call.returning ? hooked_call.return : hooked_call.returned if hook.ensured.empty?
375
+
376
+ # done at either rescue or else
377
+ # request_elapsed = request_timer.elapsed if budget # && !hook.ensured.empty?
276
378
 
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
379
  hook.ensured.each do |c|
281
380
  next if c.ignore && c.ignore.call
282
381
 
283
- if timer && !c.mandatory
284
- remaining = budget - timer.elapsed
285
- 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
287
- next
288
- end
382
+ if budget && !c.mandatory && request[:time_budget_expended]
383
+ request[:skipped_callbacks] << c
384
+ next
289
385
  end
290
386
 
387
+ remaining = budget - sqreen_timer.elapsed if budget
388
+
389
+ timer = nil
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 = Timer.new(c.name, &timed_callbacks_proc)
392
+ timer.measure(ignore: chrono) do
393
+ 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)
394
+ end
395
+ end
396
+
397
+ if budget && timer
398
+ remaining -= timer.duration
399
+ if remaining < 0.0
400
+ request[:time_budget_expended]
401
+ request[:overtime_cb] = c.name
298
402
  end
299
403
  end
300
404
 
301
405
  next unless c.flow && flow.is_a?(Flow)
406
+ conditions_passed_proc[] if flow.passed_conditions?
302
407
  hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
303
408
  hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
304
409
  break if flow.break?
305
- end unless hook.disabled?
410
+ end unless hook.ensured.empty? || hook.disabled?
411
+
412
+ Thread.current[:sqreen_hook_entered] = false
413
+ sqreen_timer.stop if sqreen_timer
414
+ rescue StandardError => e
415
+ Sqreen::Weave.logger.debug { "exception:#{e.class} message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
416
+ Sqreen::RemoteException.record(e) if Sqreen.queue
306
417
  end
307
418
 
308
- Thread.current[:sqreen_hook_entered] = false
309
- timer.stop if timer
419
+ # TODO: should we run the following?
420
+ # raise hooked_call.raise if hooked_call.raising
421
+ # return hooked_call.returning ? hooked_call.return : hooked_call.returned
310
422
  end
311
423
  end # chrono
312
424
  end