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