diff options
Diffstat (limited to 'lib/gitlab/metrics/instrumentation.rb')
-rw-r--r-- | lib/gitlab/metrics/instrumentation.rb | 194 |
1 files changed, 0 insertions, 194 deletions
diff --git a/lib/gitlab/metrics/instrumentation.rb b/lib/gitlab/metrics/instrumentation.rb deleted file mode 100644 index ad45a037161..00000000000 --- a/lib/gitlab/metrics/instrumentation.rb +++ /dev/null @@ -1,194 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Metrics - # Module for instrumenting methods. - # - # This module allows instrumenting of methods without having to actually - # alter the target code (e.g. by including modules). - # - # Example usage: - # - # Gitlab::Metrics::Instrumentation.instrument_method(User, :by_login) - module Instrumentation - PROXY_IVAR = :@__gitlab_instrumentation_proxy - - def self.configure - yield self - end - - # Returns the name of the series to use for storing method calls. - def self.series - @series ||= "#{::Gitlab::Metrics.series_prefix}method_calls" - end - - # Instruments a class method. - # - # mod - The module to instrument as a Module/Class. - # name - The name of the method to instrument. - def self.instrument_method(mod, name) - instrument(:class, mod, name) - end - - # Instruments an instance method. - # - # mod - The module to instrument as a Module/Class. - # name - The name of the method to instrument. - def self.instrument_instance_method(mod, name) - instrument(:instance, mod, name) - end - - # Recursively instruments all subclasses of the given root module. - # - # This can be used to for example instrument all ActiveRecord models (as - # these all inherit from ActiveRecord::Base). - # - # This method can optionally take a block to pass to `instrument_methods` - # and `instrument_instance_methods`. - # - # root - The root module for which to instrument subclasses. The root - # module itself is not instrumented. - def self.instrument_class_hierarchy(root, &block) - visit = root.subclasses - - until visit.empty? - klass = visit.pop - - instrument_methods(klass, &block) - instrument_instance_methods(klass, &block) - - klass.subclasses.each { |c| visit << c } - end - end - - # Instruments all public and private methods of a module. - # - # This method optionally takes a block that can be used to determine if a - # method should be instrumented or not. The block is passed the receiving - # module and an UnboundMethod. If the block returns a non truthy value the - # method is not instrumented. - # - # mod - The module to instrument. - def self.instrument_methods(mod) - methods = mod.methods(false) + mod.private_methods(false) - methods.each do |name| - method = mod.method(name) - - if method.owner == mod.singleton_class - if !block_given? || block_given? && yield(mod, method) - instrument_method(mod, name) - end - end - end - end - - # Instruments all public and private instance methods of a module. - # - # See `instrument_methods` for more information. - # - # mod - The module to instrument. - def self.instrument_instance_methods(mod) - methods = mod.instance_methods(false) + mod.private_instance_methods(false) - methods.each do |name| - method = mod.instance_method(name) - - if method.owner == mod - if !block_given? || block_given? && yield(mod, method) - instrument_instance_method(mod, name) - end - end - end - end - - # Returns true if a module is instrumented. - # - # mod - The module to check - def self.instrumented?(mod) - mod.instance_variable_defined?(PROXY_IVAR) - end - - # Returns the proxy module (if any) of `mod`. - def self.proxy_module(mod) - mod.instance_variable_get(PROXY_IVAR) - end - - # Instruments a method. - # - # type - The type (:class or :instance) of method to instrument. - # mod - The module containing the method. - # name - The name of the method to instrument. - def self.instrument(type, mod, name) - return unless ::Gitlab::Metrics.enabled? - - if type == :instance - target = mod - method_name = "##{name}" - method = mod.instance_method(name) - else - target = mod.singleton_class - method_name = ".#{name}" - method = mod.method(name) - end - - label = "#{mod.name}#{method_name}" - - unless instrumented?(target) - target.instance_variable_set(PROXY_IVAR, Module.new) - end - - proxy_module = self.proxy_module(target) - - # Some code out there (e.g. the "state_machine" Gem) checks the arity of - # a method to make sure it only passes arguments when the method expects - # any. If we were to always overwrite a method to take an `*args` - # signature this would break things. As a result we'll make sure the - # generated method _only_ accepts regular arguments if the underlying - # method also accepts them. - args_signature = - if method.arity == 0 - '' - else - '*args' - end - - method_visibility = method_visibility_for(target, name) - - # We silence warnings to avoid such warnings: - # `Skipping set of ruby2_keywords flag for <...> - # (method accepts keywords or method does not accept argument splat)` - # as we apply ruby2_keywords 'blindly' for every instrumented method. - proxy_module.class_eval <<-EOF, __FILE__, __LINE__ + 1 - def #{name}(#{args_signature}) - if trans = Gitlab::Metrics::Instrumentation.transaction - trans.method_call_for(#{label.to_sym.inspect}, #{mod.name.inspect}, "#{method_name}") - .measure { super } - else - super - end - end - silence_warnings { ruby2_keywords(:#{name}) if respond_to?(:ruby2_keywords, true) } - #{method_visibility} :#{name} - EOF - - target.prepend(proxy_module) - end - - def self.method_visibility_for(mod, name) - if mod.private_method_defined?(name) - :private - elsif mod.protected_method_defined?(name) - :protected - else - :public - end - end - private_class_method :method_visibility_for - - # Small layer of indirection to make it easier to stub out the current - # transaction. - def self.transaction - Transaction.current - end - end - end -end |