sqreen 1.19.3-java → 1.21.0-java
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 +5 -5
- data/CHANGELOG.md +38 -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/aggregated_metric.rb +25 -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 +11 -5
- data/lib/sqreen/deferred_logger.rb +50 -14
- data/lib/sqreen/deliveries/batch.rb +12 -2
- data/lib/sqreen/deliveries/simple.rb +4 -0
- data/lib/sqreen/deprecation.rb +38 -0
- data/lib/sqreen/ecosystem.rb +123 -0
- data/lib/sqreen/ecosystem/databases/database_connection_data.rb +23 -0
- data/lib/sqreen/ecosystem/databases/mongo.rb +39 -0
- data/lib/sqreen/ecosystem/databases/mysql.rb +54 -0
- data/lib/sqreen/ecosystem/databases/postgres.rb +51 -0
- data/lib/sqreen/ecosystem/databases/redis.rb +36 -0
- data/lib/sqreen/ecosystem/dispatch_table.rb +43 -0
- data/lib/sqreen/ecosystem/exception_reporting.rb +28 -0
- data/lib/sqreen/ecosystem/http/net_http.rb +50 -0
- data/lib/sqreen/ecosystem/http/rack_request.rb +39 -0
- data/lib/sqreen/ecosystem/loggable.rb +13 -0
- data/lib/sqreen/ecosystem/messaging/bunny.rb +61 -0
- data/lib/sqreen/ecosystem/messaging/kafka.rb +70 -0
- data/lib/sqreen/ecosystem/messaging/kinesis.rb +66 -0
- data/lib/sqreen/ecosystem/messaging/sqs.rb +68 -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/message_producer.rb +57 -0
- data/lib/sqreen/ecosystem/module_api/signal_producer.rb +24 -0
- data/lib/sqreen/ecosystem/module_api/tracing.rb +45 -0
- data/lib/sqreen/ecosystem/module_api/tracing/client_data.rb +31 -0
- data/lib/sqreen/ecosystem/module_api/tracing/consumer_data.rb +13 -0
- data/lib/sqreen/ecosystem/module_api/tracing/messaging_data.rb +35 -0
- data/lib/sqreen/ecosystem/module_api/tracing/producer_data.rb +13 -0
- data/lib/sqreen/ecosystem/module_api/tracing/server_data.rb +27 -0
- data/lib/sqreen/ecosystem/module_api/tracing_id_generation.rb +16 -0
- data/lib/sqreen/ecosystem/module_api/transaction_storage.rb +71 -0
- data/lib/sqreen/ecosystem/module_registry.rb +48 -0
- data/lib/sqreen/ecosystem/tracing/modules/client.rb +35 -0
- data/lib/sqreen/ecosystem/tracing/modules/consumer.rb +35 -0
- data/lib/sqreen/ecosystem/tracing/modules/determine_ip.rb +28 -0
- data/lib/sqreen/ecosystem/tracing/modules/producer.rb +35 -0
- data/lib/sqreen/ecosystem/tracing/modules/server.rb +30 -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_consumer.rb +56 -0
- data/lib/sqreen/ecosystem/tracing/signals/tracing_producer.rb +56 -0
- data/lib/sqreen/ecosystem/tracing/signals/tracing_server.rb +53 -0
- data/lib/sqreen/ecosystem/tracing_broker.rb +101 -0
- data/lib/sqreen/ecosystem/tracing_id_setup.rb +34 -0
- data/lib/sqreen/ecosystem/transaction_storage.rb +64 -0
- data/lib/sqreen/ecosystem/util/call_writers_from_init.rb +13 -0
- data/lib/sqreen/ecosystem_integration.rb +81 -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 +58 -0
- data/lib/sqreen/ecosystem_integration/signal_consumption.rb +35 -0
- data/lib/sqreen/endpoint_testing.rb +184 -0
- data/lib/sqreen/event.rb +7 -5
- data/lib/sqreen/events/attack.rb +23 -18
- data/lib/sqreen/events/remote_exception.rb +0 -22
- data/lib/sqreen/events/request_record.rb +15 -71
- data/lib/sqreen/frameworks/generic.rb +24 -1
- data/lib/sqreen/frameworks/rails.rb +0 -7
- data/lib/sqreen/frameworks/request_recorder.rb +15 -2
- data/lib/sqreen/graft/call.rb +85 -18
- data/lib/sqreen/graft/callback.rb +1 -1
- data/lib/sqreen/graft/hook.rb +192 -88
- data/lib/sqreen/graft/hook_point.rb +18 -11
- data/lib/sqreen/kit/signals/specialized/aggregated_metric.rb +72 -0
- data/lib/sqreen/kit/signals/specialized/attack.rb +57 -0
- data/lib/sqreen/kit/signals/specialized/binning_metric.rb +76 -0
- data/lib/sqreen/kit/signals/specialized/http_trace.rb +26 -0
- data/lib/sqreen/kit/signals/specialized/sdk_track_call.rb +50 -0
- data/lib/sqreen/kit/signals/specialized/sqreen_exception.rb +59 -0
- data/lib/sqreen/legacy/instrumentation.rb +22 -10
- data/lib/sqreen/legacy/old_event_submission_strategy.rb +228 -0
- data/lib/sqreen/legacy/waf_redactions.rb +49 -0
- 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/base.rb +3 -0
- data/lib/sqreen/metrics_store.rb +33 -12
- data/lib/sqreen/null_logger.rb +22 -0
- data/lib/sqreen/performance_notifications/binned_metrics.rb +8 -2
- data/lib/sqreen/remote_command.rb +4 -0
- data/lib/sqreen/rules.rb +12 -6
- 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 +4 -0
- data/lib/sqreen/rules/waf_cb.rb +14 -11
- data/lib/sqreen/runner.rb +122 -15
- data/lib/sqreen/sensitive_data_redactor.rb +19 -31
- data/lib/sqreen/session.rb +53 -43
- data/lib/sqreen/signals/conversions.rb +288 -0
- data/lib/sqreen/signals/http_trace_redaction.rb +111 -0
- data/lib/sqreen/signals/signals_submission_strategy.rb +78 -0
- 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 +96 -7
- data/lib/sqreen/backport.rb +0 -9
- data/lib/sqreen/backport/clock_gettime.rb +0 -74
- data/lib/sqreen/backport/original_name.rb +0 -88
- data/lib/sqreen/encoding_sanitizer.rb +0 -27
|
@@ -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,7 +218,16 @@ module Sqreen
|
|
|
209
218
|
|
|
210
219
|
# Should the agent not be starting up?
|
|
211
220
|
def prevent_startup
|
|
221
|
+
# SQREEN-880 - prevent Sqreen startup on Sidekiq workers
|
|
222
|
+
return :sidekiq_cli if defined?(Sidekiq::CLI)
|
|
223
|
+
return :delayed_job if defined?(Delayed::Command)
|
|
224
|
+
|
|
225
|
+
# Prevent Sqreen startup on rake tasks - unless this is a Sqreen test
|
|
226
|
+
run_in_test = sqreen_configuration.get(:run_in_test)
|
|
227
|
+
return :rake if !run_in_test && $0.end_with?('rake')
|
|
228
|
+
|
|
212
229
|
return :irb if $0 == 'irb'
|
|
230
|
+
|
|
213
231
|
return if sqreen_configuration.nil?
|
|
214
232
|
disable = sqreen_configuration.get(:disable)
|
|
215
233
|
return :config_disable if disable == true || disable.to_s.to_i == 1
|
|
@@ -251,8 +269,12 @@ module Sqreen
|
|
|
251
269
|
# Nota: cleanup should be performed at end of request (see clean_request)
|
|
252
270
|
def store_request(object)
|
|
253
271
|
return unless ensure_rack_loaded
|
|
272
|
+
|
|
273
|
+
rack_req = Rack::Request.new(object)
|
|
274
|
+
@req_start_cb.call(rack_req)
|
|
275
|
+
|
|
254
276
|
self.remaining_perf_budget = Sqreen.performance_budget
|
|
255
|
-
SharedStorage.set(:request,
|
|
277
|
+
SharedStorage.set(:request, rack_req)
|
|
256
278
|
SharedStorage.set(:xss_params, nil)
|
|
257
279
|
SharedStorage.set(:whitelisted, nil)
|
|
258
280
|
SharedStorage.set(:request_overtime, nil)
|
|
@@ -281,6 +303,7 @@ module Sqreen
|
|
|
281
303
|
SharedStorage.set(:xss_params, nil)
|
|
282
304
|
SharedStorage.set(:whitelisted, nil)
|
|
283
305
|
SharedStorage.set(:request_overtime, nil)
|
|
306
|
+
@req_end_cb.call
|
|
284
307
|
end
|
|
285
308
|
|
|
286
309
|
def remaining_perf_budget
|
|
@@ -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)
|
|
@@ -58,12 +58,22 @@ module Sqreen
|
|
|
58
58
|
Sqreen.log.debug { "close_request_record called. observed_items: #{observed_items}" }
|
|
59
59
|
|
|
60
60
|
clean_request_record if observed_items.nil?
|
|
61
|
-
if only_metric_observation
|
|
61
|
+
if Sqreen.features['use_signals'] || only_metric_observation
|
|
62
62
|
push_metrics(observations_queue, queue)
|
|
63
|
-
return clean_request_record
|
|
64
63
|
end
|
|
64
|
+
|
|
65
|
+
if only_metric_observation
|
|
66
|
+
clean_request_record
|
|
67
|
+
return
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# signals require request section to be present
|
|
71
|
+
payload_requests << 'request'
|
|
72
|
+
# for signals, response is optional, but the backend team wants them
|
|
73
|
+
payload_requests << 'response'
|
|
65
74
|
payload = payload_creator.payload(payload_requests)
|
|
66
75
|
payload[:observed] = observed_items
|
|
76
|
+
|
|
67
77
|
queue.push create_request_record(payload)
|
|
68
78
|
clean_request_record
|
|
69
79
|
end
|
|
@@ -79,10 +89,13 @@ module Sqreen
|
|
|
79
89
|
@redactor ||= SensitiveDataRedactor.from_config
|
|
80
90
|
end
|
|
81
91
|
|
|
92
|
+
# pushes metric observations to the observations queue
|
|
93
|
+
# and clears the list for the request record
|
|
82
94
|
def push_metrics(observations_queue, event_queue)
|
|
83
95
|
observed_items[:observations].each do |obs|
|
|
84
96
|
observations_queue.push obs
|
|
85
97
|
end
|
|
98
|
+
observed_items[:observations] = []
|
|
86
99
|
return unless observations_queue.size > MAX_OBS_QUEUE_LENGTH / 2
|
|
87
100
|
event_queue.push Sqreen::METRICS_EVENT
|
|
88
101
|
end
|
data/lib/sqreen/graft/call.rb
CHANGED
|
@@ -93,58 +93,125 @@ 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?
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def include_measurements(another_timer)
|
|
199
|
+
@blips += another_timer.instance_variable_get(:@blips)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def start_and_end
|
|
203
|
+
raise 'Not exactly two measurements recorded' unless size == 2
|
|
204
|
+
@blips
|
|
143
205
|
end
|
|
144
206
|
|
|
145
207
|
def to_s
|
|
146
208
|
"#{@tag}: time=%.03fus" % (duration * 1_000_000)
|
|
147
209
|
end
|
|
210
|
+
|
|
211
|
+
protected
|
|
212
|
+
|
|
213
|
+
attr_reader :tally
|
|
214
|
+
attr_writer :size, :tally
|
|
148
215
|
end
|
|
149
216
|
end
|
|
150
217
|
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
|
@@ -11,19 +11,28 @@ require 'sqreen/graft/hook_point'
|
|
|
11
11
|
module Sqreen
|
|
12
12
|
module Graft
|
|
13
13
|
class Hook
|
|
14
|
+
DEFAULT_STRATEGY = Sqreen::Graft::HookPoint::DEFAULT_STRATEGY
|
|
15
|
+
|
|
14
16
|
@hooks = {}
|
|
15
17
|
|
|
16
|
-
def self.[](hook_point, strategy =
|
|
18
|
+
def self.[](hook_point, strategy = DEFAULT_STRATEGY)
|
|
17
19
|
@hooks[hook_point] ||= new(hook_point, nil, strategy)
|
|
18
20
|
end
|
|
19
21
|
|
|
20
|
-
def self.add(hook_point, strategy =
|
|
22
|
+
def self.add(hook_point, strategy = DEFAULT_STRATEGY, &block)
|
|
21
23
|
self[hook_point, strategy].add(&block)
|
|
22
24
|
end
|
|
23
25
|
|
|
26
|
+
def self.ignore
|
|
27
|
+
Thread.current[:sqreen_hook_entered] = true
|
|
28
|
+
yield
|
|
29
|
+
ensure
|
|
30
|
+
Thread.current[:sqreen_hook_entered] = false
|
|
31
|
+
end
|
|
32
|
+
|
|
24
33
|
attr_reader :point
|
|
25
34
|
|
|
26
|
-
def initialize(hook_point, dependency_test = nil, strategy =
|
|
35
|
+
def initialize(hook_point, dependency_test = nil, strategy = DEFAULT_STRATEGY)
|
|
27
36
|
@disabled = false
|
|
28
37
|
@point = hook_point.is_a?(HookPoint) ? hook_point : HookPoint.new(hook_point, strategy)
|
|
29
38
|
@before = []
|
|
@@ -46,27 +55,31 @@ module Sqreen
|
|
|
46
55
|
end
|
|
47
56
|
|
|
48
57
|
def before(tag = nil, opts = {}, &block)
|
|
49
|
-
return @before
|
|
58
|
+
return @before if block.nil?
|
|
50
59
|
|
|
51
60
|
@before << Callback.new(callback_name(:before, tag), opts, &block)
|
|
61
|
+
@before.sort_by!(&:rank)
|
|
52
62
|
end
|
|
53
63
|
|
|
54
64
|
def after(tag = nil, opts = {}, &block)
|
|
55
|
-
return @after
|
|
65
|
+
return @after if block.nil?
|
|
56
66
|
|
|
57
67
|
@after << Callback.new(callback_name(:after, tag), opts, &block)
|
|
68
|
+
@after.sort_by!(&:rank)
|
|
58
69
|
end
|
|
59
70
|
|
|
60
71
|
def raised(tag = nil, opts = {}, &block)
|
|
61
|
-
return @raised
|
|
72
|
+
return @raised if block.nil?
|
|
62
73
|
|
|
63
74
|
@raised << Callback.new(callback_name(:raised, tag), opts, &block)
|
|
75
|
+
@raised.sort_by!(&:rank)
|
|
64
76
|
end
|
|
65
77
|
|
|
66
78
|
def ensured(tag = nil, opts = {}, &block)
|
|
67
|
-
return @ensured
|
|
79
|
+
return @ensured if block.nil?
|
|
68
80
|
|
|
69
81
|
@ensured << Callback.new(callback_name(:ensured, tag), opts, &block)
|
|
82
|
+
@ensured.sort_by!(&:rank)
|
|
70
83
|
end
|
|
71
84
|
|
|
72
85
|
def depends_on(&block)
|
|
@@ -109,11 +122,25 @@ module Sqreen
|
|
|
109
122
|
@before = []
|
|
110
123
|
@after = []
|
|
111
124
|
@raised = []
|
|
125
|
+
@ensured = []
|
|
112
126
|
end
|
|
113
127
|
|
|
114
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
|
+
|
|
115
140
|
Proc.new do |*args, &block|
|
|
116
|
-
|
|
141
|
+
request = Thread.current[:sqreen_http_request]
|
|
142
|
+
|
|
143
|
+
if Thread.current[:sqreen_hook_entered]
|
|
117
144
|
if hook.point.super?
|
|
118
145
|
return super(*args, &block)
|
|
119
146
|
else
|
|
@@ -121,49 +148,88 @@ module Sqreen
|
|
|
121
148
|
end
|
|
122
149
|
end
|
|
123
150
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
128
192
|
|
|
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
193
|
hooked_call = HookedCall.new(self, args)
|
|
132
194
|
|
|
133
195
|
begin
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
+
|
|
149
211
|
hook.before.each do |c|
|
|
150
212
|
next if c.ignore && c.ignore.call
|
|
151
213
|
|
|
152
|
-
if
|
|
153
|
-
|
|
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
|
|
154
223
|
unless remaining > 0
|
|
155
|
-
|
|
224
|
+
request[:skipped_callbacks] << c
|
|
225
|
+
request[:time_budget_expended] = true
|
|
156
226
|
next
|
|
157
227
|
end
|
|
158
228
|
end
|
|
159
229
|
|
|
160
230
|
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
|
|
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)
|
|
167
233
|
end
|
|
168
234
|
end
|
|
169
235
|
|
|
@@ -173,52 +239,59 @@ module Sqreen
|
|
|
173
239
|
hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
|
|
174
240
|
break if flow.break?
|
|
175
241
|
end unless hook.disabled?
|
|
242
|
+
rescue StandardError => e
|
|
243
|
+
Sqreen::Weave.logger.debug { "exception:#{e.class} message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
|
|
244
|
+
Sqreen::RemoteException.record(e) if Sqreen.queue
|
|
176
245
|
end
|
|
177
246
|
|
|
178
247
|
raise hooked_call.raise if hooked_call.raising
|
|
179
248
|
return hooked_call.return if hooked_call.returning
|
|
180
249
|
ensure
|
|
181
250
|
Thread.current[:sqreen_hook_entered] = false
|
|
182
|
-
|
|
183
|
-
end
|
|
251
|
+
sqreen_timer.stop if budget
|
|
252
|
+
end unless hook.before.empty?
|
|
184
253
|
|
|
185
254
|
begin
|
|
186
255
|
chrono.ignore do
|
|
187
|
-
if
|
|
256
|
+
if hook_point_super
|
|
188
257
|
hooked_call.returned = super(*(hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed), &block)
|
|
189
258
|
else
|
|
190
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)
|
|
191
260
|
end
|
|
192
261
|
end
|
|
193
262
|
rescue ::Exception => e # rubocop:disable Lint/RescueException
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
263
|
+
begin
|
|
264
|
+
sqreen_timer.start if budget
|
|
265
|
+
Thread.current[:sqreen_hook_entered] = true
|
|
266
|
+
hooked_call.raised = e
|
|
197
267
|
|
|
198
|
-
|
|
199
|
-
|
|
268
|
+
logger.debug { "[#{Process.pid}] Hook #{hook.point} disabled:#{hook.disabled?} exception:#{e}" } if logger_debug
|
|
269
|
+
raise if hook.raised.empty?
|
|
270
|
+
|
|
271
|
+
request_elapsed = request_timer.elapsed if budget
|
|
200
272
|
|
|
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
273
|
hook.raised.each do |c|
|
|
205
274
|
next if c.ignore && c.ignore.call
|
|
206
275
|
|
|
207
|
-
if
|
|
208
|
-
|
|
276
|
+
if budget && !c.mandatory
|
|
277
|
+
sqreen_elapsed = sqreen_timer.elapsed
|
|
278
|
+
if budget_ratio && !request[:time_budget_expended]
|
|
279
|
+
fixed_budget = budget_threshold
|
|
280
|
+
proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
|
|
281
|
+
remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
|
|
282
|
+
else
|
|
283
|
+
remaining = budget_threshold - sqreen_elapsed
|
|
284
|
+
end
|
|
209
285
|
unless remaining > 0
|
|
210
|
-
|
|
286
|
+
request[:skipped_callbacks] << c
|
|
287
|
+
request[:time_budget_expended] = true
|
|
211
288
|
next
|
|
212
289
|
end
|
|
213
290
|
end
|
|
214
291
|
|
|
215
292
|
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
|
|
293
|
+
Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
|
|
294
|
+
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
295
|
end
|
|
223
296
|
end
|
|
224
297
|
|
|
@@ -228,6 +301,9 @@ module Sqreen
|
|
|
228
301
|
hooked_call.retrying = true if flow.retry?
|
|
229
302
|
break if flow.break?
|
|
230
303
|
end unless hook.disabled?
|
|
304
|
+
rescue StandardError => e
|
|
305
|
+
Sqreen::Weave.logger.debug { "exception:#{e.class} message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
|
|
306
|
+
Sqreen::RemoteException.record(e) if Sqreen.queue
|
|
231
307
|
end
|
|
232
308
|
|
|
233
309
|
retry if hooked_call.retrying
|
|
@@ -235,30 +311,37 @@ module Sqreen
|
|
|
235
311
|
return hooked_call.return if hooked_call.returning
|
|
236
312
|
raise
|
|
237
313
|
else
|
|
238
|
-
|
|
239
|
-
|
|
314
|
+
begin
|
|
315
|
+
sqreen_timer.start if budget
|
|
316
|
+
Thread.current[:sqreen_hook_entered] = true
|
|
317
|
+
|
|
318
|
+
# TODO: hooked_call.returning should be always false here?
|
|
319
|
+
return hooked_call.returning ? hooked_call.return : hooked_call.returned if hook.after.empty?
|
|
320
|
+
|
|
321
|
+
request_elapsed = request_timer.elapsed if budget
|
|
240
322
|
|
|
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
323
|
hook.after.each do |c|
|
|
245
324
|
next if c.ignore && c.ignore.call
|
|
246
325
|
|
|
247
|
-
if
|
|
248
|
-
|
|
326
|
+
if budget && !c.mandatory
|
|
327
|
+
sqreen_elapsed = sqreen_timer.elapsed
|
|
328
|
+
if budget_ratio && !request[:time_budget_expended]
|
|
329
|
+
fixed_budget = budget_threshold
|
|
330
|
+
proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
|
|
331
|
+
remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
|
|
332
|
+
else
|
|
333
|
+
remaining = budget_threshold - sqreen_elapsed
|
|
334
|
+
end
|
|
249
335
|
unless remaining > 0
|
|
250
|
-
|
|
336
|
+
request[:skipped_callbacks] << c
|
|
337
|
+
request[:time_budget_expended] = true
|
|
251
338
|
next
|
|
252
339
|
end
|
|
253
340
|
end
|
|
254
341
|
|
|
255
342
|
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
|
|
343
|
+
Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
|
|
344
|
+
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
345
|
end
|
|
263
346
|
end
|
|
264
347
|
|
|
@@ -267,34 +350,48 @@ module Sqreen
|
|
|
267
350
|
hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
|
|
268
351
|
break if flow.break?
|
|
269
352
|
end unless hook.disabled?
|
|
353
|
+
rescue StandardError => e
|
|
354
|
+
Sqreen::Weave.logger.debug { "exception:#{e.class} message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
|
|
355
|
+
Sqreen::RemoteException.record(e) if Sqreen.queue
|
|
270
356
|
end
|
|
271
357
|
|
|
272
358
|
raise hooked_call.raise if hooked_call.raising
|
|
273
359
|
return hooked_call.returning ? hooked_call.return : hooked_call.returned
|
|
274
360
|
ensure
|
|
275
|
-
|
|
361
|
+
begin
|
|
362
|
+
# TODO: sqreen_timer.start if someone has thrown?
|
|
363
|
+
# TODO: sqreen_timer.stop at end of rescue+else?
|
|
364
|
+
# TODO: Thread.current[:sqreen_hook_entered] = true if neither rescue nor else ie thrown?
|
|
365
|
+
# TODO: Thread.current[:sqreen_hook_entered] = false at end of rescue+else? (risky?)
|
|
366
|
+
|
|
367
|
+
# TODO: uniform early bail out? (but don't forget sqreen_hook_entered and sqreen_timer)
|
|
368
|
+
# return hooked_call.returning ? hooked_call.return : hooked_call.returned if hook.ensured.empty?
|
|
369
|
+
|
|
370
|
+
# done at either rescue or else
|
|
371
|
+
# request_elapsed = request_timer.elapsed if budget # && !hook.ensured.empty?
|
|
276
372
|
|
|
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
373
|
hook.ensured.each do |c|
|
|
281
374
|
next if c.ignore && c.ignore.call
|
|
282
375
|
|
|
283
|
-
if
|
|
284
|
-
|
|
376
|
+
if budget && !c.mandatory
|
|
377
|
+
sqreen_elapsed = sqreen_timer.elapsed
|
|
378
|
+
if budget_ratio && !request[:time_budget_expended]
|
|
379
|
+
fixed_budget = budget_threshold
|
|
380
|
+
proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio
|
|
381
|
+
remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed
|
|
382
|
+
else
|
|
383
|
+
remaining = budget_threshold - sqreen_elapsed
|
|
384
|
+
end
|
|
285
385
|
unless remaining > 0
|
|
286
|
-
|
|
386
|
+
request[:skipped_callbacks] << c
|
|
387
|
+
request[:time_budget_expended] = true
|
|
287
388
|
next
|
|
288
389
|
end
|
|
289
390
|
end
|
|
290
391
|
|
|
291
392
|
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
|
|
393
|
+
Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do
|
|
394
|
+
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
395
|
end
|
|
299
396
|
end
|
|
300
397
|
|
|
@@ -302,11 +399,18 @@ module Sqreen
|
|
|
302
399
|
hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
|
|
303
400
|
hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
|
|
304
401
|
break if flow.break?
|
|
305
|
-
end unless hook.disabled?
|
|
402
|
+
end unless hook.ensured.empty? || hook.disabled?
|
|
403
|
+
|
|
404
|
+
Thread.current[:sqreen_hook_entered] = false
|
|
405
|
+
sqreen_timer.stop if budget
|
|
406
|
+
rescue StandardError => e
|
|
407
|
+
Sqreen::Weave.logger.debug { "exception:#{e.class} message:'#{e.message}' location:\"#{e.backtrace[0]}\"" }
|
|
408
|
+
Sqreen::RemoteException.record(e) if Sqreen.queue
|
|
306
409
|
end
|
|
307
410
|
|
|
308
|
-
|
|
309
|
-
|
|
411
|
+
# TODO: should we run the following?
|
|
412
|
+
# raise hooked_call.raise if hooked_call.raising
|
|
413
|
+
# return hooked_call.returning ? hooked_call.return : hooked_call.returned
|
|
310
414
|
end
|
|
311
415
|
end # chrono
|
|
312
416
|
end
|