sqreen 1.23.0 → 1.23.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e6b62b53864420b27411824bf5d51c80949a7e98357edd128e64ee0e77039476
4
- data.tar.gz: 85a9d762af8063c9e42978100907561397bd00e8278978b9a67f01e5fe52670f
3
+ metadata.gz: a273f9a80bf30fbd4dcf8fc21551639e1c8df66b3dfa8be2d16e8ad5d58d4b47
4
+ data.tar.gz: cf2ff1d623cdae5a55ec39bc9959351775375836e5cf2ae71844bb7ee51b5ab6
5
5
  SHA512:
6
- metadata.gz: ca4c03f84749101fe1b2235ead061cf597bfb0a4d08d95d9cd3b24cdb9ca77eea3928fa00ce4db22219acea02c93539251f384002ccb3e4fffd1c484d54ad938
7
- data.tar.gz: 02e372ee65122d783685df0756bf069a30225348239703465a7a20f751865bc5ab79ba9c0c99dafc1551d1812532669089681dc71b8375cb4791df749f9d3977
6
+ metadata.gz: 68e69af0cb0d36971cea1ea58919372840faa792d620d99e7bc9e30c6afc39aa2aa564cf529a8f67bea8da79411c5d270df9e27a4db71e015461eba1c2d9f788
7
+ data.tar.gz: 14b82f5804bf6da658f44c3702ca8f877811cb0363f754703f2f7efa6440c0ffaa1a19e09c17c96f920e2855382041420069169bed8d7a77d1b4285d083af3ca
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 1.23.1
2
+
3
+ * Improve compatibility with gems such as puma and graphql on Ruby 3.0
4
+
1
5
  ## 1.23.0
2
6
 
3
7
  * Implement GraphQL support
@@ -4,9 +4,8 @@
4
4
  # Please refer to our terms for more information: https://www.sqreen.com/terms.html
5
5
 
6
6
  require 'sqreen/graft'
7
- require 'sqreen/graft/call'
8
- require 'sqreen/graft/callback'
9
7
  require 'sqreen/graft/hook_point'
8
+ require 'sqreen/graft/callback'
10
9
 
11
10
  module Sqreen
12
11
  module Graft
@@ -124,299 +123,12 @@ module Sqreen
124
123
  @raised = []
125
124
  @ensured = []
126
125
  end
