Merge branch 'document-adding-instrumentation' into 'master'
Added documentation on how to instrument methods This will hopefully remove me as a single point of failure when it comes to adding instrumentation. cc @axil @rspeicher See merge request !4035
This commit is contained in:
commit
898f613072
1 changed files with 123 additions and 6 deletions
|
@ -1,12 +1,125 @@
|
||||||
# Instrumenting Ruby Code
|
# Instrumenting Ruby Code
|
||||||
|
|
||||||
GitLab Performance Monitoring allows instrumenting of custom blocks of Ruby
|
GitLab Performance Monitoring allows instrumenting of both methods and custom
|
||||||
code. This can be used to measure the time spent in a specific part of a larger
|
blocks of Ruby code. Method instrumentation is the primary form of
|
||||||
chunk of code. The resulting data is stored as a field in the transaction that
|
instrumentation with block-based instrumentation only being used when we want to
|
||||||
executed the block.
|
drill down to specific regions of code within a method.
|
||||||
|
|
||||||
To start measuring a block of Ruby code you should use `Gitlab::Metrics.measure`
|
## Instrumenting Methods
|
||||||
and give it a name:
|
|
||||||
|
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 will recursively
|
||||||
|
instrument all sub-classes (both class and instance methods).
|
||||||
|
* `instrument_methods`: instruments all public class methods of a Module.
|
||||||
|
* `instrument_instance_methods`: instruments all public 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:
|
||||||
|
|
||||||
|
```
|
||||||
|
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/metrics.rb`.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
Instrumenting a single method:
|
||||||
|
|
||||||
|
```
|
||||||
|
Gitlab::Metrics::Instrumentation.configure do |conf|
|
||||||
|
conf.instrument_method(User, :find_by)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Instrumenting an entire class hierarchy:
|
||||||
|
|
||||||
|
```
|
||||||
|
Gitlab::Metrics::Instrumentation.configure do |conf|
|
||||||
|
conf.instrument_class_hierarchy(ActiveRecord::Base)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Instrumenting all public class methods:
|
||||||
|
|
||||||
|
```
|
||||||
|
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:
|
||||||
|
|
||||||
|
```
|
||||||
|
method = Rugged::TagCollection.instance_method(:[])
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ Rugged::TagCollection#[]
|
||||||
|
```
|
||||||
|
|
||||||
|
This will print out something along the lines of:
|
||||||
|
|
||||||
|
```
|
||||||
|
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})
|
||||||
|
trans = Gitlab::Metrics::Instrumentation.transaction
|
||||||
|
|
||||||
|
if trans
|
||||||
|
start = Time.now
|
||||||
|
retval = super
|
||||||
|
duration = (Time.now - start) * 1000.0
|
||||||
|
|
||||||
|
if duration >= Gitlab::Metrics.method_call_threshold
|
||||||
|
trans.increment(:method_duration, duration)
|
||||||
|
|
||||||
|
trans.add_metric(Gitlab::Metrics::Instrumentation::SERIES,
|
||||||
|
{ duration: duration },
|
||||||
|
method: #{label.inspect})
|
||||||
|
end
|
||||||
|
|
||||||
|
retval
|
||||||
|
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
|
```ruby
|
||||||
Gitlab::Metrics.measure(:foo) do
|
Gitlab::Metrics.measure(:foo) do
|
||||||
|
@ -14,6 +127,10 @@ Gitlab::Metrics.measure(:foo) do
|
||||||
end
|
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.
|
||||||
|
|
||||||
3 values are measured for a block:
|
3 values are measured for a block:
|
||||||
|
|
||||||
1. The real time elapsed, stored in NAME_real_time.
|
1. The real time elapsed, stored in NAME_real_time.
|
||||||
|
|
Loading…
Reference in a new issue