class Sqreen::Graft::Hook
Constants
- DEFAULT_STRATEGY
Attributes
point[R]
Public Class Methods
[](hook_point, strategy = DEFAULT_STRATEGY)
click to toggle source
# File lib/sqreen/graft/hook.rb, line 17 def self.[](hook_point, strategy = DEFAULT_STRATEGY) @hooks[hook_point] ||= new(hook_point, nil, strategy) end
add(hook_point, strategy = DEFAULT_STRATEGY, &block)
click to toggle source
# File lib/sqreen/graft/hook.rb, line 21 def self.add(hook_point, strategy = DEFAULT_STRATEGY, &block) self[hook_point, strategy].add(&block) end
ignore() { || ... }
click to toggle source
# File lib/sqreen/graft/hook.rb, line 25 def self.ignore Thread.current[:sqreen_hook_entered] = true yield ensure Thread.current[:sqreen_hook_entered] = false end
new(hook_point, dependency_test = nil, strategy = DEFAULT_STRATEGY)
click to toggle source
# File lib/sqreen/graft/hook.rb, line 34 def initialize(hook_point, dependency_test = nil, strategy = DEFAULT_STRATEGY) @disabled = false @point = hook_point.is_a?(HookPoint) ? hook_point : HookPoint.new(hook_point, strategy) @before = [] @after = [] @raised = [] @ensured = [] @dependency_test = dependency_test || Proc.new { point.exist? } end
wrapper(hook)
click to toggle source
Calls superclass method
# File lib/sqreen/graft/hook.ruby_2.rb, line 11 def self.wrapper(hook) timed_hooks_proc = proc do |t| if (request = Thread.current[:sqreen_http_request]) request[:timed_hooks] << t if request[:timed_level] >= 1 end end timed_callbacks_proc = proc do |t| if (request = Thread.current[:sqreen_http_request]) request[:timed_callbacks] << t if request[:timed_level] >= 1 end end Proc.new do |*args, &block| request = Thread.current[:sqreen_http_request] if Thread.current[:sqreen_hook_entered] if hook.point.super? return super(*args, &block) else return hook.point.apply(self, 'sqreen_hook', *args, &block) end end if request && request[:time_budget_expended] && (hook.before + hook.after + hook.raised + hook.ensured).none?(&:mandatory) if request[:timed_level] >= 2 begin request[:skipped_callbacks].concat(hook.before) if hook.point.super? return super(*args, &block) else return hook.point.apply(self, 'sqreen_hook', *args, &block) end rescue ::Exception # rubocop:disable Lint/RescueException request[:skipped_callbacks].concat(hook.raised) raise else request[:skipped_callbacks].concat(hook.after) ensure request[:skipped_callbacks].concat(hook.ensured) end else if hook.point.super? # rubocop:disable Style/IfInsideElse return super(*args, &block) else return hook.point.apply(self, 'sqreen_hook', *args, &block) end end end hook_point_super = hook.point.super? logger = Sqreen::Graft.logger logger_debug = Sqreen::Graft.logger.debug? Timer.new(hook.point, &timed_hooks_proc).measure do |chrono| # budget implies request # TODO: make budget depend on a generic context (currently "request") budget = request[:time_budget] if request if budget budget_threshold = request[:time_budget_threshold] budget_ratio = request[:time_budget_ratio] sqreen_timer = request[:sqreen_timer] request_timer = request[:request_timer] end hooked_call = HookedCall.new(self, args) begin begin sqreen_timer.start if budget Thread.current[:sqreen_hook_entered] = true # TODO: make Call have #ball to throw by cb # TODO: can Call be the ball? r = catch(Call.new, &c) # TODO: is catch return value a Call? a #dispatch? # TODO: make before/after/raised return a CallbackCollection << Array (or extend with module) # TODO: add CallbackCollection#each_with_call(instance, args) { |call| ... } ? # TODO: HookCall x CallbackCollection#each_with_call x Flow # TODO: TimedHookCall TimedCallbackCall # TODO: TimeBoundHookCall TimeBoundCallbackCall TimeBoundFlow? request_elapsed = request_timer.elapsed if budget hook.before.each do |c| next if c.ignore && c.ignore.call if budget && !c.mandatory sqreen_elapsed = sqreen_timer.elapsed if budget_ratio && !request[:time_budget_expended] fixed_budget = budget_threshold proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed else remaining = budget_threshold - sqreen_elapsed end unless remaining > 0 request[:skipped_callbacks] << c request[:time_budget_expended] = true next end end flow = catch(Ball.new) do |ball| Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do c.call(CallbackCall.new(c, remaining, hooked_call.instance, hooked_call.args_passed), ball) end end next unless c.flow && flow.is_a?(Flow) hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise? hooked_call.args_pass = flow.args and hooked_call.args_passing = true if flow.args? hooked_call.return = flow.return and hooked_call.returning = true if flow.return? break if flow.break? end unless hook.disabled? rescue StandardError => e Sqreen::Weave.logger.debug { "exception:#{e.class} when:before message:'#{e.message}' location:\"#{e.backtrace[0]}\"" } Sqreen::RemoteException.record(e) if Sqreen.queue end raise hooked_call.raise if hooked_call.raising return hooked_call.return if hooked_call.returning ensure Thread.current[:sqreen_hook_entered] = false sqreen_timer.stop if budget end unless hook.before.empty? begin chrono.ignore do if hook_point_super hooked_call.returned = super(*(hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed), &block) else hooked_call.returned = hook.point.apply(hooked_call.instance, 'sqreen_hook', *(hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed), &block) end end rescue ::Exception => e # rubocop:disable Lint/RescueException begin sqreen_timer.start if budget Thread.current[:sqreen_hook_entered] = true hooked_call.raised = e logger.debug { "[#{Process.pid}] Hook #{hook.point} disabled:#{hook.disabled?} exception:#{e}" } if logger_debug # TODO: early escape causes raise too early then caught by ensure which mistakenly reports legit errors to sqreen # raise if hook.raised.empty? request_elapsed = request_timer.elapsed if budget hook.raised.each do |c| next if c.ignore && c.ignore.call if budget && !c.mandatory sqreen_elapsed = sqreen_timer.elapsed if budget_ratio && !request[:time_budget_expended] fixed_budget = budget_threshold proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed else remaining = budget_threshold - sqreen_elapsed end unless remaining > 0 request[:skipped_callbacks] << c request[:time_budget_expended] = true next end end flow = catch(Ball.new) do |ball| Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do 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) end end next unless c.flow && flow.is_a?(Flow) hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise? hooked_call.return = flow.return and hooked_call.returning = true if flow.return? hooked_call.retrying = true if flow.retry? break if flow.break? end unless hook.disabled? rescue StandardError => e Sqreen::Weave.logger.debug { "exception:#{e.class} when:raised message:'#{e.message}' location:\"#{e.backtrace[0]}\"" } Sqreen::RemoteException.record(e) if Sqreen.queue end retry if hooked_call.retrying raise hooked_call.raise if hooked_call.raising return hooked_call.return if hooked_call.returning raise else begin sqreen_timer.start if budget Thread.current[:sqreen_hook_entered] = true # TODO: hooked_call.returning should be always false here? return hooked_call.returning ? hooked_call.return : hooked_call.returned if hook.after.empty? request_elapsed = request_timer.elapsed if budget hook.after.each do |c| next if c.ignore && c.ignore.call if budget && !c.mandatory sqreen_elapsed = sqreen_timer.elapsed if budget_ratio && !request[:time_budget_expended] fixed_budget = budget_threshold proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed else remaining = budget_threshold - sqreen_elapsed end unless remaining > 0 request[:skipped_callbacks] << c request[:time_budget_expended] = true next end end flow = catch(Ball.new) do |ball| Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do 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) end end next unless c.flow && flow.is_a?(Flow) hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise? hooked_call.return = flow.return and hooked_call.returning = true if flow.return? break if flow.break? end unless hook.disabled? rescue StandardError => e Sqreen::Weave.logger.debug { "exception:#{e.class} when:after message:'#{e.message}' location:\"#{e.backtrace[0]}\"" } Sqreen::RemoteException.record(e) if Sqreen.queue end raise hooked_call.raise if hooked_call.raising return hooked_call.returning ? hooked_call.return : hooked_call.returned ensure begin # TODO: sqreen_timer.start if someone has thrown? # TODO: sqreen_timer.stop at end of rescue+else? # TODO: Thread.current[:sqreen_hook_entered] = true if neither rescue nor else ie thrown? # TODO: Thread.current[:sqreen_hook_entered] = false at end of rescue+else? (risky?) # TODO: uniform early bail out? (but don't forget sqreen_hook_entered and sqreen_timer) # return hooked_call.returning ? hooked_call.return : hooked_call.returned if hook.ensured.empty? # done at either rescue or else # request_elapsed = request_timer.elapsed if budget # && !hook.ensured.empty? hook.ensured.each do |c| next if c.ignore && c.ignore.call if budget && !c.mandatory sqreen_elapsed = sqreen_timer.elapsed if budget_ratio && !request[:time_budget_expended] fixed_budget = budget_threshold proportional_budget = (request_elapsed - sqreen_elapsed) * budget_ratio remaining = (fixed_budget > proportional_budget ? fixed_budget : proportional_budget) - sqreen_elapsed else remaining = budget_threshold - sqreen_elapsed end unless remaining > 0 request[:skipped_callbacks] << c request[:time_budget_expended] = true next end end flow = catch(Ball.new) do |ball| Timer.new(c.name, &timed_callbacks_proc).measure(ignore: chrono) do 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) end end next unless c.flow && flow.is_a?(Flow) hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise? hooked_call.return = flow.return and hooked_call.returning = true if flow.return? break if flow.break? end unless hook.ensured.empty? || hook.disabled? Thread.current[:sqreen_hook_entered] = false sqreen_timer.stop if budget rescue StandardError => e Sqreen::Weave.logger.debug { "exception:#{e.class} when:ensured message:'#{e.message}' location:\"#{e.backtrace[0]}\"" } Sqreen::RemoteException.record(e) if Sqreen.queue end # TODO: should we run the following? # raise hooked_call.raise if hooked_call.raising # return hooked_call.returning ? hooked_call.return : hooked_call.returned end end # chrono end end
Public Instance Methods
add(&block)
click to toggle source
# File lib/sqreen/graft/hook.rb, line 48 def add(&block) tap { instance_eval(&block) } end
after(tag = nil, opts = {}, &block)
click to toggle source
# File lib/sqreen/graft/hook.rb, line 63 def after(tag = nil, opts = {}, &block) return @after if block.nil? @after << Callback.new(callback_name(:after, tag), opts, &block) @after.sort_by!(&:rank) end
before(tag = nil, opts = {}, &block)
click to toggle source
# File lib/sqreen/graft/hook.rb, line 56 def before(tag = nil, opts = {}, &block) return @before if block.nil? @before << Callback.new(callback_name(:before, tag), opts, &block) @before.sort_by!(&:rank) end
callback_name(whence, tag = nil)
click to toggle source
# File lib/sqreen/graft/hook.rb, line 52 def callback_name(whence, tag = nil) "#{point}@#{whence}" << (tag ? ":#{tag}" : "") end
clear()
click to toggle source
# File lib/sqreen/graft/hook.rb, line 120 def clear @before = [] @after = [] @raised = [] @ensured = [] end
dependency?()
click to toggle source
# File lib/sqreen/graft/hook.rb, line 44 def dependency? @dependency_test.call if @dependency_test end
depends_on(&block)
click to toggle source
# File lib/sqreen/graft/hook.rb, line 84 def depends_on(&block) @dependency_test = block end
disable()
click to toggle source
# File lib/sqreen/graft/hook.rb, line 92 def disable @disabled = true end
disabled?()
click to toggle source
# File lib/sqreen/graft/hook.rb, line 96 def disabled? @disabled end
enable()
click to toggle source
# File lib/sqreen/graft/hook.rb, line 88 def enable @disabled = false end
ensured(tag = nil, opts = {}, &block)
click to toggle source
# File lib/sqreen/graft/hook.rb, line 77 def ensured(tag = nil, opts = {}, &block) return @ensured if block.nil? @ensured << Callback.new(callback_name(:ensured, tag), opts, &block) @ensured.sort_by!(&:rank) end
install()
click to toggle source
# File lib/sqreen/graft/hook.rb, line 100 def install unless point.exist? Sqreen::Graft.logger.debug { "[#{Process.pid}] #{point} not found" } return end Sqreen::Graft.logger.debug { "[#{Process.pid}] Hook #{point}: installing" } point.install('sqreen_hook', &Sqreen::Graft::Hook.wrapper(self)) end
raised(tag = nil, opts = {}, &block)
click to toggle source
# File lib/sqreen/graft/hook.rb, line 70 def raised(tag = nil, opts = {}, &block) return @raised if block.nil? @raised << Callback.new(callback_name(:raised, tag), opts, &block) @raised.sort_by!(&:rank) end
uninstall()
click to toggle source
# File lib/sqreen/graft/hook.rb, line 110 def uninstall unless point.exist? Sqreen::Graft.logger.debug { "[#{Process.pid}] #{point} not found" } return end Sqreen::Graft.logger.debug { "[#{Process.pid}] Hook #{point}: uninstalling" } point.uninstall('sqreen_hook', &Sqreen::Graft::Hook.wrapper(self)) end