127
-
128
- def self.wrapper(hook)
129
- timed_hooks_proc = proc do |t|
130
- if (request = Thread.current[:sqreen_http_request])
131
- request[:timed_hooks] << t if request[:timed_level] >= 1
132
- end
133
- end
134
- timed_callbacks_proc = proc do |t|
135
- if (request = Thread.current[:sqreen_http_request])
136
- request[:timed_callbacks] << t if request[:timed_level] >= 1
137
- end
138
- end
139
-
140
- Proc.new do |*args, &block|
141
- request = Thread.current[:sqreen_http_request]
142
-
143
- if Thread.current[:sqreen_hook_entered]
144
- if hook.point.super?
145
- return super(*args, &block)
146
- else
147
- return hook.point.apply(self, 'sqreen_hook', *args, &block)
148
- end
149
- end
150
-
151
- if request && request[:time_budget_expended] && (hook.before + hook.after + hook.raised + hook.ensured).none?(&:mandatory)
152
- if request[:timed_level] >= 2
153
- begin
154
- request[:skipped_callbacks].concat(hook.before)
155
-
156
- if hook.point.super?
157
- return super(*args, &block)
158
- else
159
- return hook.point.apply(self, 'sqreen_hook', *args, &block)
160
- end
161
- rescue ::Exception # rubocop:disable Lint/RescueException
162
- request[:skipped_callbacks].concat(hook.raised)
163
- raise
164
- else
165
- request[:skipped_callbacks].concat(hook.after)
166
- ensure
167
- request[:skipped_callbacks].concat(hook.ensured)
168
- end
169
- else
170
- if hook.point.super? # rubocop:disable Style/IfInsideElse
171
- return super(*args, &block)
172
- else
173
- return hook.point.apply(self, 'sqreen_hook', *args, &block)
174
- end
175
- end
176
- end
177
-
178
- hook_point_super = hook.point.super?
179
- logger = Sqreen::Graft.logger
180
- logger_debug = Sqreen::Graft.logger.debug?
181
-
182
- Timer.new(hook.point, &timed_hooks_proc).measure do |chrono|
183
- # budget implies request
184
- # TODO: make budget depend on a generic context (currently "request")
185
- budget = request[:time_budget] if request
186
- if budget
187
- budget_threshold = request[:time_budget_threshold]
188
- budget_ratio = request[:time_budget_ratio]
189
- sqreen_timer = request[:sqreen_timer]
190
- request_timer = request[:request_timer]
191
- end
192
-
193
- hooked_call = HookedCall.new(self, args)
194
-
195
- begin
196
- begin
197
- sqreen_timer.start if budget
198
- Thread.current[:sqreen_hook_entered] = true
199
-
200
- # TODO: make Call have #ball to throw by cb
201
- # TODO: can Call be the ball? r = catch(Call.new, &c)
202
- # TODO: is catch return value a Call? a #dispatch?
203
- # TODO: make before/after/raised return a CallbackCollection << Array (or extend with module)
204
- # TODO: add CallbackCollection#each_with_call(instance, args) { |call| ... } ?
205
- # TODO: HookCall x CallbackCollection#each_with_call x Flow
206
- # TODO: TimedHookCall TimedCallbackCall
207
- # TODO: TimeBoundHookCall TimeBoundCallbackCall TimeBoundFlow?
208
-
209
- request_elapsed = request_timer.elapsed if budget
210
-
211
- hook.before.each do |c|
212
- next if c.ignore && c.ignore.call
213
-
214
- if budget && !c.mandatory
215
- sqreen_elapsed = sqreen_timer.elapsed
216
- if budget_ratio && !request[:time_budget_expended]
217
- fixed_budget = budget_threshold
218
- proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
219
- remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
220
- else
221
- remaining = budget_threshold - sqreen_elapsed
222
- end
223
- unless remaining > 0
224
- request[:skipped_callbacks] << c
225
- request[:time_budget_expended] = true
226
- next
227
- end
228
- end
229
-
230
- flow = catch(Ball.new) do |ball|
231
- Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
232
- c.call(CallbackCall.new(c, remaining, hooked_call.instance, hooked_call.args_passed), ball)
233
- end
234
- end
235
-
236
- next unless c.flow && flow.is_a?(Flow)
237
- hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
238
- hooked_call.args_pass = flow.args and hooked_call.args_passing = true if flow.args?
239
- hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
240
- break if flow.break?
241
- end unless hook.disabled?
242
- rescue StandardError => e
243
- Sqreen::Weave.logger.debug { "exception:#{e.class} when:before message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
244
- Sqreen::RemoteException.record(e) if Sqreen.queue
245
- end
246
-
247
- raise hooked_call.raise if hooked_call.raising
248
- return hooked_call.return if hooked_call.returning
249
- ensure
250
- Thread.current[:sqreen_hook_entered] = false
251
- sqreen_timer.stop if budget
252
- end unless hook.before.empty?
253
-
254
- begin
255
- chrono.ignore do
256
- if hook_point_super
257
- hooked_call.returned = super(*(hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed), &block)
258
- else
259
- hooked_call.returned = hook.point.apply(hooked_call.instance, 'sqreen_hook', *(hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed), &block)
260
- end
261
- end
262
- rescue ::Exception => e # rubocop:disable Lint/RescueException
263
- begin
264
- sqreen_timer.start if budget
265
- Thread.current[:sqreen_hook_entered] = true
266
- hooked_call.raised = e
267
-
268
- logger.debug { "[#{Process.pid}] Hook #{hook.point} disabled:#{hook.disabled?} exception:#{e}" } if logger_debug
269
-
270
- # TODO: early escape causes raise too early then caught by ensure which mistakenly reports legit errors to sqreen
271
- # raise if hook.raised.empty?
272
-
273
- request_elapsed = request_timer.elapsed if budget
274
-
275
- hook.raised.each do |c|
276
- next if c.ignore && c.ignore.call
277
-
278
- if budget && !c.mandatory
279
- sqreen_elapsed = sqreen_timer.elapsed
280
- if budget_ratio && !request[:time_budget_expended]
281
- fixed_budget = budget_threshold
282
- proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
283
- remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
284
- else
285
- remaining = budget_threshold - sqreen_elapsed
286
- end
287
- unless remaining > 0
288
- request[:skipped_callbacks] << c
289
- request[:time_budget_expended] = true
290
- next
291
- end
292
- end
293
-
294
- flow = catch(Ball.new) do |ball|
295
- Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
296
- 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)
297
- end
298
- end
299
-
300
- next unless c.flow && flow.is_a?(Flow)
301
- hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
302
- hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
303
- hooked_call.retrying = true if flow.retry?
304
- break if flow.break?
305
- end unless hook.disabled?
306
- rescue StandardError => e
307
- Sqreen::Weave.logger.debug { "exception:#{e.class} when:raised message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
308
- Sqreen::RemoteException.record(e) if Sqreen.queue
309
- end
310
-
311
- retry if hooked_call.retrying
312
- raise hooked_call.raise if hooked_call.raising
313
- return hooked_call.return if hooked_call.returning
314
- raise
315
- else
316
- begin
317
- sqreen_timer.start if budget
318
- Thread.current[:sqreen_hook_entered] = true
319
-
320
- # TODO: hooked_call.returning should be always false here?
321
- return hooked_call.returning ? hooked_call.return : hooked_call.returned if hook.after.empty?
322
-
323
- request_elapsed = request_timer.elapsed if budget
324
-
325
- hook.after.each do |c|
326
- next if c.ignore && c.ignore.call
327
-
328
- if budget && !c.mandatory
329
- sqreen_elapsed = sqreen_timer.elapsed
330
- if budget_ratio && !request[:time_budget_expended]
331
- fixed_budget = budget_threshold
332
- proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
333
- remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
334
- else
335
- remaining = budget_threshold - sqreen_elapsed
336
- end
337
- unless remaining > 0
338
- request[:skipped_callbacks] << c
339
- request[:time_budget_expended] = true
340
- next
341
- end
342
- end
343
-
344
- flow = catch(Ball.new) do |ball|
345
- Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
346
- 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)
347
- end
348
- end
349
-
350
- next unless c.flow && flow.is_a?(Flow)
351
- hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
352
- hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
353
- break if flow.break?
354
- end unless hook.disabled?
355
- rescue StandardError => e
356
- Sqreen::Weave.logger.debug { "exception:#{e.class} when:after message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
357
- Sqreen::RemoteException.record(e) if Sqreen.queue
358
- end
359
-
360
- raise hooked_call.raise if hooked_call.raising
361
- return hooked_call.returning ? hooked_call.return : hooked_call.returned
362
- ensure
363
- begin
364
- # TODO: sqreen_timer.start if someone has thrown?
365
- # TODO: sqreen_timer.stop at end of rescue+else?
366
- # TODO: Thread.current[:sqreen_hook_entered] = true if neither rescue nor else ie thrown?
367
- # TODO: Thread.current[:sqreen_hook_entered] = false at end of rescue+else? (risky?)
368
-
369
- # TODO: uniform early bail out? (but don't forget sqreen_hook_entered and sqreen_timer)
370
- # return hooked_call.returning ? hooked_call.return : hooked_call.returned if hook.ensured.empty?
371
-
372
- # done at either rescue or else
373
- # request_elapsed = request_timer.elapsed if budget # && !hook.ensured.empty?
374
-
375
- hook.ensured.each do |c|
376
- next if c.ignore && c.ignore.call
377
-
378
- if budget && !c.mandatory
379
- sqreen_elapsed = sqreen_timer.elapsed
380
- if budget_ratio && !request[:time_budget_expended]
381
- fixed_budget = budget_threshold
382
- proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
383
- remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
384
- else
385
- remaining = budget_threshold - sqreen_elapsed
386
- end
387
- unless remaining > 0
388
- request[:skipped_callbacks] << c
389
- request[:time_budget_expended] = true
390
- next
391
- end
392
- end
393
-
394
- flow = catch(Ball.new) do |ball|
395
- Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
396
- 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)
397
- end
398
- end
399
-
400
- next unless c.flow && flow.is_a?(Flow)
401
- hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
402
- hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
403
- break if flow.break?
404
- end unless hook.ensured.empty? || hook.disabled?
405
-
406
- Thread.current[:sqreen_hook_entered] = false
407
- sqreen_timer.stop if budget
408
- rescue StandardError => e
409
- Sqreen::Weave.logger.debug { "exception:#{e.class} when:ensured message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
410
- Sqreen::RemoteException.record(e) if Sqreen.queue
411
- end
412
-
413
- # TODO: should we run the following?
414
- # raise hooked_call.raise if hooked_call.raising
415
- # return hooked_call.returning ? hooked_call.return : hooked_call.returned
416
- end
417
- end # chrono
418
- end
419
- end
420
126
  end
421
127
  end
422
128
  end
129
+
130
+ if RUBY_VERSION =~ /^2\./
131
+ load File.join(__dir__, 'hook.ruby_2.rb')
132
+ else
133
+ load File.join(__dir__, 'hook.ruby_3.rb')
134
+ 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, &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, 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), **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
+
@@ -4,5 +4,5 @@
4
4
  # Please refer to our terms for more information: https://www.sqreen.com/terms.html
