sqreen 1.23.0 → 1.25.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,305 @@
1
+ # typed: ignore
2
+
3
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
4
+ # Please refer to our terms for more information: https://www.sqreen.com/terms.html
5
+
6
+ require 'sqreen/graft/call'
7
+
8
+ module Sqreen
9
+ module Graft
10
+ class Hook
11
+ def self.wrapper(hook)
12
+ timed_hooks_proc = proc do |t|
13
+ if (request = Thread.current[:sqreen_http_request])
14
+ request[:timed_hooks] << t if request[:timed_level] >= 1
15
+ end
16
+ end
17
+ timed_callbacks_proc = proc do |t|
18
+ if (request = Thread.current[:sqreen_http_request])
19
+ request[:timed_callbacks] << t if request[:timed_level] >= 1
20
+ end
21
+ end
22
+
23
+ Proc.new do |*args, &block|
24
+ request = Thread.current[:sqreen_http_request]
25
+
26
+ if Thread.current[:sqreen_hook_entered]
27
+ if hook.point.super?
28
+ return super(*args, &block)
29
+ else
30
+ return hook.point.apply(self, 'sqreen_hook', *args, &block)
31
+ end
32
+ end
33
+
34
+ if request && request[:time_budget_expended] && (hook.before + hook.after + hook.raised + hook.ensured).none?(&:mandatory)
35
+ if request[:timed_level] >= 2
36
+ begin
37
+ request[:skipped_callbacks].concat(hook.before)
38
+
39
+ if hook.point.super?
40
+ return super(*args, &block)
41
+ else
42
+ return hook.point.apply(self, 'sqreen_hook', *args, &block)
43
+ end
44
+ rescue ::Exception # rubocop:disable Lint/RescueException
45
+ request[:skipped_callbacks].concat(hook.raised)
46
+ raise
47
+ else
48
+ request[:skipped_callbacks].concat(hook.after)
49
+ ensure
50
+ request[:skipped_callbacks].concat(hook.ensured)
51
+ end
52
+ else
53
+ if hook.point.super? # rubocop:disable Style/IfInsideElse
54
+ return super(*args, &block)
55
+ else
56
+ return hook.point.apply(self, 'sqreen_hook', *args, &block)
57
+ end
58
+ end
59
+ end
60
+
61
+ hook_point_super = hook.point.super?
62
+ logger = Sqreen::Graft.logger
63
+ logger_debug = Sqreen::Graft.logger.debug?
64
+
65
+ Timer.new(hook.point, &timed_hooks_proc).measure do |chrono|
66
+ # budget implies request
67
+ # TODO: make budget depend on a generic context (currently "request")
68
+ budget = request[:time_budget] if request
69
+ if budget
70
+ budget_threshold = request[:time_budget_threshold]
71
+ budget_ratio = request[:time_budget_ratio]
72
+ sqreen_timer = request[:sqreen_timer]
73
+ request_timer = request[:request_timer]
74
+ end
75
+
76
+ hooked_call = HookedCall.new(self, args)
77
+
78
+ begin
79
+ begin
80
+ sqreen_timer.start if budget
81
+ Thread.current[:sqreen_hook_entered] = true
82
+
83
+ # TODO: make Call have #ball to throw by cb
84
+ # TODO: can Call be the ball? r = catch(Call.new, &c)
85
+ # TODO: is catch return value a Call? a #dispatch?
86
+ # TODO: make before/after/raised return a CallbackCollection << Array (or extend with module)
87
+ # TODO: add CallbackCollection#each_with_call(instance, args) { |call| ... } ?
88
+ # TODO: HookCall x CallbackCollection#each_with_call x Flow
89
+ # TODO: TimedHookCall TimedCallbackCall
90
+ # TODO: TimeBoundHookCall TimeBoundCallbackCall TimeBoundFlow?
91
+
92
+ request_elapsed = request_timer.elapsed if budget
93
+
94
+ hook.before.each do |c|
95
+ next if c.ignore && c.ignore.call
96
+
97
+ if budget && !c.mandatory
98
+ sqreen_elapsed = sqreen_timer.elapsed
99
+ if budget_ratio && !request[:time_budget_expended]
100
+ fixed_budget = budget_threshold
101
+ proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
102
+ remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
103
+ else
104
+ remaining = budget_threshold - sqreen_elapsed
105
+ end
106
+ unless remaining > 0
107
+ request[:skipped_callbacks] << c
108
+ request[:time_budget_expended] = true
109
+ next
110
+ end
111
+ end
112
+
113
+ flow = catch(Ball.new) do |ball|
114
+ Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
115
+ c.call(CallbackCall.new(c, remaining, hooked_call.instance, hooked_call.args_passed), ball)
116
+ end
117
+ end
118
+
119
+ next unless c.flow && flow.is_a?(Flow)
120
+ hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
121
+ hooked_call.args_pass = flow.args and hooked_call.args_passing = true if flow.args?
122
+ hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
123
+ break if flow.break?
124
+ end unless hook.disabled?
125
+ rescue StandardError => e
126
+ Sqreen::Weave.logger.debug { "exception:#{e.class} when:before message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
127
+ Sqreen::RemoteException.record(e) if Sqreen.queue
128
+ end
129
+
130
+ raise hooked_call.raise if hooked_call.raising
131
+ return hooked_call.return if hooked_call.returning
132
+ ensure
133
+ Thread.current[:sqreen_hook_entered] = false
134
+ sqreen_timer.stop if budget
135
+ end unless hook.before.empty?
136
+
137
+ begin
138
+ chrono.ignore do
139
+ if hook_point_super
140
+ hooked_call.returned = super(*(hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed), &block)
141
+ else
142
+ hooked_call.returned = hook.point.apply(hooked_call.instance, 'sqreen_hook', *(hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed), &block)
143
+ end
144
+ end
145
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
146
+ begin
147
+ sqreen_timer.start if budget
148
+ Thread.current[:sqreen_hook_entered] = true
149
+ hooked_call.raised = e
150
+
151
+ logger.debug { "[#{Process.pid}] Hook #{hook.point} disabled:#{hook.disabled?} exception:#{e}" } if logger_debug
152
+
153
+ # TODO: early escape causes raise too early then caught by ensure which mistakenly reports legit errors to sqreen
154
+ # raise if hook.raised.empty?
155
+
156
+ request_elapsed = request_timer.elapsed if budget
157
+
158
+ hook.raised.each do |c|
159
+ next if c.ignore && c.ignore.call
160
+
161
+ if budget && !c.mandatory
162
+ sqreen_elapsed = sqreen_timer.elapsed
163
+ if budget_ratio && !request[:time_budget_expended]
164
+ fixed_budget = budget_threshold
165
+ proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
166
+ remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
167
+ else
168
+ remaining = budget_threshold - sqreen_elapsed
169
+ end
170
+ unless remaining > 0
171
+ request[:skipped_callbacks] << c
172
+ request[:time_budget_expended] = true
173
+ next
174
+ end
175
+ end
176
+
177
+ flow = catch(Ball.new) do |ball|
178
+ Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
179
+ 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)
180
+ end
181
+ end
182
+
183
+ next unless c.flow && flow.is_a?(Flow)
184
+ hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
185
+ hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
186
+ hooked_call.retrying = true if flow.retry?
187
+ break if flow.break?
188
+ end unless hook.disabled?
189
+ rescue StandardError => e
190
+ Sqreen::Weave.logger.debug { "exception:#{e.class} when:raised message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
191
+ Sqreen::RemoteException.record(e) if Sqreen.queue
192
+ end
193
+
194
+ retry if hooked_call.retrying
195
+ raise hooked_call.raise if hooked_call.raising
196
+ return hooked_call.return if hooked_call.returning
197
+ raise
198
+ else
199
+ begin
200
+ sqreen_timer.start if budget
201
+ Thread.current[:sqreen_hook_entered] = true
202
+
203
+ # TODO: hooked_call.returning should be always false here?
204
+ return hooked_call.returning ? hooked_call.return : hooked_call.returned if hook.after.empty?
205
+
206
+ request_elapsed = request_timer.elapsed if budget
207
+
208
+ hook.after.each do |c|
209
+ next if c.ignore && c.ignore.call
210
+
211
+ if budget && !c.mandatory
212
+ sqreen_elapsed = sqreen_timer.elapsed
213
+ if budget_ratio && !request[:time_budget_expended]
214
+ fixed_budget = budget_threshold
215
+ proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
216
+ remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
217
+ else
218
+ remaining = budget_threshold - sqreen_elapsed
219
+ end
220
+ unless remaining > 0
221
+ request[:skipped_callbacks] << c
222
+ request[:time_budget_expended] = true
223
+ next
224
+ end
225
+ end
226
+
227
+ flow = catch(Ball.new) do |ball|
228
+ Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
229
+ 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)
230
+ end
231
+ end
232
+
233
+ next unless c.flow && flow.is_a?(Flow)
234
+ hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
235
+ hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
236
+ break if flow.break?
237
+ end unless hook.disabled?
238
+ rescue StandardError => e
239
+ Sqreen::Weave.logger.debug { "exception:#{e.class} when:after message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
240
+ Sqreen::RemoteException.record(e) if Sqreen.queue
241
+ end
242
+
243
+ raise hooked_call.raise if hooked_call.raising
244
+ return hooked_call.returning ? hooked_call.return : hooked_call.returned
245
+ ensure
246
+ begin
247
+ # TODO: sqreen_timer.start if someone has thrown?
248
+ # TODO: sqreen_timer.stop at end of rescue+else?
249
+ # TODO: Thread.current[:sqreen_hook_entered] = true if neither rescue nor else ie thrown?
250
+ # TODO: Thread.current[:sqreen_hook_entered] = false at end of rescue+else? (risky?)
251
+
252
+ # TODO: uniform early bail out? (but don't forget sqreen_hook_entered and sqreen_timer)
253
+ # return hooked_call.returning ? hooked_call.return : hooked_call.returned if hook.ensured.empty?
254
+
255
+ # done at either rescue or else
256
+ # request_elapsed = request_timer.elapsed if budget # && !hook.ensured.empty?
257
+
258
+ hook.ensured.each do |c|
259
+ next if c.ignore && c.ignore.call
260
+
261
+ if budget && !c.mandatory
262
+ sqreen_elapsed = sqreen_timer.elapsed
263
+ if budget_ratio && !request[:time_budget_expended]
264
+ fixed_budget = budget_threshold
265
+ proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
266
+ remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
267
+ else
268
+ remaining = budget_threshold - sqreen_elapsed
269
+ end
270
+ unless remaining > 0
271
+ request[:skipped_callbacks] << c
272
+ request[:time_budget_expended] = true
273
+ next
274
+ end
275
+ end
276
+
277
+ flow = catch(Ball.new) do |ball|
278
+ Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
279
+ 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)
280
+ end
281
+ end
282
+
283
+ next unless c.flow && flow.is_a?(Flow)
284
+ hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
285
+ hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
286
+ break if flow.break?
287
+ end unless hook.ensured.empty? || hook.disabled?
288
+
289
+ Thread.current[:sqreen_hook_entered] = false
290
+ sqreen_timer.stop if budget
291
+ rescue StandardError => e
292
+ Sqreen::Weave.logger.debug { "exception:#{e.class} when:ensured message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
293
+ Sqreen::RemoteException.record(e) if Sqreen.queue
294
+ end
295
+
296
+ # TODO: should we run the following?
297
+ # raise hooked_call.raise if hooked_call.raising
298
+ # return hooked_call.returning ? hooked_call.return : hooked_call.returned
299
+ end
300
+ end # chrono
301
+ end
302
+ end
303
+ end
304
+ end
305
+ end
@@ -0,0 +1,305 @@
1
+ # typed: ignore
2
+
3
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
4
+ # Please refer to our terms for more information: https://www.sqreen.com/terms.html
5
+
6
+ require 'sqreen/graft/call'
7
+
8
+ module Sqreen
9
+ module Graft
10
+ class Hook
11
+ def self.wrapper(hook)
12
+ timed_hooks_proc = proc do |t|
13
+ if (request = Thread.current[:sqreen_http_request])
14
+ request[:timed_hooks] << t if request[:timed_level] >= 1
15
+ end
16
+ end
17
+ timed_callbacks_proc = proc do |t|
18
+ if (request = Thread.current[:sqreen_http_request])
19
+ request[:timed_callbacks] << t if request[:timed_level] >= 1
20
+ end
21
+ end
22
+
23
+ Proc.new do |*args, **kwargs, &block|
24
+ request = Thread.current[:sqreen_http_request]
25
+
26
+ if Thread.current[:sqreen_hook_entered]
27
+ if hook.point.super?
28
+ return super(*args, **kwargs, &block)
29
+ else
30
+ return hook.point.apply(self, 'sqreen_hook', *args, **kwargs, &block)
31
+ end
32
+ end
33
+
34
+ if request && request[:time_budget_expended] && (hook.before + hook.after + hook.raised + hook.ensured).none?(&:mandatory)
35
+ if request[:timed_level] >= 2
36
+ begin
37
+ request[:skipped_callbacks].concat(hook.before)
38
+
39
+ if hook.point.super?
40
+ return super(*args, **kwargs, &block)
41
+ else
42
+ return hook.point.apply(self, 'sqreen_hook', *args, **kwargs, &block)
43
+ end
44
+ rescue ::Exception # rubocop:disable Lint/RescueException
45
+ request[:skipped_callbacks].concat(hook.raised)
46
+ raise
47
+ else
48
+ request[:skipped_callbacks].concat(hook.after)
49
+ ensure
50
+ request[:skipped_callbacks].concat(hook.ensured)
51
+ end
52
+ else
53
+ if hook.point.super? # rubocop:disable Style/IfInsideElse
54
+ return super(*args, **kwargs, &block)
55
+ else
56
+ return hook.point.apply(self, 'sqreen_hook', *args, **kwargs, &block)
57
+ end
58
+ end
59
+ end
60
+
61
+ hook_point_super = hook.point.super?
62
+ logger = Sqreen::Graft.logger
63
+ logger_debug = Sqreen::Graft.logger.debug?
64
+
65
+ Timer.new(hook.point, &timed_hooks_proc).measure do |chrono|
66
+ # budget implies request
67
+ # TODO: make budget depend on a generic context (currently "request")
68
+ budget = request[:time_budget] if request
69
+ if budget
70
+ budget_threshold = request[:time_budget_threshold]
71
+ budget_ratio = request[:time_budget_ratio]
72
+ sqreen_timer = request[:sqreen_timer]
73
+ request_timer = request[:request_timer]
74
+ end
75
+
76
+ hooked_call = HookedCall.new(self, args)
77
+
78
+ begin
79
+ begin
80
+ sqreen_timer.start if budget
81
+ Thread.current[:sqreen_hook_entered] = true
82
+
83
+ # TODO: make Call have #ball to throw by cb
84
+ # TODO: can Call be the ball? r = catch(Call.new, &c)
85
+ # TODO: is catch return value a Call? a #dispatch?
86
+ # TODO: make before/after/raised return a CallbackCollection << Array (or extend with module)
87
+ # TODO: add CallbackCollection#each_with_call(instance, args) { |call| ... } ?
88
+ # TODO: HookCall x CallbackCollection#each_with_call x Flow
89
+ # TODO: TimedHookCall TimedCallbackCall
90
+ # TODO: TimeBoundHookCall TimeBoundCallbackCall TimeBoundFlow?
91
+
92
+ request_elapsed = request_timer.elapsed if budget
93
+
94
+ hook.before.each do |c|
95
+ next if c.ignore && c.ignore.call
96
+
97
+ if budget && !c.mandatory
98
+ sqreen_elapsed = sqreen_timer.elapsed
99
+ if budget_ratio && !request[:time_budget_expended]
100
+ fixed_budget = budget_threshold
101
+ proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
102
+ remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
103
+ else
104
+ remaining = budget_threshold - sqreen_elapsed
105
+ end
106
+ unless remaining > 0
107
+ request[:skipped_callbacks] << c
108
+ request[:time_budget_expended] = true
109
+ next
110
+ end
111
+ end
112
+
113
+ flow = catch(Ball.new) do |ball|
114
+ Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
115
+ c.call(CallbackCall.new(c, remaining, hooked_call.instance, kwargs.empty? ? hooked_call.args_passed : hooked_call.args_passed + [kwargs]), ball)
116
+ end
117
+ end
118
+
119
+ next unless c.flow && flow.is_a?(Flow)
120
+ hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
121
+ hooked_call.args_pass = flow.args and hooked_call.args_passing = true if flow.args?
122
+ hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
123
+ break if flow.break?
124
+ end unless hook.disabled?
125
+ rescue StandardError => e
126
+ Sqreen::Weave.logger.debug { "exception:#{e.class} when:before message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
127
+ Sqreen::RemoteException.record(e) if Sqreen.queue
128
+ end
129
+
130
+ raise hooked_call.raise if hooked_call.raising
131
+ return hooked_call.return if hooked_call.returning
132
+ ensure
133
+ Thread.current[:sqreen_hook_entered] = false
134
+ sqreen_timer.stop if budget
135
+ end unless hook.before.empty?
136
+
137
+ begin
138
+ chrono.ignore do
139
+ if hook_point_super
140
+ hooked_call.returned = super(*(hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed), **kwargs, &block)
141
+ else
142
+ hooked_call.returned = hook.point.apply(hooked_call.instance, 'sqreen_hook', *(hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed), **kwargs, &block)
143
+ end
144
+ end
145
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
146
+ begin
147
+ sqreen_timer.start if budget
148
+ Thread.current[:sqreen_hook_entered] = true
149
+ hooked_call.raised = e
150
+
151
+ logger.debug { "[#{Process.pid}] Hook #{hook.point} disabled:#{hook.disabled?} exception:#{e}" } if logger_debug
152
+
153
+ # TODO: early escape causes raise too early then caught by ensure which mistakenly reports legit errors to sqreen
154
+ # raise if hook.raised.empty?
155
+
156
+ request_elapsed = request_timer.elapsed if budget
157
+
158
+ hook.raised.each do |c|
159
+ next if c.ignore && c.ignore.call
160
+
161
+ if budget && !c.mandatory
162
+ sqreen_elapsed = sqreen_timer.elapsed
163
+ if budget_ratio && !request[:time_budget_expended]
164
+ fixed_budget = budget_threshold
165
+ proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
166
+ remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
167
+ else
168
+ remaining = budget_threshold - sqreen_elapsed
169
+ end
170
+ unless remaining > 0
171
+ request[:skipped_callbacks] << c
172
+ request[:time_budget_expended] = true
173
+ next
174
+ end
175
+ end
176
+
177
+ flow = catch(Ball.new) do |ball|
178
+ Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
179
+ 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)
180
+ end
181
+ end
182
+
183
+ next unless c.flow && flow.is_a?(Flow)
184
+ hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
185
+ hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
186
+ hooked_call.retrying = true if flow.retry?
187
+ break if flow.break?
188
+ end unless hook.disabled?
189
+ rescue StandardError => e
190
+ Sqreen::Weave.logger.debug { "exception:#{e.class} when:raised message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
191
+ Sqreen::RemoteException.record(e) if Sqreen.queue
192
+ end
193
+
194
+ retry if hooked_call.retrying
195
+ raise hooked_call.raise if hooked_call.raising
196
+ return hooked_call.return if hooked_call.returning
197
+ raise
198
+ else
199
+ begin
200
+ sqreen_timer.start if budget
201
+ Thread.current[:sqreen_hook_entered] = true
202
+
203
+ # TODO: hooked_call.returning should be always false here?
204
+ return hooked_call.returning ? hooked_call.return : hooked_call.returned if hook.after.empty?
205
+
206
+ request_elapsed = request_timer.elapsed if budget
207
+
208
+ hook.after.each do |c|
209
+ next if c.ignore && c.ignore.call
210
+
211
+ if budget && !c.mandatory
212
+ sqreen_elapsed = sqreen_timer.elapsed
213
+ if budget_ratio && !request[:time_budget_expended]
214
+ fixed_budget = budget_threshold
215
+ proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
216
+ remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
217
+ else
218
+ remaining = budget_threshold - sqreen_elapsed
219
+ end
220
+ unless remaining > 0
221
+ request[:skipped_callbacks] << c
222
+ request[:time_budget_expended] = true
223
+ next
224
+ end
225
+ end
226
+
227
+ flow = catch(Ball.new) do |ball|
228
+ Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
229
+ 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)
230
+ end
231
+ end
232
+
233
+ next unless c.flow && flow.is_a?(Flow)
234
+ hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
235
+ hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
236
+ break if flow.break?
237
+ end unless hook.disabled?
238
+ rescue StandardError => e
239
+ Sqreen::Weave.logger.debug { "exception:#{e.class} when:after message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
240
+ Sqreen::RemoteException.record(e) if Sqreen.queue
241
+ end
242
+
243
+ raise hooked_call.raise if hooked_call.raising
244
+ return hooked_call.returning ? hooked_call.return : hooked_call.returned
245
+ ensure
246
+ begin
247
+ # TODO: sqreen_timer.start if someone has thrown?
248
+ # TODO: sqreen_timer.stop at end of rescue+else?
249
+ # TODO: Thread.current[:sqreen_hook_entered] = true if neither rescue nor else ie thrown?
250
+ # TODO: Thread.current[:sqreen_hook_entered] = false at end of rescue+else? (risky?)
251
+
252
+ # TODO: uniform early bail out? (but don't forget sqreen_hook_entered and sqreen_timer)
253
+ # return hooked_call.returning ? hooked_call.return : hooked_call.returned if hook.ensured.empty?
254
+
255
+ # done at either rescue or else
256
+ # request_elapsed = request_timer.elapsed if budget # && !hook.ensured.empty?
257
+
258
+ hook.ensured.each do |c|
259
+ next if c.ignore && c.ignore.call
260
+
261
+ if budget && !c.mandatory
262
+ sqreen_elapsed = sqreen_timer.elapsed
263
+ if budget_ratio && !request[:time_budget_expended]
264
+ fixed_budget = budget_threshold
265
+ proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
266
+ remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
267
+ else
268
+ remaining = budget_threshold - sqreen_elapsed
269
+ end
270
+ unless remaining > 0
271
+ request[:skipped_callbacks] << c
272
+ request[:time_budget_expended] = true
273
+ next
274
+ end
275
+ end
276
+
277
+ flow = catch(Ball.new) do |ball|
278
+ Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
279
+ 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)
280
+ end
281
+ end
282
+
283
+ next unless c.flow && flow.is_a?(Flow)
284
+ hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
285
+ hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
286
+ break if flow.break?
287
+ end unless hook.ensured.empty? || hook.disabled?
288
+
289
+ Thread.current[:sqreen_hook_entered] = false
290
+ sqreen_timer.stop if budget
291
+ rescue StandardError => e
292
+ Sqreen::Weave.logger.debug { "exception:#{e.class} when:ensured message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
293
+ Sqreen::RemoteException.record(e) if Sqreen.queue
294
+ end
295
+
296
+ # TODO: should we run the following?
297
+ # raise hooked_call.raise if hooked_call.raising
298
+ # return hooked_call.returning ? hooked_call.return : hooked_call.returned
299
+ end
300
+ end # chrono
301
+ end
302
+ end
303
+ end
304
+ end
305
+ end
@@ -105,12 +105,6 @@ module Sqreen
105
105
  @strategy == :prepend
106
106
  end
107
107
 
108
- def apply(obj, suffix, *args, &block)
109
- raise 'use super' if super?
110
-
111
- obj.send("#{method_name}_without_#{suffix}", *args, &block)
112
- end
113
-
114
108
  def install(key, &block)
115
109
  if installed?(key)
116
110
  Sqreen::Graft.logger.debug { "[#{Process.pid}] #{self} already installed" }
@@ -344,3 +338,9 @@ module Sqreen
344
338
  end
345
339
  end
346
340
  end
341
+
342
+ if RUBY_VERSION =~ /^2\./
343
+ load File.join(__dir__, 'hook_point.ruby_2.rb')
344
+ else
345
+ load File.join(__dir__, 'hook_point.ruby_3.rb')
346
+ end
@@ -0,0 +1,18 @@
1
+ # typed: ignore
2
+
3
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
4
+ # Please refer to our terms for more information: https://www.sqreen.com/terms.html
5
+
6
+ require 'sqreen/graft'
7
+
8
+ module Sqreen
9
+ module Graft
10
+ class HookPoint
11
+ def apply(obj, suffix, *args, &block)
12
+ raise 'use super' if super?
13
+
14
+ obj.send("#{method_name}_without_#{suffix}", *args, &block)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ # typed: ignore
2
+
3
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
4
+ # Please refer to our terms for more information: https://www.sqreen.com/terms.html
5
+
6
+ require 'sqreen/graft'
7
+
8
+ module Sqreen
9
+ module Graft
10
+ class HookPoint
11
+ def apply(obj, suffix, *args, **kwargs, &block)
12
+ raise 'use super' if super?
13
+
14
+ obj.send("#{method_name}_without_#{suffix}", *args, **kwargs, &block)
15
+ end
16
+ end
17
+ end
18
+ end
19
+