timeasure 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +1 -0
- data/.rubocop.yml +11 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +207 -0
- data/Rakefile +2 -0
- data/lib/timeasure.rb +69 -0
- data/lib/timeasure/class_methods.rb +33 -0
- data/lib/timeasure/configuration.rb +42 -0
- data/lib/timeasure/measurement.rb +26 -0
- data/lib/timeasure/profiling/manager.rb +39 -0
- data/lib/timeasure/profiling/reported_method.rb +27 -0
- data/lib/timeasure/profiling/reported_methods_handler.rb +30 -0
- data/lib/timeasure/version.rb +3 -0
- data/timeasure.gemspec +27 -0
- metadata +121 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 284614263b2c5c4343831c8d79e391c869009490
|
4
|
+
data.tar.gz: 72aa7b99c6eaeef54890c1c12d66679be73895f1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 175a8df40f6cf4a2ef58b11375b6b83da05499d960ed67055812fae8ebb9337b5d76834a432efc781d6ca8afc43f2fe816011538047755746bffb1ca448e853d
|
7
|
+
data.tar.gz: 20b2c718f96d7d0f8057cb086dc0cdab8fce80b1933fe9b7c0f68dddfa9606244b99cf57311b89976bac98250c8bd15652f832aba2ceb4317cd2e7d7c29dc5e5
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
.idea/*
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/.rubocop.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2017 Eliav Lavi
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
# Timeasure
|
2
|
+
|
3
|
+
**What Is It?**
|
4
|
+
|
5
|
+
Timeasure is a transparent method-level wrapper for profiling purposes developed by [Eliav Lavi](http://www.eliavlavi.com) & [Riskified](https://www.riskified.com/)).
|
6
|
+
|
7
|
+
Timeasure is a Ruby gem that allows measuring the runtime of methods in production environments
|
8
|
+
without having to alter the code of the methods themselves.
|
9
|
+
|
10
|
+
Timeasure allows you to declare tracked methods to be measured transparently upon each call.
|
11
|
+
Measured calls are then reported to Timeasure's Profiler, which aggregates the measurements on the method level.
|
12
|
+
This part is configurable and if you wish you can report measurements to another profiler of your choice.
|
13
|
+
|
14
|
+
**Why Use It?**
|
15
|
+
|
16
|
+
Timeasure was created in order to serve as an easy-to-use, self-contained framework for method-level profiling
|
17
|
+
that is safe to use in production. Testing runtime in non-production environments is helpful, but there is
|
18
|
+
great value to the knowledge gained by measuring what really goes on at real time.
|
19
|
+
|
20
|
+
**What To Do With the Data?**
|
21
|
+
|
22
|
+
The imagined usage of measured methods timing is to aggregate it along a certain transaction and report it to a live
|
23
|
+
BI service such as [NewRelic Insights](https://newrelic.com/insights) or [Keen.io](https://keen.io/);
|
24
|
+
however, different usages might prove helpful as well, such as writing the data to a database or a file.
|
25
|
+
|
26
|
+
**Disclaimers**
|
27
|
+
|
28
|
+
Timeasure uses minimal intervention in the Ruby Object Model for tracked modules and classes.
|
29
|
+
It integrates well within Rails and non-Rails apps.
|
30
|
+
|
31
|
+
Timeasure is inspired by [Metaprogramming Ruby 2](https://pragprog.com/book/ppmetr2/metaprogramming-ruby-2)
|
32
|
+
by [Paolo Perrotta](https://twitter.com/nusco)
|
33
|
+
and by [this](https://hashrocket.com/blog/posts/module-prepend-a-super-story) blog post by Hashrocket.
|
34
|
+
|
35
|
+
## Requirements
|
36
|
+
|
37
|
+
Ruby 2.0 or a later version is mandatory. (Timeasure uses `Module#prepend` introduced in Ruby 2.0.)
|
38
|
+
|
39
|
+
## Installation
|
40
|
+
|
41
|
+
Add this line to your application's Gemfile:
|
42
|
+
|
43
|
+
gem 'timeasure'
|
44
|
+
|
45
|
+
And then execute:
|
46
|
+
|
47
|
+
$ bundle
|
48
|
+
|
49
|
+
Or install it yourself as:
|
50
|
+
|
51
|
+
$ gem install timeasure
|
52
|
+
|
53
|
+
## Usage
|
54
|
+
#### 1. Include Timeasure in Modules and Classes
|
55
|
+
Simply include the Timeasure module in any class or module and declare the desired methods to track:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
class Foo
|
59
|
+
include Timeasure
|
60
|
+
tracked_class_methods :bar
|
61
|
+
tracked_instance_methods :baz, :qux
|
62
|
+
|
63
|
+
def self.bar
|
64
|
+
# some class-level stuff that can benefit from measuring runtime...
|
65
|
+
end
|
66
|
+
|
67
|
+
def baz
|
68
|
+
# some instance-level stuff that can benefit from measuring runtime...
|
69
|
+
end
|
70
|
+
|
71
|
+
def qux
|
72
|
+
# some other instance-level stuff that can benefit from measuring runtime...
|
73
|
+
end
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
#### 2. Define the Boundaries of the Tracked Transaction
|
78
|
+
**Preparing for Method Tracking**
|
79
|
+
|
80
|
+
The user is responsible for managing the final reporting and the clean-up of the aggregated data after each transation.
|
81
|
+
It is recommended to prepare the profiler at the beginning of a transaction in which tracked methods exist with
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
Timeasure::Profiling::Manager.prepare
|
85
|
+
```
|
86
|
+
and to re-prepare it again at the end of it in order to ensure a "clean slate" -
|
87
|
+
after you have handled the aggregated data in some way.
|
88
|
+
|
89
|
+
**Getting Hold of the Data**
|
90
|
+
|
91
|
+
In order to get hold of the reported methods data, use
|
92
|
+
```ruby
|
93
|
+
Timeasure::Profiling::Manager.export
|
94
|
+
````
|
95
|
+
This will return an array of `ReportedMethod`s. Each `ReportedMethod` object holds the aggregated timing data per
|
96
|
+
each tracked method call. This means that no matter how many times you call a tracked method, Timeasure's Profiler will
|
97
|
+
still hold a single `ReportedMethod` object to represent it.
|
98
|
+
|
99
|
+
`ReportedMethod` allows reading the following attributes:
|
100
|
+
* `klass_name`: Name of the class in which the tracked method resides.
|
101
|
+
* `method_name`: Name of the tracked method.
|
102
|
+
* `segment`: See [Segmented Method Tracking](#segmented-method-tracking) below.
|
103
|
+
* `metadata`: See [Carrying Metadata](#carrying-metadata) below.
|
104
|
+
* `method_path`: `klass_name` and `method_name` concatenated.
|
105
|
+
* `full_path`: Same as `method_path` unless segmentation is declared,
|
106
|
+
in which case the segment will be concatenated to the string as well. See [Segmented Method Tracking](#segmented-method-tracking) below.
|
107
|
+
* `runtime_sum`: The aggregated time it took the reported method in question to run across all calls.
|
108
|
+
* `call_count`: The times the reported method in question was called across all calls.
|
109
|
+
|
110
|
+
|
111
|
+
## Advanced Usage
|
112
|
+
|
113
|
+
#### Segmented Method Tracking
|
114
|
+
Timeasure was designed to separate regular code from its time measurement declaration.
|
115
|
+
This is achieved by Timeasure's class macros `tracked_class_methods` and `tracked_instance_methods`.
|
116
|
+
Sometimes, however, the need for additional data might arise. Imagine this method:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
class Foo
|
120
|
+
def bar(baz)
|
121
|
+
# some stuff that can benefit from measuring runtime
|
122
|
+
# yet its runtime is also highly affected by the value of baz...
|
123
|
+
end
|
124
|
+
end
|
125
|
+
```
|
126
|
+
|
127
|
+
We've seen how Timeasure makes it easy to measure the `bar` method.
|
128
|
+
However, if we wish to segment each call by the value of `baz`,
|
129
|
+
we may use Timeasure's direct interface and send this value as a **segment**:
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
class Foo
|
133
|
+
def bar(baz)
|
134
|
+
Timeasure.measure(klass_name: 'Foo', method_name: 'bar', segment: { baz: baz }) do
|
135
|
+
# the code to be measured
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
141
|
+
For such calls, Timeasure's Profiler will aggregate the data in `ReportedMethod` objects grouped by
|
142
|
+
class, method and segment.
|
143
|
+
|
144
|
+
This approach obviously violates Timeasure's idea of separating code and measurement-declaration,
|
145
|
+
but it allows for much more detailed investigations, if needed.
|
146
|
+
This will result in different `ReportedMethod` object in Timeasure's Profiler for
|
147
|
+
each combination of class, method and segment. Accordingly, such `ReportedMethod` object will include
|
148
|
+
these three elements, concatenated, as the value for `ReportedMethod#full_path`.
|
149
|
+
|
150
|
+
#### Carrying Metadata
|
151
|
+
This feature was developed in order to complement the segmented method tracking.
|
152
|
+
|
153
|
+
Sometimes carrying data with measurement that does not define a segment might be needed.
|
154
|
+
For example, assuming we save all our `ReportedMethod`s to some table called `reported_methods`,
|
155
|
+
we might want to supply a custom table name for specific measurements.
|
156
|
+
This might be achieved by using `metadata`:
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
class Foo
|
160
|
+
def bar
|
161
|
+
Timeasure.measure(klass_name: 'Foo', method_name: 'bar', metadata: { table_name: 'my_custom_table' }) do
|
162
|
+
# the code to be measured
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
```
|
167
|
+
|
168
|
+
Unlike Segments, Timeasure only carries the Metadata onwards.
|
169
|
+
It is up to the user to make use of this data, probably after calling `Timeasure::Profiling::Manager.export`.
|
170
|
+
|
171
|
+
## Notes
|
172
|
+
|
173
|
+
#### Compatibility with RSpec
|
174
|
+
|
175
|
+
If you run your test suite with Timeasure installed and modules, classes and methods tracked and all works fine - hurray!
|
176
|
+
However, due to the mechanics of Timeasure - namely, its usage of prepended modules - there exist a problem with
|
177
|
+
**stubbing** Timeasure-tracked method (RSpec does not support stubbing methods that appear in a prepended module).
|
178
|
+
To be accurate, that means that if you are tracking method `#foo`, you can not
|
179
|
+
declare something like `allow(bar).to receive(:foo).and_return(bar)`. Your specs will refuse to run in this case.
|
180
|
+
To solve that problem you can configure Timeasure's `enable_timeasure_proc` **not** to run under certain conditions.
|
181
|
+
|
182
|
+
If you are on Rails, add the following as a Rails initializer:
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
require 'timeasure'
|
186
|
+
|
187
|
+
Timeasure.configure do |configuration|
|
188
|
+
configuration.enable_timeasure_proc = lambda { !Rails.env.test? }
|
189
|
+
end
|
190
|
+
```
|
191
|
+
|
192
|
+
Timeasure will not come into action if the expression in the block evaluates to `false`.
|
193
|
+
By default this block evaluates to `true`.
|
194
|
+
|
195
|
+
|
196
|
+
## Feature Requests
|
197
|
+
|
198
|
+
Timeasure is open for changes and requests!
|
199
|
+
If you have an idea, a question or some need, feel free to contact me here or at eliavlavi@gmail.com.
|
200
|
+
|
201
|
+
## Contributing
|
202
|
+
|
203
|
+
1. Fork it ( https://github.com/riskified/timeasure/fork )
|
204
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
205
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
206
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
207
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/lib/timeasure.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative 'timeasure/version'
|
2
|
+
require_relative 'timeasure/configuration'
|
3
|
+
require_relative 'timeasure/class_methods'
|
4
|
+
require_relative 'timeasure/measurement'
|
5
|
+
require_relative 'timeasure/profiling/manager'
|
6
|
+
|
7
|
+
module Timeasure
|
8
|
+
class << self
|
9
|
+
def configure
|
10
|
+
yield(configuration)
|
11
|
+
end
|
12
|
+
|
13
|
+
def configuration
|
14
|
+
@configuration ||= Configuration.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def included(base_class)
|
18
|
+
base_class.extend ClassMethods
|
19
|
+
|
20
|
+
instance_interceptor = const_set(instance_interceptor_name_for(base_class), interceptor_module_for(base_class))
|
21
|
+
class_interceptor = const_set(class_interceptor_name_for(base_class), interceptor_module_for(base_class))
|
22
|
+
|
23
|
+
return unless timeasure_enabled?
|
24
|
+
|
25
|
+
base_class.prepend instance_interceptor
|
26
|
+
base_class.singleton_class.prepend class_interceptor
|
27
|
+
end
|
28
|
+
|
29
|
+
def measure(klass_name: nil, method_name: nil, segment: nil, metadata: nil)
|
30
|
+
t0 = Time.now.utc
|
31
|
+
block_return_value = yield if block_given?
|
32
|
+
t1 = Time.now.utc
|
33
|
+
|
34
|
+
begin
|
35
|
+
measurement = Timeasure::Measurement.new(klass_name: klass_name.to_s, method_name: method_name.to_s,
|
36
|
+
segment: segment, metadata: metadata, t0: t0, t1: t1)
|
37
|
+
Timeasure.configuration.post_measuring_proc.call(measurement)
|
38
|
+
rescue => e
|
39
|
+
Timeasure.configuration.rescue_proc.call(e, klass_name)
|
40
|
+
end
|
41
|
+
|
42
|
+
block_return_value
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def instance_interceptor_name_for(base_class)
|
48
|
+
"#{base_class.timeasure_name}InstanceInterceptor"
|
49
|
+
end
|
50
|
+
|
51
|
+
def class_interceptor_name_for(base_class)
|
52
|
+
"#{base_class.timeasure_name}ClassInterceptor"
|
53
|
+
end
|
54
|
+
|
55
|
+
def interceptor_module_for(base_class)
|
56
|
+
Module.new do
|
57
|
+
@klass_name = base_class
|
58
|
+
|
59
|
+
def self.klass_name
|
60
|
+
@klass_name
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def timeasure_enabled?
|
66
|
+
configuration.enable_timeasure_proc.call
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Timeasure
|
2
|
+
module ClassMethods
|
3
|
+
def tracked_instance_methods(*method_names)
|
4
|
+
method_names.each do |method_name|
|
5
|
+
instance_interceptor = const_get("#{timeasure_name}InstanceInterceptor")
|
6
|
+
add_method_to_interceptor(instance_interceptor, method_name)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def tracked_class_methods(*method_names)
|
11
|
+
method_names.each do |method_name|
|
12
|
+
class_interceptor = const_get("#{timeasure_name}ClassInterceptor")
|
13
|
+
add_method_to_interceptor(class_interceptor, method_name)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def timeasure_name
|
18
|
+
name.gsub('::', '_')
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def add_method_to_interceptor(interceptor, method_name)
|
24
|
+
interceptor.class_eval do
|
25
|
+
define_method method_name do |*args, &block|
|
26
|
+
Timeasure.measure(klass_name: interceptor.klass_name.to_s, method_name: method_name.to_s) do
|
27
|
+
super(*args, &block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Timeasure
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :post_measuring_proc, :rescue_proc, :enable_timeasure_proc,
|
4
|
+
:reported_methods_handler_set_proc, :reported_methods_handler_get_proc
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@post_measuring_proc = lambda do |measurement|
|
8
|
+
# Enables the configuration of what to do with each method runtime measurement.
|
9
|
+
# By default it reports to Timeasure's Profiling Manager.
|
10
|
+
|
11
|
+
Timeasure::Profiling::Manager.report(measurement)
|
12
|
+
end
|
13
|
+
|
14
|
+
@rescue_proc = lambda do |e, klass|
|
15
|
+
# Enabled the configuration of post_measuring_proc rescue.
|
16
|
+
end
|
17
|
+
|
18
|
+
@enable_timeasure_proc = lambda do
|
19
|
+
# Enables toggling Timeasure's activation (e.g. for disabling Timeasure for RSpec).
|
20
|
+
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
@reported_methods_handler_set_proc = lambda do |reported_methods_handler|
|
25
|
+
# Enables configuring where to store the ReportedMethodsHandler instance.
|
26
|
+
# This proc will be called by Timeasure::Profiling::Manager.prepare.
|
27
|
+
# By default it stores the handler as a class instance variable (in Timeasure::Profiling::Manager)
|
28
|
+
|
29
|
+
@reported_methods_handler = reported_methods_handler
|
30
|
+
end
|
31
|
+
|
32
|
+
@reported_methods_handler_get_proc = lambda do
|
33
|
+
# Enables configuring where to fetch the ReportedMethodsHandler instance.
|
34
|
+
# This proc will be called by Timeasure::Profiling::Manager.report and Timeasure::Profiling::Manager.export.
|
35
|
+
# By default it fetches the handler from the class instance variable
|
36
|
+
# (see @reported_methods_handler_set_proc).
|
37
|
+
|
38
|
+
@reported_methods_handler
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Timeasure
|
2
|
+
class Measurement
|
3
|
+
attr_reader :klass_name, :method_name, :segment, :metadata, :t0, :t1
|
4
|
+
|
5
|
+
def initialize(klass_name:, method_name:, t0:, t1:, segment: nil, metadata: nil)
|
6
|
+
@klass_name = klass_name
|
7
|
+
@method_name = method_name
|
8
|
+
@t0 = t0
|
9
|
+
@t1 = t1
|
10
|
+
@segment = segment
|
11
|
+
@metadata = metadata
|
12
|
+
end
|
13
|
+
|
14
|
+
def runtime_in_milliseconds
|
15
|
+
(@t1 - @t0) * 1000
|
16
|
+
end
|
17
|
+
|
18
|
+
def full_path
|
19
|
+
@segment.nil? ? method_path : "#{method_path}:#{@segment}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def method_path
|
23
|
+
"#{@klass_name}##{@method_name}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require_relative 'reported_methods_handler'
|
3
|
+
require_relative 'reported_method'
|
4
|
+
|
5
|
+
module Timeasure
|
6
|
+
module Profiling
|
7
|
+
class Manager
|
8
|
+
class << self
|
9
|
+
def prepare
|
10
|
+
Timeasure.configuration.reported_methods_handler_set_proc.call(ReportedMethodsHandler.new)
|
11
|
+
end
|
12
|
+
|
13
|
+
def report(measurement)
|
14
|
+
handler = reported_methods_handler
|
15
|
+
handler.nil? ? warn_unprepared_handler : handler.report(measurement)
|
16
|
+
end
|
17
|
+
|
18
|
+
def export
|
19
|
+
handler = reported_methods_handler
|
20
|
+
handler.nil? ? warn_unprepared_handler : handler.export
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def reported_methods_handler
|
26
|
+
Timeasure.configuration.reported_methods_handler_get_proc.call
|
27
|
+
end
|
28
|
+
|
29
|
+
def warn_unprepared_handler
|
30
|
+
logger.warn("#{self} is not prepared. Call Timeasure::Profiling::Manager.prepare before trying to report measurements or export reported methods.")
|
31
|
+
end
|
32
|
+
|
33
|
+
def logger
|
34
|
+
@logger ||= Logger.new(STDOUT)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Timeasure
|
2
|
+
module Profiling
|
3
|
+
class ReportedMethod
|
4
|
+
attr_reader :klass_name, :method_name, :segment, :metadata, :full_path, :method_path, :runtime_sum, :call_count
|
5
|
+
|
6
|
+
def initialize(measurement)
|
7
|
+
@klass_name = measurement.klass_name
|
8
|
+
@method_name = measurement.method_name
|
9
|
+
@segment = measurement.segment
|
10
|
+
@metadata = measurement.metadata
|
11
|
+
@full_path = measurement.full_path
|
12
|
+
@method_path = measurement.method_path
|
13
|
+
|
14
|
+
@runtime_sum = 0
|
15
|
+
@call_count = 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def increment_runtime_sum(runtime)
|
19
|
+
@runtime_sum += runtime
|
20
|
+
end
|
21
|
+
|
22
|
+
def increment_call_count
|
23
|
+
@call_count += 1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Timeasure
|
2
|
+
module Profiling
|
3
|
+
class ReportedMethodsHandler
|
4
|
+
def initialize
|
5
|
+
@reported_methods = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def report(measurement)
|
9
|
+
initialize_path_for(measurement) if path_uninitialized_for(measurement)
|
10
|
+
|
11
|
+
@reported_methods[measurement.full_path].increment_runtime_sum(measurement.runtime_in_milliseconds)
|
12
|
+
@reported_methods[measurement.full_path].increment_call_count
|
13
|
+
end
|
14
|
+
|
15
|
+
def export
|
16
|
+
@reported_methods.values
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def path_uninitialized_for(measurement)
|
22
|
+
@reported_methods[measurement.full_path].nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize_path_for(measurement)
|
26
|
+
@reported_methods[measurement.full_path] = ReportedMethod.new(measurement)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/timeasure.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'timeasure/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'timeasure'
|
7
|
+
spec.version = Timeasure::VERSION
|
8
|
+
spec.authors = ['Eliav Lavi']
|
9
|
+
spec.email = ['eliav@riskified.com', 'eliavlavi@gmail.com']
|
10
|
+
spec.summary = 'Transparent method-level wrapper for profiling purposes'
|
11
|
+
spec.description = <<-DESCRIPTION
|
12
|
+
Timeasure is a Ruby gem that allows measuring the runtime of methods
|
13
|
+
without having to alter the code of the methods themselves.
|
14
|
+
DESCRIPTION
|
15
|
+
spec.homepage = 'https://github.com/riskified/timeasure'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.start_with? 'spec/' }
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^spec/})
|
20
|
+
spec.require_paths = ['lib', 'lib/timeasure', 'lib/timeasure/profiling']
|
21
|
+
|
22
|
+
spec.required_ruby_version = '>= 2.0'
|
23
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
24
|
+
spec.add_development_dependency 'coveralls'
|
25
|
+
spec.add_development_dependency 'rake', '~> 12.0'
|
26
|
+
spec.add_development_dependency 'rspec', '~> 3.6'
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: timeasure
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eliav Lavi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-02-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: coveralls
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '12.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '12.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.6'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.6'
|
69
|
+
description: |2
|
70
|
+
Timeasure is a Ruby gem that allows measuring the runtime of methods
|
71
|
+
without having to alter the code of the methods themselves.
|
72
|
+
email:
|
73
|
+
- eliav@riskified.com
|
74
|
+
- eliavlavi@gmail.com
|
75
|
+
executables: []
|
76
|
+
extensions: []
|
77
|
+
extra_rdoc_files: []
|
78
|
+
files:
|
79
|
+
- ".gitignore"
|
80
|
+
- ".rspec"
|
81
|
+
- ".rubocop.yml"
|
82
|
+
- Gemfile
|
83
|
+
- LICENSE.txt
|
84
|
+
- README.md
|
85
|
+
- Rakefile
|
86
|
+
- lib/timeasure.rb
|
87
|
+
- lib/timeasure/class_methods.rb
|
88
|
+
- lib/timeasure/configuration.rb
|
89
|
+
- lib/timeasure/measurement.rb
|
90
|
+
- lib/timeasure/profiling/manager.rb
|
91
|
+
- lib/timeasure/profiling/reported_method.rb
|
92
|
+
- lib/timeasure/profiling/reported_methods_handler.rb
|
93
|
+
- lib/timeasure/version.rb
|
94
|
+
- timeasure.gemspec
|
95
|
+
homepage: https://github.com/riskified/timeasure
|
96
|
+
licenses:
|
97
|
+
- MIT
|
98
|
+
metadata: {}
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
- lib/timeasure
|
104
|
+
- lib/timeasure/profiling
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '2.0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 2.6.14
|
118
|
+
signing_key:
|
119
|
+
specification_version: 4
|
120
|
+
summary: Transparent method-level wrapper for profiling purposes
|
121
|
+
test_files: []
|