5
5
 
6
6
  module Sqreen
7
- VERSION = '1.23.0'.freeze
7
+ VERSION = '1.23.1'.freeze
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqreen
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.23.0
4
+ version: 1.23.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sqreen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-15 00:00:00.000000000 Z
11
+ date: 2021-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sqreen-backport
@@ -198,7 +198,11 @@ files:
198
198
  - lib/sqreen/graft/call.rb
199
199
  - lib/sqreen/graft/callback.rb
200
200
  - lib/sqreen/graft/hook.rb
201
+ - lib/sqreen/graft/hook.ruby_2.rb
202
+ - lib/sqreen/graft/hook.ruby_3.rb
201
203
  - lib/sqreen/graft/hook_point.rb
204
+ - lib/sqreen/graft/hook_point.ruby_2.rb
205
+ - lib/sqreen/graft/hook_point.ruby_3.rb
202
206
  - lib/sqreen/graft/hook_point_error.rb
203
207
  - lib/sqreen/invalid_signature_exception.rb
204
208
  - lib/sqreen/js.rb
@@ -341,7 +345,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
341
345
  requirements:
342
346
  - - ">="
343
347
  - !ruby/object:Gem::Version
344
- version: 1.9.3
348
+ version: '2.0'
345
349
  required_rubygems_version: !ruby/object:Gem::Requirement
346
350
  requirements:
347
351
  - - ">="