sqreen 1.20.4 → 1.21.0.beta1
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 +0 -25
- 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/condition_evaluator.rb +2 -8
- data/lib/sqreen/configuration.rb +1 -1
- data/lib/sqreen/deferred_logger.rb +14 -50
- data/lib/sqreen/deliveries/batch.rb +8 -1
- data/lib/sqreen/ecosystem.rb +80 -0
- data/lib/sqreen/ecosystem/dispatch_table.rb +43 -0
- data/lib/sqreen/ecosystem/http/net_http.rb +51 -0
- data/lib/sqreen/ecosystem/http/rack_request.rb +38 -0
- data/lib/sqreen/ecosystem/loggable.rb +13 -0
- data/lib/sqreen/ecosystem/module_api.rb +30 -0
- data/lib/sqreen/ecosystem/module_api/event_listener.rb +18 -0
- data/lib/sqreen/ecosystem/module_api/instrumentation.rb +23 -0
- data/lib/sqreen/ecosystem/module_api/signal_producer.rb +26 -0
- data/lib/sqreen/ecosystem/module_api/tracing_push_down.rb +34 -0
- data/lib/sqreen/ecosystem/module_api/transaction_storage.rb +71 -0
- data/lib/sqreen/ecosystem/module_registry.rb +39 -0
- data/lib/sqreen/ecosystem/redis/redis_connection.rb +35 -0
- data/lib/sqreen/ecosystem/tracing/sampler.rb +160 -0
- data/lib/sqreen/ecosystem/tracing/sampling_configuration.rb +150 -0
- data/lib/sqreen/ecosystem/tracing/signals/tracing_client.rb +53 -0
- data/lib/sqreen/ecosystem/tracing/signals/tracing_server.rb +53 -0
- data/lib/sqreen/ecosystem/tracing_id_setup.rb +34 -0
- data/lib/sqreen/ecosystem/transaction_storage.rb +64 -0
- data/lib/sqreen/ecosystem_integration.rb +70 -0
- data/lib/sqreen/ecosystem_integration/around_callbacks.rb +89 -0
- data/lib/sqreen/ecosystem_integration/instrumentation_service.rb +38 -0
- data/lib/sqreen/ecosystem_integration/request_lifecycle_tracking.rb +56 -0
- data/lib/sqreen/ecosystem_integration/signal_consumption.rb +35 -0
- data/lib/sqreen/encoding_sanitizer.rb +27 -0
- data/lib/sqreen/events/request_record.rb +1 -0
- data/lib/sqreen/frameworks/generic.rb +15 -10
- data/lib/sqreen/frameworks/rails.rb +7 -0
- data/lib/sqreen/frameworks/request_recorder.rb +0 -2
- data/lib/sqreen/graft/call.rb +23 -72
- data/lib/sqreen/graft/callback.rb +1 -1
- data/lib/sqreen/graft/hook.rb +85 -187
- data/lib/sqreen/graft/hook_point.rb +1 -1
- data/lib/sqreen/legacy/instrumentation.rb +10 -22
- data/lib/sqreen/legacy/old_event_submission_strategy.rb +8 -3
- data/lib/sqreen/log.rb +2 -3
- data/lib/sqreen/log/loggable.rb +0 -1
- data/lib/sqreen/logger.rb +0 -24
- data/lib/sqreen/metrics_store.rb +0 -11
- data/lib/sqreen/null_logger.rb +0 -22
- data/lib/sqreen/remote_command.rb +3 -1
- data/lib/sqreen/rules.rb +4 -8
- 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 +0 -2
- data/lib/sqreen/rules/waf_cb.rb +3 -3
- data/lib/sqreen/runner.rb +21 -33
- data/lib/sqreen/session.rb +2 -0
- data/lib/sqreen/signals/conversions.rb +6 -1
- data/lib/sqreen/version.rb +1 -1
- data/lib/sqreen/weave/legacy/instrumentation.rb +103 -194
- data/lib/sqreen/worker.rb +2 -6
- metadata +35 -10
- data/lib/sqreen/deprecation.rb +0 -38
- data/lib/sqreen/weave/budget.rb +0 -46
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'sqreen/graft/hook'
|
2
|
+
require 'sqreen/ecosystem_integration/around_callbacks'
|
3
|
+
|
4
|
+
module Sqreen
|
5
|
+
class EcosystemIntegration
|
6
|
+
module InstrumentationService
|
7
|
+
class << self
|
8
|
+
# @param [String] module_name
|
9
|
+
# @param [String] method in form A::B#c or A::B.c
|
10
|
+
# @param [Hash{Symbol=>Proc}] spec
|
11
|
+
def instrument(module_name, method, spec)
|
12
|
+
hook = Sqreen::Graft::Hook[method].add do
|
13
|
+
if spec[:before]
|
14
|
+
cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'pre', spec[:before])
|
15
|
+
before(nil, flow: true, &cb)
|
16
|
+
end
|
17
|
+
|
18
|
+
if spec[:after]
|
19
|
+
cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'post', spec[:after])
|
20
|
+
after(nil, flow: true, &cb)
|
21
|
+
end
|
22
|
+
|
23
|
+
if spec[:raised]
|
24
|
+
cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'failing', spec[:raised])
|
25
|
+
raised(nil, flow: true, &cb)
|
26
|
+
end
|
27
|
+
|
28
|
+
if spec[:ensured]
|
29
|
+
cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'finally', spec[:ensured])
|
30
|
+
ensured(nil, flow: true, &cb)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
hook.install
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'sqreen/log/loggable'
|
2
|
+
|
3
|
+
module Sqreen
|
4
|
+
class EcosystemIntegration
|
5
|
+
# This class gets notified of request start/end and
|
6
|
+
# 1) distributes such events to listeners (typically ecosystem modules;
|
7
|
+
# the method add_start_observer is exposed to ecosystem modules through
|
8
|
+
# +Sqreen::Ecosystem::ModuleApi::EventListener+ and the dispatch table).
|
9
|
+
# 2) keeps track of whether a request is active on this thread. This is
|
10
|
+
# so that users of this class can have this information without needing
|
11
|
+
# to subscribe to request start/events and keeping thread local state
|
12
|
+
# themselves.
|
13
|
+
# XXX: Since the Ecosystem is also notified of request start/end, it could
|
14
|
+
# notify its modules of request start without going through the dispatch
|
15
|
+
# table and call add_start_observer. We need to think if we want to keep
|
16
|
+
# the transaction / request distinction or if they should just be
|
17
|
+
# assumed to be the same, though.
|
18
|
+
class RequestLifecycleTracking
|
19
|
+
include Sqreen::Log::Loggable
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@start_observers = []
|
23
|
+
@tl_key = "#{object_id}_req_in_flight"
|
24
|
+
end
|
25
|
+
|
26
|
+
# API for classes needing to know the request state
|
27
|
+
|
28
|
+
# @param cb A callback taking a Rack::Request
|
29
|
+
def add_start_observer(cb)
|
30
|
+
@start_observers << cb
|
31
|
+
end
|
32
|
+
|
33
|
+
def in_request?
|
34
|
+
Thread.current[@tl_key] ? true : false
|
35
|
+
end
|
36
|
+
|
37
|
+
# API for classes notifying this one of request events
|
38
|
+
|
39
|
+
def notify_request_start(rack_req)
|
40
|
+
Thread.current[@tl_key] = true
|
41
|
+
return if @start_observers.empty?
|
42
|
+
@start_observers.each do |cb|
|
43
|
+
begin
|
44
|
+
cb.call(rack_req)
|
45
|
+
rescue ::Exception => e # rubocop:disable Lint/RescueException
|
46
|
+
logger.warn { "Error calling #{cb} on request start: #{e.message}" }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def notify_request_end
|
52
|
+
Thread.current[@tl_key] = false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'sqreen/log/loggable'
|
2
|
+
|
3
|
+
module Sqreen
|
4
|
+
class EcosystemIntegration
|
5
|
+
class SignalConsumption
|
6
|
+
include Sqreen::Log::Loggable
|
7
|
+
|
8
|
+
PAYLOAD_CREATOR_SECTIONS = %w[request response params headers].freeze
|
9
|
+
|
10
|
+
# @param [Sqreen::Frameworks::GenericFramework] framework
|
11
|
+
# @param [Sqreen::EcosystemIntegration::RequestLifecycleTracking]
|
12
|
+
# @param [Sqreen::CappedQueue]
|
13
|
+
def initialize(framework, req_lifecycle, queue)
|
14
|
+
@framework = framework
|
15
|
+
@req_lifecycle = req_lifecycle
|
16
|
+
@queue = queue
|
17
|
+
end
|
18
|
+
|
19
|
+
def consume_signal(signal)
|
20
|
+
# transitional
|
21
|
+
unless Sqreen.features.fetch('use_signals', DEFAULT_USE_SIGNALS)
|
22
|
+
logger.debug { "Discarding signal #{signal} (signals disabled)" }
|
23
|
+
return
|
24
|
+
end
|
25
|
+
|
26
|
+
if @req_lifecycle.in_request?
|
27
|
+
# add it to the request record
|
28
|
+
@framework.observe(:signals, signal, PAYLOAD_CREATOR_SECTIONS, true)
|
29
|
+
else
|
30
|
+
@queue.push signal
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# typed: true
|
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
|
+
module Sqreen
|
7
|
+
class EncodingSanitizer
|
8
|
+
def self.sanitize(obj)
|
9
|
+
case obj
|
10
|
+
when String
|
11
|
+
sanitize_string(obj)
|
12
|
+
when Array
|
13
|
+
obj.map { |e| sanitize(e) }
|
14
|
+
when Hash
|
15
|
+
obj.each_with_object({}) { |(k, v), h| h[k] = sanitize(v) }
|
16
|
+
else
|
17
|
+
obj
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.sanitize_string(s)
|
22
|
+
return s if s.encoding.name == 'UTF-8' && s.valid_encoding?
|
23
|
+
|
24
|
+
s.encode('UTF-16', :invalid => :replace, :undef => :replace).encode('UTF-8')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -22,8 +22,17 @@ module Sqreen
|
|
22
22
|
include RequestRecorder
|
23
23
|
attr_accessor :sqreen_configuration
|
24
24
|
|
25
|
+
attr_writer :req_start_cb, :req_end_cb
|
26
|
+
|
25
27
|
def initialize
|
26
28
|
clean_request_record
|
29
|
+
|
30
|
+
# for notifying the ecosystem of request boundaries
|
31
|
+
# XXX: this should be refactored. It shouldn't be
|
32
|
+
# the framework doing these notifications to the ecosystem
|
33
|
+
# Probably the rule callback should do it itself
|
34
|
+
@req_start_cb = Proc.new {}
|
35
|
+
@req_end_cb = Proc.new {}
|
27
36
|
end
|
28
37
|
|
29
38
|
# What kind of database is this
|
@@ -209,16 +218,7 @@ module Sqreen
|
|
209
218
|
|
210
219
|
# Should the agent not be starting up?
|
211
220
|
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
|
-
|
220
221
|
return :irb if $0 == 'irb'
|
221
|
-
|
222
222
|
return if sqreen_configuration.nil?
|
223
223
|
disable = sqreen_configuration.get(:disable)
|
224
224
|
return :config_disable if disable == true || disable.to_s.to_i == 1
|
@@ -260,8 +260,12 @@ module Sqreen
|
|
260
260
|
# Nota: cleanup should be performed at end of request (see clean_request)
|
261
261
|
def store_request(object)
|
262
262
|
return unless ensure_rack_loaded
|
263
|
+
|
264
|
+
rack_req = Rack::Request.new(object)
|
265
|
+
@req_start_cb.call(rack_req)
|
266
|
+
|
263
267
|
self.remaining_perf_budget = Sqreen.performance_budget
|
264
|
-
SharedStorage.set(:request,
|
268
|
+
SharedStorage.set(:request, rack_req)
|
265
269
|
SharedStorage.set(:xss_params, nil)
|
266
270
|
SharedStorage.set(:whitelisted, nil)
|
267
271
|
SharedStorage.set(:request_overtime, nil)
|
@@ -290,6 +294,7 @@ module Sqreen
|
|
290
294
|
SharedStorage.set(:xss_params, nil)
|
291
295
|
SharedStorage.set(:whitelisted, nil)
|
292
296
|
SharedStorage.set(:request_overtime, nil)
|
297
|
+
@req_end_cb.call
|
293
298
|
end
|
294
299
|
|
295
300
|
def remaining_perf_budget
|
@@ -103,6 +103,13 @@ 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
|
+
|
106
113
|
return nil unless defined?(Rails::CommandsTasks)
|
107
114
|
return nil if defined?(Rails::Server)
|
108
115
|
return :rails_console if defined?(Rails::Console)
|
@@ -69,8 +69,6 @@ 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'
|
74
72
|
payload = payload_creator.payload(payload_requests)
|
75
73
|
payload[:observed] = observed_items
|
76
74
|
|
data/lib/sqreen/graft/call.rb
CHANGED
@@ -93,116 +93,67 @@ module Sqreen
|
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
|
-
class TimerError < StandardError; end
|
97
|
-
|
98
96
|
class Timer
|
99
97
|
def self.read
|
100
98
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
101
99
|
end
|
102
100
|
|
103
|
-
attr_reader :tag
|
101
|
+
attr_reader :tag
|
104
102
|
|
105
103
|
def initialize(tag, &block)
|
106
104
|
@tag = tag
|
105
|
+
@blips = []
|
107
106
|
@block = block
|
108
|
-
@tally = 0
|
109
|
-
@size = 0
|
110
|
-
end
|
111
|
-
|
112
|
-
def elapsed
|
113
|
-
raise(TimerError, 'Timer#elapsed when paused') if @size.even?
|
114
|
-
|
115
|
-
@tally + Timer.read
|
116
107
|
end
|
117
108
|
|
118
109
|
def duration
|
119
|
-
|
110
|
+
@blips.each_with_index.reduce(0) { |a, (e, i)| i.even? ? a - e : a + e }
|
111
|
+
end
|
120
112
|
|
121
|
-
|
113
|
+
def elapsed
|
114
|
+
@blips.each_with_index.reduce(0) { |a, (e, i)| i.even? ? a - e : a + e } + Timer.read
|
122
115
|
end
|
123
116
|
|
124
117
|
def ignore
|
125
|
-
|
126
|
-
|
127
|
-
@size += 1
|
128
|
-
@tally += Timer.read
|
118
|
+
@blips << Timer.read
|
129
119
|
yield(self)
|
130
120
|
ensure
|
131
|
-
@
|
132
|
-
@tally -= Timer.read
|
121
|
+
@blips << Timer.read
|
133
122
|
end
|
134
123
|
|
135
|
-
def measure
|
136
|
-
|
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
|
-
|
124
|
+
def measure
|
125
|
+
@blips << Timer.read
|
149
126
|
yield(self)
|
150
127
|
ensure
|
151
|
-
|
152
|
-
|
153
|
-
if ignore
|
154
|
-
ignore.size += 1
|
155
|
-
ignore.tally -= now
|
156
|
-
end
|
157
|
-
|
158
|
-
@size += 1
|
159
|
-
@tally += now
|
160
|
-
|
128
|
+
@blips << Timer.read
|
161
129
|
@block.call(self) if @block
|
130
|
+
Sqreen::Graft.logger.debug { "#{@tag}: time=%.03fus" % (duration * 1_000_000) }
|
162
131
|
end
|
163
132
|
|
164
|
-
def start
|
165
|
-
|
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
|
133
|
+
def start
|
134
|
+
@blips << Timer.read
|
180
135
|
end
|
181
136
|
|
182
|
-
def
|
183
|
-
@
|
137
|
+
def stop
|
138
|
+
@blips << Timer.read
|
184
139
|
end
|
185
140
|
|
186
|
-
def
|
187
|
-
@
|
141
|
+
def include_measurements(another_timer)
|
142
|
+
@blips += another_timer.instance_variable_get(:@blips)
|
188
143
|
end
|
189
144
|
|
190
|
-
def
|
191
|
-
|
145
|
+
def start_and_end
|
146
|
+
raise 'Not exactly two measurements recorded' unless size == 2
|
147
|
+
@blips
|
192
148
|
end
|
193
149
|
|
194
|
-
def
|
195
|
-
@size
|
150
|
+
def size
|
151
|
+
@blips.size
|
196
152
|
end
|
197
153
|
|
198
154
|
def to_s
|
199
155
|
"#{@tag}: time=%.03fus" % (duration * 1_000_000)
|
200
156
|
end
|
201
|
-
|
202
|
-
protected
|
203
|
-
|
204
|
-
attr_reader :tally
|
205
|
-
attr_writer :size, :tally
|
206
157
|
end
|
207
158
|
end
|
208
159
|
end
|
@@ -21,7 +21,7 @@ module Sqreen
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def call(*args, &block)
|
24
|
-
|
24
|
+
Sqreen::Graft.logger.debug { "[#{Process.pid}] Callback #{@name} disabled:#{disabled?}" }
|
25
25
|
return if @disabled
|
26
26
|
@block.call(*args, &block)
|
27
27
|
end
|
data/lib/sqreen/graft/hook.rb
CHANGED
@@ -21,13 +21,6 @@ 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
|
-
|
31
24
|
attr_reader :point
|
32
25
|
|
33
26
|
def initialize(hook_point, dependency_test = nil, strategy = :chain)
|
@@ -53,31 +46,27 @@ module Sqreen
|
|
53
46
|
end
|
54
47
|
|
55
48
|
def before(tag = nil, opts = {}, &block)
|
56
|
-
return @before if block.nil?
|
49
|
+
return @before.sort_by(&:rank) if block.nil?
|
57
50
|
|
58
51
|
@before << Callback.new(callback_name(:before, tag), opts, &block)
|
59
|
-
@before.sort_by!(&:rank)
|
60
52
|
end
|
61
53
|
|
62
54
|
def after(tag = nil, opts = {}, &block)
|
63
|
-
return @after if block.nil?
|
55
|
+
return @after.sort_by(&:rank) if block.nil?
|
64
56
|
|
65
57
|
@after << Callback.new(callback_name(:after, tag), opts, &block)
|
66
|
-
@after.sort_by!(&:rank)
|
67
58
|
end
|
68
59
|
|
69
60
|
def raised(tag = nil, opts = {}, &block)
|
70
|
-
return @raised if block.nil?
|
61
|
+
return @raised.sort_by(&:rank) if block.nil?
|
71
62
|
|
72
63
|
@raised << Callback.new(callback_name(:raised, tag), opts, &block)
|
73
|
-
@raised.sort_by!(&:rank)
|
74
64
|
end
|
75
65
|
|
76
66
|
def ensured(tag = nil, opts = {}, &block)
|
77
|
-
return @ensured if block.nil?
|
67
|
+
return @ensured.sort_by(&:rank) if block.nil?
|
78
68
|
|
79
69
|
@ensured << Callback.new(callback_name(:ensured, tag), opts, &block)
|
80
|
-
@ensured.sort_by!(&:rank)
|
81
70
|
end
|
82
71
|
|
83
72
|
def depends_on(&block)
|
@@ -120,25 +109,11 @@ module Sqreen
|
|
120
109
|
@before = []
|
121
110
|
@after = []
|
122
111
|
@raised = []
|
123
|
-
@ensured = []
|
124
112
|
end
|
125
113
|
|
126
114
|
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
|
-
|
138
115
|
Proc.new do |*args, &block|
|
139
|
-
|
140
|
-
|
141
|
-
if Thread.current[:sqreen_hook_entered]
|
116
|
+
if Thread.current[:sqreen_hook_entered] || Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:time_budget_expended]
|
142
117
|
if hook.point.super?
|
143
118
|
return super(*args, &block)
|
144
119
|
else
|
@@ -146,88 +121,49 @@ module Sqreen
|
|
146
121
|
end
|
147
122
|
end
|
148
123
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
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
|
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}" }
|
190
128
|
|
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
|
191
131
|
hooked_call = HookedCall.new(self, args)
|
192
132
|
|
193
133
|
begin
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
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|
|
209
149
|
hook.before.each do |c|
|
210
150
|
next if c.ignore && c.ignore.call
|
211
151
|
|
212
|
-
if
|
213
|
-
|
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
|
152
|
+
if timer && !c.mandatory
|
153
|
+
remaining = budget - timer.elapsed
|
221
154
|
unless remaining > 0
|
222
|
-
|
223
|
-
request[:time_budget_expended] = true
|
155
|
+
Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:skipped_callbacks] << c && Thread.current[:sqreen_http_request][:time_budget_expended] = true
|
224
156
|
next
|
225
157
|
end
|
226
158
|
end
|
227
159
|
|
228
160
|
flow = catch(Ball.new) do |ball|
|
229
|
-
Timer.new(c.name
|
230
|
-
|
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
|
231
167
|
end
|
232
168
|
end
|
233
169
|
|
@@ -237,59 +173,52 @@ module Sqreen
|
|
237
173
|
hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
|
238
174
|
break if flow.break?
|
239
175
|
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
|
243
176
|
end
|
244
177
|
|
245
178
|
raise hooked_call.raise if hooked_call.raising
|
246
179
|
return hooked_call.return if hooked_call.returning
|
247
180
|
ensure
|
248
181
|
Thread.current[:sqreen_hook_entered] = false
|
249
|
-
|
250
|
-
end
|
182
|
+
timer.stop if timer
|
183
|
+
end
|
251
184
|
|
252
185
|
begin
|
253
186
|
chrono.ignore do
|
254
|
-
if
|
187
|
+
if hook.point.super?
|
255
188
|
hooked_call.returned = super(*(hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed), &block)
|
256
189
|
else
|
257
190
|
hooked_call.returned = hook.point.apply(hooked_call.instance, 'sqreen_hook', *(hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed), &block)
|
258
191
|
end
|
259
192
|
end
|
260
193
|
rescue ::Exception => e # rubocop:disable Lint/RescueException
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
hooked_call.raised = e
|
194
|
+
timer.start if timer
|
195
|
+
Thread.current[:sqreen_hook_entered] = true
|
196
|
+
hooked_call.raised = e
|
265
197
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
request_elapsed = request_timer.elapsed if budget
|
198
|
+
Sqreen::Graft.logger.debug { "[#{Process.pid}] Hook #{hook.point} disabled:#{hook.disabled?} exception:#{e}" }
|
199
|
+
raise if hook.raised.empty?
|
270
200
|
|
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|
|
271
204
|
hook.raised.each do |c|
|
272
205
|
next if c.ignore && c.ignore.call
|
273
206
|
|
274
|
-
if
|
275
|
-
|
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
|
207
|
+
if timer && !c.mandatory
|
208
|
+
remaining = budget - timer.elapsed
|
283
209
|
unless remaining > 0
|
284
|
-
|
285
|
-
request[:time_budget_expended] = true
|
210
|
+
Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:skipped_callbacks] << c && Thread.current[:sqreen_http_request][:time_budget_expended] = true
|
286
211
|
next
|
287
212
|
end
|
288
213
|
end
|
289
214
|
|
290
215
|
flow = catch(Ball.new) do |ball|
|
291
|
-
Timer.new(c.name
|
292
|
-
|
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
|
293
222
|
end
|
294
223
|
end
|
295
224
|
|
@@ -299,9 +228,6 @@ module Sqreen
|
|
299
228
|
hooked_call.retrying = true if flow.retry?
|
300
229
|
break if flow.break?
|
301
230
|
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
|
305
231
|
end
|
306
232
|
|
307
233
|
retry if hooked_call.retrying
|
@@ -309,37 +235,30 @@ module Sqreen
|
|
309
235
|
return hooked_call.return if hooked_call.returning
|
310
236
|
raise
|
311
237
|
else
|
312
|
-
|
313
|
-
|
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
|
238
|
+
timer.start if timer
|
239
|
+
Thread.current[:sqreen_hook_entered] = true
|
320
240
|
|
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|
|
321
244
|
hook.after.each do |c|
|
322
245
|
next if c.ignore && c.ignore.call
|
323
246
|
|
324
|
-
if
|
325
|
-
|
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
|
247
|
+
if timer && !c.mandatory
|
248
|
+
remaining = budget - timer.elapsed
|
333
249
|
unless remaining > 0
|
334
|
-
|
335
|
-
request[:time_budget_expended] = true
|
250
|
+
Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:skipped_callbacks] << c && Thread.current[:sqreen_http_request][:time_budget_expended] = true
|
336
251
|
next
|
337
252
|
end
|
338
253
|
end
|
339
254
|
|
340
255
|
flow = catch(Ball.new) do |ball|
|
341
|
-
Timer.new(c.name
|
342
|
-
|
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
|
343
262
|
end
|
344
263
|
end
|
345
264
|
|
@@ -348,48 +267,34 @@ module Sqreen
|
|
348
267
|
hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
|
349
268
|
break if flow.break?
|
350
269
|
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
|
354
270
|
end
|
355
271
|
|
356
272
|
raise hooked_call.raise if hooked_call.raising
|
357
273
|
return hooked_call.returning ? hooked_call.return : hooked_call.returned
|
358
274
|
ensure
|
359
|
-
|
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?
|
275
|
+
# TODO: timer.start if someone has thrown?
|
370
276
|
|
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|
|
371
280
|
hook.ensured.each do |c|
|
372
281
|
next if c.ignore && c.ignore.call
|
373
282
|
|
374
|
-
if
|
375
|
-
|
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
|
283
|
+
if timer && !c.mandatory
|
284
|
+
remaining = budget - timer.elapsed
|
383
285
|
unless remaining > 0
|
384
|
-
|
385
|
-
request[:time_budget_expended] = true
|
286
|
+
Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:skipped_callbacks] << c && Thread.current[:sqreen_http_request][:time_budget_expended] = true
|
386
287
|
next
|
387
288
|
end
|
388
289
|
end
|
389
290
|
|
390
291
|
flow = catch(Ball.new) do |ball|
|
391
|
-
Timer.new(c.name
|
392
|
-
|
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
|
393
298
|
end
|
394
299
|
end
|
395
300
|
|
@@ -397,18 +302,11 @@ module Sqreen
|
|
397
302
|
hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
|
398
303
|
hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
|
399
304
|
break if flow.break?
|
400
|
-
end unless hook.
|
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
|
305
|
+
end unless hook.disabled?
|
407
306
|
end
|
408
307
|
|
409
|
-
|
410
|
-
|
411
|
-
# return hooked_call.returning ? hooked_call.return : hooked_call.returned
|
308
|
+
Thread.current[:sqreen_hook_entered] = false
|
309
|
+
timer.stop if timer
|
412
310
|
end
|
413
311
|
end # chrono
|
414
312
|
end
|