Commit 98b90de6 authored by Stan Hu's avatar Stan Hu

Merge branch '217978-remove-instrumentation-code' into 'master'

Remove Instrumentation implementation [RUN ALL RSPEC] [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!70533
parents 3777d380 db981576
......@@ -3,128 +3,6 @@
# This file was prefixed with zz_ because we want to load it the last!
# See: https://gitlab.com/gitlab-org/gitlab-foss/issues/55611
# Autoload all classes that we want to instrument, and instrument the methods we
# need. This takes the Gitlab::Metrics::Instrumentation module as an argument so
# that we can stub it for testing, as it is only called when metrics are
# enabled.
#
# rubocop:disable Metrics/AbcSize
def instrument_classes(instrumentation)
return if ENV['STATIC_VERIFICATION']
instrumentation.instrument_instance_methods(Gitlab::Shell)
instrumentation.instrument_methods(Gitlab::Git)
Gitlab::Git.constants.each do |name|
const = Gitlab::Git.const_get(name, false)
next unless const.is_a?(Module)
instrumentation.instrument_methods(const)
instrumentation.instrument_instance_methods(const)
end
# Path to search => prefix to strip from constant
paths_to_instrument = {
%w(app finders) => %w(app finders),
%w(app mailers emails) => %w(app mailers),
# Don't instrument `app/services/concerns`
# It contains modules that are included in the services.
# The services themselves are instrumented so the methods from the modules
# are included.
%w(app services [^concerns]**) => %w(app services),
%w(lib gitlab conflicts) => ['lib'],
%w(lib gitlab email message) => ['lib'],
%w(lib gitlab checks) => ['lib']
}
paths_to_instrument.each do |(path, prefix)|
prefix = Rails.root.join(*prefix)
Dir[Rails.root.join(*path + ['*.rb'])].each do |file_path|
path = Pathname.new(file_path).relative_path_from(prefix)
const = path.to_s.sub('.rb', '').camelize.constantize
instrumentation.instrument_methods(const)
instrumentation.instrument_instance_methods(const)
end
end
instrumentation.instrument_methods(Premailer::Adapter::Nokogiri)
instrumentation.instrument_instance_methods(Premailer::Adapter::Nokogiri)
instrumentation.instrument_methods(Banzai::Renderer)
instrumentation.instrument_methods(Banzai::Querying)
instrumentation.instrument_instance_methods(Banzai::ObjectRenderer)
instrumentation.instrument_instance_methods(Banzai::ReferenceRedactor)
[Issuable, Mentionable, Participable].each do |klass|
instrumentation.instrument_instance_methods(klass)
instrumentation.instrument_instance_methods(klass::ClassMethods)
end
instrumentation.instrument_methods(Gitlab::ReferenceExtractor)
instrumentation.instrument_instance_methods(Gitlab::ReferenceExtractor)
# Instrument the classes used for checking if somebody has push access.
instrumentation.instrument_instance_methods(Gitlab::GitAccess)
instrumentation.instrument_instance_methods(Gitlab::GitAccessWiki)
instrumentation.instrument_instance_methods(API::Helpers)
instrumentation.instrument_instance_methods(RepositoryCheck::SingleRepositoryWorker)
instrumentation.instrument_instance_methods(Rouge::Formatters::HTMLGitlab)
[:XML, :HTML].each do |namespace|
namespace_mod = Nokogiri.const_get(namespace, false)
instrumentation.instrument_methods(namespace_mod)
instrumentation.instrument_methods(namespace_mod::Document)
end
instrumentation.instrument_methods(Rinku)
instrumentation.instrument_instance_methods(Repository)
instrumentation.instrument_methods(Gitlab::Highlight)
instrumentation.instrument_instance_methods(Gitlab::Highlight)
instrumentation.instrument_instance_method(Gitlab::Ci::Config::Yaml::Tags::Resolver, :to_hash)
Gitlab.ee do
instrumentation.instrument_instance_methods(Elastic::Latest::GitInstanceProxy)
instrumentation.instrument_instance_methods(Elastic::Latest::GitClassProxy)
instrumentation.instrument_instance_methods(Search::GlobalService)
instrumentation.instrument_instance_methods(Search::ProjectService)
instrumentation.instrument_instance_methods(Gitlab::Elastic::SearchResults)
instrumentation.instrument_instance_methods(Gitlab::Elastic::ProjectSearchResults)
instrumentation.instrument_instance_methods(Gitlab::Elastic::Indexer)
instrumentation.instrument_instance_methods(Gitlab::Elastic::SnippetSearchResults)
instrumentation.instrument_instance_methods(Gitlab::Elastic::Helper)
instrumentation.instrument_instance_methods(Elastic::ApplicationVersionedSearch)
instrumentation.instrument_instance_methods(Elastic::ProjectsSearch)
instrumentation.instrument_instance_methods(Elastic::RepositoriesSearch)
instrumentation.instrument_instance_methods(Elastic::SnippetsSearch)
instrumentation.instrument_instance_methods(Elastic::WikiRepositoriesSearch)
instrumentation.instrument_instance_methods(Gitlab::BitbucketImport::Importer)
instrumentation.instrument_instance_methods(Bitbucket::Connection)
instrumentation.instrument_instance_methods(Geo::RepositorySyncWorker)
end
# This is a Rails scope so we have to instrument it manually.
instrumentation.instrument_method(Project, :visible_to_user)
# Needed for https://gitlab.com/gitlab-org/gitlab-foss/issues/30224#note_32306159
instrumentation.instrument_instance_method(MergeRequestDiff, :load_commits)
end
# rubocop:enable Metrics/AbcSize
# With prometheus enabled by default this breaks all specs
# that stubs methods using `any_instance_of` for the models reloaded here.
#
......
......@@ -38,8 +38,6 @@ Array.methods.grep(/sing/)
## Find method source
Works for [non-instrumented methods](../../development/instrumentation.md#checking-instrumented-methods):
```ruby
instance_of_object.method(:foo).source_location
......
......@@ -253,8 +253,6 @@ the [reviewer values](https://about.gitlab.com/handbook/engineering/workflow/rev
## Performance guides
- [Instrumentation](instrumentation.md) for Ruby code running in production
environments.
- [Performance guidelines](performance.md) for writing code, benchmarks, and
certain patterns to avoid.
- [Caching guidelines](caching.md) for using caching in Rails under a GitLab environment.
......
---
stage: Monitor
group: Monitor
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Instrumenting Ruby code **(FREE)**
[GitLab Performance Monitoring](../administration/monitoring/performance/index.md) allows instrumenting of both methods and custom
blocks of Ruby code. Method instrumentation is the primary form of
instrumentation with block-based instrumentation only being used when we want to
drill down to specific regions of code within a method.
Please refer to [Product Intelligence](https://about.gitlab.com/handbook/product/product-intelligence-guide/) if you are tracking product usage patterns.
## Instrumenting Methods
Instrumenting methods is done by using the `Gitlab::Metrics::Instrumentation`
module. This module offers a few different methods that can be used to
instrument code:
- `instrument_method`: Instruments a single class method.
- `instrument_instance_method`: Instruments a single instance method.
- `instrument_class_hierarchy`: Given a Class, this method recursively
instruments all sub-classes (both class and instance methods).
- `instrument_methods`: Instruments all public and private class methods of a
Module.
- `instrument_instance_methods`: Instruments all public and private instance
methods of a Module.
To remove the need for typing the full `Gitlab::Metrics::Instrumentation`
namespace you can use the `configure` class method. This method simply yields
the supplied block while passing `Gitlab::Metrics::Instrumentation` as its
argument. An example:
```ruby
Gitlab::Metrics::Instrumentation.configure do |conf|
conf.instrument_method(Foo, :bar)
conf.instrument_method(Foo, :baz)
end
```
Using this method is in general preferred over directly calling the various
instrumentation methods.
Method instrumentation should be added in the initializer
`config/initializers/zz_metrics.rb`.
### Examples
Instrumenting a single method:
```ruby
Gitlab::Metrics::Instrumentation.configure do |conf|
conf.instrument_method(User, :find_by)
end
```
Instrumenting an entire class hierarchy:
```ruby
Gitlab::Metrics::Instrumentation.configure do |conf|
conf.instrument_class_hierarchy(ActiveRecord::Base)
end
```
Instrumenting all public class methods:
```ruby
Gitlab::Metrics::Instrumentation.configure do |conf|
conf.instrument_methods(User)
end
```
### Checking Instrumented Methods
The easiest way to check if a method has been instrumented is to check its
source location. For example:
```ruby
method = Banzai::Renderer.method(:render)
method.source_location
```
If the source location points to `lib/gitlab/metrics/instrumentation.rb` you
know the method has been instrumented.
If you're using Pry you can use the `$` command to display the source code of a
method (along with its source location), this is easier than running the above
Ruby code. In case of the above snippet you'd run the following:
- `$ Banzai::Renderer.render`
This prints a result similar to:
```plaintext
From: /path/to/your/gitlab/lib/gitlab/metrics/instrumentation.rb @ line 148:
Owner: #<Module:0x0055f0865c6d50>
Visibility: public
Number of lines: 21
def #{name}(#{args_signature})
if trans = Gitlab::Metrics::Instrumentation.transaction
trans.measure_method(#{label.inspect}) { super }
else
super
end
end
```
## Instrumenting Ruby Blocks
Measuring blocks of Ruby code is done by calling `Gitlab::Metrics.measure` and
passing it a block. For example:
```ruby
Gitlab::Metrics.measure(:foo) do
...
end
```
The block is executed and the execution time is stored as a set of fields in the
currently running transaction. If no transaction is present the block is yielded
without measuring anything.
Three values are measured for a block:
- The real time elapsed, stored in `NAME_real_time`.
- The CPU time elapsed, stored in `NAME_cpu_time`.
- The call count, stored in `NAME_call_count`.
Both the real and CPU timings are measured in milliseconds.
Multiple calls to the same block results in the final values being the sum
of all individual values. Take this code for example:
```ruby
3.times do
Gitlab::Metrics.measure(:sleep) do
sleep 1
end
end
```
Here, the final value of `sleep_real_time` is `3`, and not `1`.
## Tracking Custom Events
Besides instrumenting code GitLab Performance Monitoring also supports tracking
of custom events. This is primarily intended to be used for tracking business
metrics such as the number of Git pushes, repository imports, and so on.
To track a custom event simply call `Gitlab::Metrics.add_event` passing it an
event name and a custom set of (optional) tags. For example:
```ruby
Gitlab::Metrics.add_event(:user_login, email: current_user.email)
```
Event names should be verbs such as `push_repository` and `remove_branch`.
......@@ -377,16 +377,6 @@ comment. Instead of always rendering these kind of elements they should only be
rendered when actually needed. This ensures we don't spend time generating
Haml/HTML when it's not used.
## Instrumenting New Code
**Summary:** always add instrumentation for new classes, modules, and methods.
Newly added classes, modules, and methods must be instrumented. This ensures
we can track the performance of this code over time.
For more information see [Instrumentation](instrumentation.md). This guide
describes how to add instrumentation and where to add it.
## Use of Caching
**Summary:** cache data in memory or in Redis when it's needed multiple times in
......
# 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
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'instrument_classes' do
let(:config) { double(:config) }
before do
allow(config).to receive(:instrument_method)
allow(config).to receive(:instrument_methods)
allow(config).to receive(:instrument_instance_method)
allow(config).to receive(:instrument_instance_methods)
allow(Gitlab::Application).to receive(:configure)
end
it 'can autoload and instrument all files' do
require_relative '../../config/initializers/zz_metrics'
expect { instrument_classes(config) }.not_to raise_error
end
end
......@@ -22,9 +22,6 @@ RSpec.describe Gitlab::BacktraceCleaner do
"lib/gitlab/git/repository.rb:1451:in `gitaly_migrate'",
"lib/gitlab/git/commit.rb:66:in `find'",
"app/models/repository.rb:1047:in `find_commit'",
"lib/gitlab/metrics/instrumentation.rb:159:in `block in find_commit'",
"lib/gitlab/metrics/method_call.rb:36:in `measure'",
"lib/gitlab/metrics/instrumentation.rb:159:in `find_commit'",
"app/models/repository.rb:113:in `commit'",
"lib/gitlab/i18n.rb:50:in `with_locale'",
"lib/gitlab/middleware/multipart.rb:95:in `call'",
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Metrics::Instrumentation do
let(:env) { {} }
let(:transaction) { Gitlab::Metrics::WebTransaction.new(env) }
before do
@dummy = Class.new do
def self.foo(text = 'foo')
text
end
def self.wat(text = 'wat')
text
end
private_class_method :wat
class << self
def buzz(text = 'buzz')
text
end
private :buzz
def flaky(text = 'flaky')
text
end
protected :flaky
end
def bar(text = 'bar')
text
end
def wadus(text = 'wadus')
text
end
private :wadus
def chaf(text = 'chaf')
text
end
protected :chaf
end
allow(@dummy).to receive(:name).and_return('Dummy')
end
describe '.series' do
it 'returns a String' do
expect(described_class.series).to be_an_instance_of(String)
end
end
describe '.configure' do
it 'yields self' do
described_class.configure do |c|
expect(c).to eq(described_class)
end
end
end
describe '.instrument_method' do
describe 'with metrics enabled' do
before do
allow(Gitlab::Metrics).to receive(:enabled?).and_return(true)
described_class.instrument_method(@dummy, :foo)
end
it 'instruments the Class' do
target = @dummy.singleton_class
expect(described_class.instrumented?(target)).to eq(true)
end
it 'defines a proxy method' do
mod = described_class.proxy_module(@dummy.singleton_class)
expect(mod.method_defined?(:foo)).to eq(true)
end
it 'calls the instrumented method with the correct arguments' do
expect(@dummy.foo).to eq('foo')
end
it 'tracks the call duration upon calling the method' do
allow(Gitlab::Metrics).to receive(:method_call_threshold)
.and_return(0)
allow(described_class).to receive(:transaction)
.and_return(transaction)
expect_next_instance_of(Gitlab::Metrics::MethodCall) do |instance|
expect(instance).to receive(:measure)
end
@dummy.foo
end
it 'does not track method calls below a given duration threshold' do
allow(Gitlab::Metrics).to receive(:method_call_threshold)
.and_return(100)
expect(transaction).not_to receive(:add_metric)
@dummy.foo
end
it 'generates a method with the correct arity when using methods without arguments' do
dummy = Class.new do
def self.test; end
end
described_class.instrument_method(dummy, :test)
expect(dummy.method(:test).arity).to eq(0)
end
describe 'when a module is instrumented multiple times' do
it 'calls the instrumented method with the correct arguments' do
described_class.instrument_method(@dummy, :foo)
expect(@dummy.foo).to eq('foo')
end
end
end
describe 'with metrics disabled' do
before do
allow(Gitlab::Metrics).to receive(:enabled?).and_return(false)
end
it 'does not instrument the method' do
described_class.instrument_method(@dummy, :foo)
target = @dummy.singleton_class
expect(described_class.instrumented?(target)).to eq(false)
end
end
end
describe '.instrument_instance_method' do
describe 'with metrics enabled' do
before do
allow(Gitlab::Metrics).to receive(:enabled?).and_return(true)
described_class
.instrument_instance_method(@dummy, :bar)
end
it 'instruments instances of the Class' do
expect(described_class.instrumented?(@dummy)).to eq(true)
end
it 'defines a proxy method' do
mod = described_class.proxy_module(@dummy)
expect(mod.method_defined?(:bar)).to eq(true)
end
it 'calls the instrumented method with the correct arguments' do
expect(@dummy.new.bar).to eq('bar')
end
it 'tracks the call duration upon calling the method' do
allow(Gitlab::Metrics).to receive(:method_call_threshold)
.and_return(0)
allow(described_class).to receive(:transaction)
.and_return(transaction)
expect_next_instance_of(Gitlab::Metrics::MethodCall) do |instance|
expect(instance).to receive(:measure)
end
@dummy.new.bar
end
it 'does not track method calls below a given duration threshold' do
allow(Gitlab::Metrics).to receive(:method_call_threshold)
.and_return(100)
expect(transaction).not_to receive(:add_metric)
@dummy.new.bar
end
end
describe 'with metrics disabled' do
before do
allow(Gitlab::Metrics).to receive(:enabled?).and_return(false)
end
it 'does not instrument the method' do
described_class
.instrument_instance_method(@dummy, :bar)
expect(described_class.instrumented?(@dummy)).to eq(false)
end
end
end
describe '.instrument_class_hierarchy' do
before do
allow(Gitlab::Metrics).to receive(:enabled?).and_return(true)
@child1 = Class.new(@dummy) do
def self.child1_foo; end
def child1_bar; end
end
@child2 = Class.new(@child1) do
def self.child2_foo; end
def child2_bar; end
end
end
it 'recursively instruments a class hierarchy' do
described_class.instrument_class_hierarchy(@dummy)
expect(described_class.instrumented?(@child1.singleton_class)).to eq(true)
expect(described_class.instrumented?(@child2.singleton_class)).to eq(true)
expect(described_class.instrumented?(@child1)).to eq(true)
expect(described_class.instrumented?(@child2)).to eq(true)
end
it 'does not instrument the root module' do
described_class.instrument_class_hierarchy(@dummy)
expect(described_class.instrumented?(@dummy)).to eq(false)
end
end
describe '.instrument_methods' do
before do
allow(Gitlab::Metrics).to receive(:enabled?).and_return(true)
end
it 'instruments all public class methods' do
described_class.instrument_methods(@dummy)
expect(described_class.instrumented?(@dummy.singleton_class)).to eq(true)
expect(@dummy.method(:foo).source_location.first).to match(/instrumentation\.rb/)
expect(@dummy.public_methods).to include(:foo)
end
it 'instruments all protected class methods' do
described_class.instrument_methods(@dummy)
expect(described_class.instrumented?(@dummy.singleton_class)).to eq(true)
expect(@dummy.method(:flaky).source_location.first).to match(/instrumentation\.rb/)
expect(@dummy.protected_methods).to include(:flaky)
end
it 'instruments all private class methods' do
described_class.instrument_methods(@dummy)
expect(described_class.instrumented?(@dummy.singleton_class)).to eq(true)
expect(@dummy.method(:buzz).source_location.first).to match(/instrumentation\.rb/)
expect(@dummy.private_methods).to include(:buzz)
expect(@dummy.private_methods).to include(:wat)
end
it 'only instruments methods directly defined in the module' do
mod = Module.new do
def kittens
end
end
@dummy.extend(mod)
described_class.instrument_methods(@dummy)
expect(@dummy).not_to respond_to(:_original_kittens)
end
it 'can take a block to determine if a method should be instrumented' do
described_class.instrument_methods(@dummy) do
false
end
expect(@dummy).not_to respond_to(:_original_foo)
end
end
describe '.instrument_instance_methods' do
before do
allow(Gitlab::Metrics).to receive(:enabled?).and_return(true)
end
it 'instruments all public instance methods' do
described_class.instrument_instance_methods(@dummy)
expect(described_class.instrumented?(@dummy)).to eq(true)
expect(@dummy.new.method(:bar).source_location.first).to match(/instrumentation\.rb/)
expect(@dummy.public_instance_methods).to include(:bar)
end
it 'instruments all protected instance methods' do
described_class.instrument_instance_methods(@dummy)
expect(described_class.instrumented?(@dummy)).to eq(true)
expect(@dummy.new.method(:chaf).source_location.first).to match(/instrumentation\.rb/)
expect(@dummy.protected_instance_methods).to include(:chaf)
end
it 'instruments all private instance methods' do
described_class.instrument_instance_methods(@dummy)
expect(described_class.instrumented?(@dummy)).to eq(true)
expect(@dummy.new.method(:wadus).source_location.first).to match(/instrumentation\.rb/)
expect(@dummy.private_instance_methods).to include(:wadus)
end
it 'only instruments methods directly defined in the module' do
mod = Module.new do
def kittens
end
end
@dummy.include(mod)
described_class.instrument_instance_methods(@dummy)
expect(@dummy.new.method(:kittens).source_location.first).not_to match(/instrumentation\.rb/)
end
it 'can take a block to determine if a method should be instrumented' do
described_class.instrument_instance_methods(@dummy) do
false
end
expect(@dummy.new.method(:bar).source_location.first).not_to match(/instrumentation\.rb/)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment