Module: Gitlab::Metrics::Instrumentation
- Defined in:
- lib/gitlab/metrics/instrumentation.rb
Overview
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)
Constant Summary collapse
- PROXY_IVAR =
:@__gitlab_instrumentation_proxy
Class Method Summary collapse
- .configure {|_self| ... } ⇒ Object
-
.instrument(type, mod, name) ⇒ Object
Instruments a method.
-
.instrument_class_hierarchy(root, &block) ⇒ Object
Recursively instruments all subclasses of the given root module.
-
.instrument_instance_method(mod, name) ⇒ Object
Instruments an instance method.
-
.instrument_instance_methods(mod) ⇒ Object
Instruments all public and private instance methods of a module.
-
.instrument_method(mod, name) ⇒ Object
Instruments a class method.
-
.instrument_methods(mod) ⇒ Object
Instruments all public and private methods of a module.
-
.instrumented?(mod) ⇒ Boolean
Returns true if a module is instrumented.
-
.proxy_module(mod) ⇒ Object
Returns the proxy module (if any) of `mod`.
-
.series ⇒ Object
Returns the name of the series to use for storing method calls.
-
.transaction ⇒ Object
Small layer of indirection to make it easier to stub out the current transaction.
Class Method Details
.configure {|_self| ... } ⇒ Object
16 17 18 |
# File 'lib/gitlab/metrics/instrumentation.rb', line 16 def self.configure yield self end |
.instrument(type, mod, name) ⇒ Object
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.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/gitlab/metrics/instrumentation.rb', line 120 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) 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 #{method_visibility} :#{name} EOF target.prepend(proxy_module) end |
.instrument_class_hierarchy(root, &block) ⇒ Object
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.
51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/gitlab/metrics/instrumentation.rb', line 51 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 |
.instrument_instance_method(mod, name) ⇒ Object
Instruments an instance method.
mod - The module to instrument as a Module/Class. name - The name of the method to instrument.
37 38 39 |
# File 'lib/gitlab/metrics/instrumentation.rb', line 37 def self.instrument_instance_method(mod, name) instrument(:instance, mod, name) end |
.instrument_instance_methods(mod) ⇒ Object
Instruments all public and private instance methods of a module.
See `instrument_methods` for more information.
mod - The module to instrument.
90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/gitlab/metrics/instrumentation.rb', line 90 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 |
.instrument_method(mod, name) ⇒ Object
Instruments a class method.
mod - The module to instrument as a Module/Class. name - The name of the method to instrument.
29 30 31 |
# File 'lib/gitlab/metrics/instrumentation.rb', line 29 def self.instrument_method(mod, name) instrument(:class, mod, name) end |
.instrument_methods(mod) ⇒ Object
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.
72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/gitlab/metrics/instrumentation.rb', line 72 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 |
.instrumented?(mod) ⇒ Boolean
Returns true if a module is instrumented.
mod - The module to check
106 107 108 |
# File 'lib/gitlab/metrics/instrumentation.rb', line 106 def self.instrumented?(mod) mod.instance_variable_defined?(PROXY_IVAR) end |
.proxy_module(mod) ⇒ Object
Returns the proxy module (if any) of `mod`.
111 112 113 |
# File 'lib/gitlab/metrics/instrumentation.rb', line 111 def self.proxy_module(mod) mod.instance_variable_get(PROXY_IVAR) end |
.series ⇒ Object
Returns the name of the series to use for storing method calls.
21 22 23 |
# File 'lib/gitlab/metrics/instrumentation.rb', line 21 def self.series @series ||= "#{::Gitlab::Metrics.series_prefix}method_calls" end |
.transaction ⇒ Object
Small layer of indirection to make it easier to stub out the current transaction.
184 185 186 |
# File 'lib/gitlab/metrics/instrumentation.rb', line 184 def self.transaction Transaction.current end |