pilfer 0.0.1.pre4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +162 -32
- data/lib/pilfer.rb +5 -0
- data/lib/pilfer/logger.rb +5 -4
- data/lib/pilfer/middleware.rb +10 -5
- data/lib/pilfer/profiler.rb +6 -4
- data/lib/pilfer/server.rb +103 -0
- data/lib/pilfer/version.rb +1 -1
- data/pilfer.gemspec +5 -1
- data/spec/integration_spec.rb +45 -0
- data/spec/pilfer/logger_spec.rb +19 -10
- data/spec/pilfer/middleware_spec.rb +4 -3
- data/spec/pilfer/profiler_spec.rb +19 -15
- metadata +9 -4
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MWJkZjU3Njk0N2IyMjZiNjJiOTZkMTQ4ZDk4MGQwZWU3MGQ4NGExNQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NmExMWFlYWIwNGU3ZTg5NTk1MWYyMTgxZWJhMmQwNDVhOWIwMDdiMw==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YWM2ZTNjNmZkYzA5YzlhNDIxM2RiYTA5NTkwOTZkN2VkODBkOGEwN2VmMmM1
|
10
|
+
ZjdiYjRjNzhkZWEzYzlmN2MzNmZhZTRiYjUxZjc0NWE2Y2UwMDAwYmViMTgw
|
11
|
+
ZjE1NDg3MzM0NzMyMGVjM2NiMGU4OGIxZjU0YTcxMDlhM2Y4NTY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
M2RhZDQ5MWFmMmNkYjI5MTM5NzM4ODA1NTFiODUxOTZhMWU5MWYwMmY5YjE0
|
14
|
+
NjA5OTE3ZDVlZmUzNWY3NDBhYjkzZmY0Y2NmMTBlYmMwMDY3ZTBiYjlmYmE3
|
15
|
+
NTVmNGZiOWE1NWFkYzBjMjQ4ZjFhMGViNDM2ODBiZTY1YjYxZWY=
|
data/README.md
CHANGED
@@ -1,30 +1,94 @@
|
|
1
1
|
# Pilfer
|
2
2
|
|
3
|
-
|
3
|
+
Profile Ruby code and find out _exactly_ how slow it runs.
|
4
|
+
|
5
|
+
Pilfer uses [rblineprof][] to measure how long each line of code takes to
|
6
|
+
execute and the number of times it was called.
|
7
|
+
|
8
|
+
![Pilfer Profile.png](http://cl.ly/image/2a1d332M2w05/Pilfer%20Profile.png)
|
9
|
+
|
10
|
+
Take a look at some [Pilfer profiles of the Bundler API site][bundler-pilfer].
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Using with Bundler is as simple as adding `pilfer` to your `Gemfile`.
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem 'pilfer', '~> 1.0.0'
|
18
|
+
```
|
19
|
+
|
20
|
+
Or install it locally like any other gem.
|
21
|
+
|
22
|
+
```bash
|
23
|
+
$ gem install pilfer
|
24
|
+
```
|
4
25
|
|
5
26
|
## Usage
|
6
27
|
|
7
28
|
Profile a block of code saving the report to the file `profile.log`.
|
8
29
|
|
30
|
+
```ruby
|
31
|
+
require 'pilfer'
|
32
|
+
|
33
|
+
reporter = Pilfer::Logger.new('pilfer.log')
|
34
|
+
profiler = Pilfer::Profiler.new(reporter)
|
35
|
+
profiler.profile('bubble sorting') do
|
36
|
+
array = (0..100).to_a.shuffle
|
37
|
+
bubble_sort array
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
Profile your Rack or Rails app using `Pilfer::Middleware`.
|
42
|
+
|
9
43
|
```ruby
|
10
44
|
reporter = Pilfer::Logger.new('pilfer.log')
|
11
45
|
profiler = Pilfer::Profiler.new(reporter)
|
12
|
-
profiler
|
46
|
+
use Pilfer::Middleware :profiler => profiler
|
13
47
|
```
|
14
48
|
|
15
|
-
The report
|
16
|
-
|
49
|
+
The profile report consists of the wall time and call count for each line of
|
50
|
+
code executed along with the total wall and CPU times for each file.
|
17
51
|
|
18
|
-
|
52
|
+
```
|
53
|
+
Profile start="2013-05-12 00:41:16 UTC" description="bubble sorting"
|
54
|
+
/Users/Larry/Sites/pilfer/sort.rb wall_time=42.5ms cpu_time=29.7ms
|
55
|
+
| require 'pilfer'
|
56
|
+
|
|
57
|
+
| def bubble_sort(container)
|
58
|
+
42.5ms ( 1) | loop do
|
59
|
+
| swapped = false
|
60
|
+
42.3ms ( 94) | (container.size-1).times do |i|
|
61
|
+
10.2ms ( 9400) | if (container[i] <=> container[i+1]) == 1
|
62
|
+
6.2ms ( 5092) | container[i], container[i+1] = container[i+1], container[i] # Swap
|
63
|
+
| swapped = true
|
64
|
+
| end
|
65
|
+
| end
|
66
|
+
| break unless swapped
|
67
|
+
| end
|
68
|
+
| container
|
69
|
+
| end
|
70
|
+
|
|
71
|
+
| reporter = Pilfer::Logger.new($stdout)
|
72
|
+
| profiler = Pilfer::Profiler.new(reporter)
|
73
|
+
| profiler.profile_files_matching(/sort\.rb/, 'bubble sorting') do
|
74
|
+
0.1ms ( 3) | array = (0..100).to_a.shuffle
|
75
|
+
42.5ms ( 1) | bubble_sort array
|
76
|
+
| end
|
77
|
+
```
|
19
78
|
|
20
79
|
### Step 1: Create a reporter
|
21
80
|
|
22
|
-
|
23
|
-
object.
|
81
|
+
Decide how you want line profiles to be reported. Profiles can be sent to a
|
82
|
+
[pilfer-server][] or written to a file path or `IO` object.
|
24
83
|
|
25
84
|
```ruby
|
26
|
-
|
85
|
+
# Send reports to a pilfer-server
|
86
|
+
reporter = Pilfer::Server.new('https://pilfer.com', 'my-pilfer-server-token')
|
87
|
+
|
88
|
+
# Append reports to a file
|
27
89
|
reporter = Pilfer::Logger.new('pilfer.log')
|
90
|
+
|
91
|
+
# Print reports to standard out
|
28
92
|
reporter = Pilfer::Logger.new($stdout)
|
29
93
|
```
|
30
94
|
|
@@ -33,12 +97,21 @@ the application root with `:app_root` to have it trimmed from reported file
|
|
33
97
|
paths.
|
34
98
|
|
35
99
|
```ruby
|
36
|
-
reporter = Pilfer::Logger.new(
|
100
|
+
reporter = Pilfer::Logger.new('pilfer.log')
|
101
|
+
# Profile start=2013-05-02 14:17:26 UTC
|
102
|
+
# /Sites/bundler-api/lib/bundler-api/web.rb wall_time=1009.5ms cpu_time=0.5ms
|
103
|
+
# ...
|
104
|
+
|
105
|
+
reporter = Pilfer::Logger.new('pilfer.log', :app_root => '/Sites/bundler-api/')
|
106
|
+
# Profile start=2013-05-02 14:17:26 UTC
|
107
|
+
# lib/bundler-api/web.rb wall_time=1009.5ms cpu_time=0.5ms
|
108
|
+
# ...
|
37
109
|
```
|
38
110
|
|
39
111
|
### Step 2: Create a profiler
|
40
112
|
|
41
|
-
|
113
|
+
A `Profiler` runs the line profiler and sends it to a reporter. Create one
|
114
|
+
passing the reporter created in the previous step.
|
42
115
|
|
43
116
|
```ruby
|
44
117
|
profiler = Pilfer::Profiler.new(reporter)
|
@@ -46,53 +119,81 @@ profiler = Pilfer::Profiler.new(reporter)
|
|
46
119
|
|
47
120
|
### Step 3: Profile a block of code
|
48
121
|
|
49
|
-
|
122
|
+
Use `Profiler#profile` to profile a block of code. Optionally, provide a
|
123
|
+
description of the code being profiling.
|
50
124
|
|
51
125
|
```ruby
|
52
|
-
profiler.profile
|
126
|
+
profiler.profile('bubble sorting') do
|
127
|
+
array = (0..100).to_a.shuffle
|
128
|
+
bubble_sort array
|
129
|
+
end
|
53
130
|
```
|
54
131
|
|
55
|
-
Every file that's executed by the block
|
56
|
-
application like gems and standard libraries
|
57
|
-
Use `#profile_files_matching`
|
58
|
-
|
132
|
+
Every file that's executed by the block including code outside the
|
133
|
+
application like gems and standard libraries will be included in the profile.
|
134
|
+
Use `#profile_files_matching` to limit profiling to files whose paths match a
|
135
|
+
regular expression.
|
59
136
|
|
60
137
|
```ruby
|
138
|
+
# Only profile Rails models
|
61
139
|
matcher = %r{^#{Regexp.escape(Rails.root.to_s)}/app/models}
|
62
|
-
profiler.profile_files_matching(matcher)
|
140
|
+
profiler.profile_files_matching(matcher, 'User.find_by_email') do
|
141
|
+
User.find_by_email('arthur@dent.com')
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
Additional arguments to `#profile` and `#profile_files_matching` will be
|
146
|
+
passed to the reporter. The `Pilfer::Server` reporter, for example, can submit
|
147
|
+
profiles asynchronously.
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
profiler.profile('bubble sorting', :submit => :async) do
|
151
|
+
array = (0..100).to_a.shuffle
|
152
|
+
bubble_sort array
|
153
|
+
end
|
63
154
|
```
|
64
155
|
|
65
|
-
##
|
156
|
+
## Extras
|
66
157
|
|
67
|
-
|
68
|
-
|
69
|
-
|
158
|
+
### Pilfer Server
|
159
|
+
|
160
|
+
[Pilfer Server][pilfer-server] is your own, personal service for collecting
|
161
|
+
and viewing line profiles gathered by Pilfer. Follow the
|
162
|
+
[Pilfer Server setup instructions][pilfer-server-readme] to stand up a new
|
163
|
+
server.
|
70
164
|
|
71
165
|
```ruby
|
72
|
-
reporter = Pilfer::Server.new('https://pilfer.com', '
|
166
|
+
reporter = Pilfer::Server.new('https://pilfer.com', 'my-pilfer-server-token')
|
167
|
+
profiler = Pilfer::Profiler.new(reporter)
|
168
|
+
profiler.profile('bubble sorting') do
|
169
|
+
array = (0..100).to_a.shuffle
|
170
|
+
bubble_sort array
|
171
|
+
end
|
73
172
|
```
|
74
173
|
|
75
|
-
|
174
|
+
### Rack Middleware
|
76
175
|
|
77
|
-
Profile your Rack or Rails app using `Pilfer::Middleware`.
|
176
|
+
Profile your entire Rack or Rails app using `Pilfer::Middleware`. Pass it a
|
177
|
+
`Profiler` created with a reporter as normal.
|
78
178
|
|
79
179
|
```ruby
|
80
|
-
reporter = Pilfer::
|
180
|
+
reporter = Pilfer::Server.new('https://pilfer.com', 'my-pilfer-server-token')
|
81
181
|
profiler = Pilfer::Profiler.new(reporter)
|
82
182
|
use Pilfer::Middleware :profiler => profiler
|
83
183
|
```
|
84
184
|
|
85
|
-
Restrict
|
86
|
-
`:files_matching
|
185
|
+
Restrict profiling to files matching a regular expression using the
|
186
|
+
`:files_matching` option. This calls `Profiler#profile_files_matching` using
|
187
|
+
the given regular expression.
|
87
188
|
|
88
189
|
```ruby
|
89
190
|
matcher = %r{^#{Regexp.escape(Rails.root.to_s)}/(app|config|lib|vendor/plugin)}
|
90
|
-
use Pilfer::Middleware, :
|
91
|
-
:
|
191
|
+
use Pilfer::Middleware, :profiler => profiler,
|
192
|
+
:files_matching => matcher
|
92
193
|
```
|
93
194
|
|
94
|
-
You
|
95
|
-
|
195
|
+
You almost certainly don't want to profile _every_ request. Provide a block to
|
196
|
+
determine if a profile should be run on the incoming request.
|
96
197
|
|
97
198
|
```ruby
|
98
199
|
use Pilfer::Middleware, :profiler => profiler do
|
@@ -104,10 +205,39 @@ end
|
|
104
205
|
The Rack environment is available to allow profiling on demand.
|
105
206
|
|
106
207
|
```ruby
|
208
|
+
# Profile requests containing the query string ?profile=true
|
107
209
|
use Pilfer::Middleware, :profiler => profiler do |env|
|
108
210
|
env.query_string.include? 'profile=true'
|
109
211
|
end
|
212
|
+
|
213
|
+
# Profile requests containing a header whose value matches a secret
|
214
|
+
use Pilfer::Middleware, :profiler => profiler do |env|
|
215
|
+
env['HTTP_PROFILE_AUTHORIZATION'] == 'super-secret'
|
216
|
+
end
|
110
217
|
```
|
111
218
|
|
219
|
+
## Supported Ruby Versions
|
220
|
+
|
221
|
+
This library is [tested against][travis] the following Ruby versions.
|
222
|
+
|
223
|
+
- MRI 1.9.3
|
224
|
+
- MRI 1.8.7
|
225
|
+
- REE
|
226
|
+
|
227
|
+
If you need a specific version supported, open and issue or send a pull
|
228
|
+
request.
|
229
|
+
|
230
|
+
## License
|
231
|
+
|
232
|
+
The MIT License (MIT)
|
233
|
+
|
234
|
+
Copyright (c) 2013 Eric Lindvall and Larry Marburger. See [LICENSE][] for
|
235
|
+
details.
|
236
|
+
|
112
237
|
|
113
|
-
[
|
238
|
+
[rblineprof]: https://github.com/tmm1/rblineprof
|
239
|
+
[bundler-pilfer]: https://pilfer.herokuapp.com/dashboard
|
240
|
+
[pilfer-server]: https://github.com/eric/pilfer-server
|
241
|
+
[pilfer-server-readme]: https://github.com/eric/pilfer-server#readme
|
242
|
+
[travis]: https://travis-ci.org/eric/pilfer
|
243
|
+
[license]: LICENSE
|
data/lib/pilfer.rb
ADDED
data/lib/pilfer/logger.rb
CHANGED
@@ -13,9 +13,9 @@ module Pilfer
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
def write(profile_data, profile_start)
|
16
|
+
def write(profile_data, profile_start, description, options = {})
|
17
17
|
profile = Pilfer::Profile.new(profile_data, profile_start)
|
18
|
-
print_report_banner profile_start
|
18
|
+
print_report_banner profile_start, description
|
19
19
|
profile.each do |path, data|
|
20
20
|
print_file_banner path, data
|
21
21
|
print_file_source_with_profile path, data
|
@@ -24,9 +24,10 @@ module Pilfer
|
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
|
-
def print_report_banner(profile_start)
|
27
|
+
def print_report_banner(profile_start, description)
|
28
28
|
formatted_start = profile_start.utc.strftime('%Y-%m-%d %H:%M:%S UTC')
|
29
|
-
logger.info
|
29
|
+
logger.info %{Profile start="#{formatted_start}" } +
|
30
|
+
%{description="#{description}"}
|
30
31
|
end
|
31
32
|
|
32
33
|
def print_file_banner(path, data)
|
data/lib/pilfer/middleware.rb
CHANGED
@@ -14,23 +14,28 @@ module Pilfer
|
|
14
14
|
|
15
15
|
def call(env)
|
16
16
|
if profile_guard.call(env)
|
17
|
-
run_profiler { app.call(env) }
|
17
|
+
run_profiler(request_description(env)) { app.call(env) }
|
18
18
|
else
|
19
19
|
app.call(env)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def run_profiler(&downstream)
|
23
|
+
def run_profiler(description, &downstream)
|
24
24
|
if file_matcher
|
25
|
-
profiler.profile_files_matching(file_matcher,
|
25
|
+
profiler.profile_files_matching(file_matcher, description,
|
26
|
+
:submit => :async, &downstream)
|
26
27
|
else
|
27
|
-
profiler.profile(&downstream)
|
28
|
+
profiler.profile(description, :submit => :async, &downstream)
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
31
32
|
def default_profiler
|
32
|
-
reporter = Pilfer::Logger.new($stdout
|
33
|
+
reporter = Pilfer::Logger.new($stdout)
|
33
34
|
Pilfer::Profiler.new(reporter)
|
34
35
|
end
|
36
|
+
|
37
|
+
def request_description(env)
|
38
|
+
"#{env["REQUEST_METHOD"]} #{env["PATH_INFO"]}"
|
39
|
+
end
|
35
40
|
end
|
36
41
|
end
|
data/lib/pilfer/profiler.rb
CHANGED
@@ -8,17 +8,19 @@ module Pilfer
|
|
8
8
|
@reporter = reporter
|
9
9
|
end
|
10
10
|
|
11
|
-
def profile(
|
12
|
-
profile_files_matching(/./,
|
11
|
+
def profile(*args, &app)
|
12
|
+
profile_files_matching(/./, *args, &app)
|
13
13
|
end
|
14
14
|
|
15
|
-
def profile_files_matching(matcher,
|
15
|
+
def profile_files_matching(matcher, description = nil,
|
16
|
+
reporter_options = {},
|
17
|
+
profiler = method(:lineprof),
|
16
18
|
start = Time.now, &app)
|
17
19
|
app_response = nil
|
18
20
|
profile = profiler.call(matcher) do
|
19
21
|
app_response = app.call
|
20
22
|
end
|
21
|
-
reporter.write profile, start
|
23
|
+
reporter.write profile, start, description, reporter_options
|
22
24
|
app_response
|
23
25
|
end
|
24
26
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
require 'pilfer/profile'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
module Pilfer
|
7
|
+
class Server
|
8
|
+
attr_accessor :uri, :token
|
9
|
+
|
10
|
+
def initialize(uri, token, options = {})
|
11
|
+
@uri = URI.parse(uri)
|
12
|
+
@token = token
|
13
|
+
@async = options[:async] || true
|
14
|
+
end
|
15
|
+
|
16
|
+
def write(profile_data, profile_start, description, options = {})
|
17
|
+
async = (options[:submit] || :sync) == :async
|
18
|
+
details = { 'hostname' => Socket.gethostname,
|
19
|
+
'pid' => Process.pid,
|
20
|
+
'description' => description,
|
21
|
+
'file_sources' => file_sources_for_profile(profile_data) }
|
22
|
+
|
23
|
+
payload = RbLineProfFormat.
|
24
|
+
profile_to_json(profile_data, profile_start).
|
25
|
+
merge(details)
|
26
|
+
|
27
|
+
if async
|
28
|
+
Thread.new(payload) do |payload|
|
29
|
+
submit_profile payload
|
30
|
+
end
|
31
|
+
else
|
32
|
+
submit_profile payload
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def submit_profile(payload)
|
39
|
+
request = Net::HTTP::Post.new('/api/profiles')
|
40
|
+
request.content_type = 'application/json'
|
41
|
+
request['Authorization'] = %{Token token="#{token}"}
|
42
|
+
request.body = JSON.generate(payload)
|
43
|
+
|
44
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
45
|
+
|
46
|
+
if uri.scheme == 'https'
|
47
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
48
|
+
http.use_ssl = true
|
49
|
+
store = OpenSSL::X509::Store.new
|
50
|
+
store.set_default_paths
|
51
|
+
http.cert_store = store
|
52
|
+
end
|
53
|
+
|
54
|
+
case (response = http.start {|http| http.request(request) })
|
55
|
+
when Net::HTTPSuccess, Net::HTTPRedirection
|
56
|
+
else
|
57
|
+
response.error!
|
58
|
+
end
|
59
|
+
rescue Exception => ex
|
60
|
+
$stdout.puts ex.message, ex.backtrace
|
61
|
+
end
|
62
|
+
|
63
|
+
def file_sources_for_profile(profile_data)
|
64
|
+
profile_data.each_with_object({}) {|(file, _), sources|
|
65
|
+
sources[file] = File.exists?(file) ? File.read(file) : nil
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Formatting a profile as JSON may eventually be provided by rblineprof.
|
72
|
+
class RbLineProfFormat
|
73
|
+
def self.profile_to_json(profile_data, profile_start)
|
74
|
+
files = profile_data.each_with_object({}) do |(file, lines), files|
|
75
|
+
profile_lines = lines[1..-1].
|
76
|
+
each_with_index.
|
77
|
+
each_with_object({}) do |(data, number), lines|
|
78
|
+
next unless data.any? {|datum| datum > 0 }
|
79
|
+
wall_time, cpu_time, calls = data
|
80
|
+
lines[number] = { 'wall_time' => wall_time,
|
81
|
+
'cpu_time' => cpu_time,
|
82
|
+
'calls' => calls }
|
83
|
+
end
|
84
|
+
|
85
|
+
total, child, exclusive, total_cpu, child_cpu, exclusive_cpu = lines[0]
|
86
|
+
|
87
|
+
files[file] = { 'total' => total,
|
88
|
+
'child' => child,
|
89
|
+
'exclusive' => exclusive,
|
90
|
+
'total_cpu' => total_cpu,
|
91
|
+
'child_cpu' => child_cpu,
|
92
|
+
'exclusive_cpu' => exclusive_cpu,
|
93
|
+
'lines' => profile_lines }
|
94
|
+
end
|
95
|
+
|
96
|
+
{
|
97
|
+
'profile' => {
|
98
|
+
'timestamp' => profile_start.to_i,
|
99
|
+
'files' => files
|
100
|
+
}
|
101
|
+
}
|
102
|
+
end
|
103
|
+
end
|
data/lib/pilfer/version.rb
CHANGED
data/pilfer.gemspec
CHANGED
@@ -5,12 +5,16 @@ require 'pilfer/version'
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = 'pilfer'
|
7
7
|
spec.version = Pilfer::VERSION
|
8
|
-
spec.summary = 'Look into your ruby with rblineprof'
|
9
8
|
spec.authors = ['Eric Lindvall', 'Larry Marburger']
|
10
9
|
spec.email = ['eric@sevenscale.com', 'larry@marburger.cc']
|
11
10
|
spec.homepage = 'https://github.com/eric/pilfer'
|
12
11
|
spec.license = 'MIT'
|
13
12
|
|
13
|
+
spec.summary = 'line-profiler for ruby and rack'
|
14
|
+
spec.description = 'pilfer uses rblineprof to measure how long each line ' +
|
15
|
+
'of code takes to execute and the number of times it ' +
|
16
|
+
'was called.'
|
17
|
+
|
14
18
|
spec.files = %w(Gemfile LICENSE README.md)
|
15
19
|
spec.files << 'pilfer.gemspec'
|
16
20
|
spec.files += Dir.glob('lib/**/*.rb')
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'stringio'
|
3
|
+
require 'pilfer'
|
4
|
+
|
5
|
+
describe Pilfer do
|
6
|
+
context 'reporting to a Pilfer::Logger' do
|
7
|
+
let(:reporter) { Pilfer::Logger.new(output) }
|
8
|
+
let(:output) { StringIO.new }
|
9
|
+
|
10
|
+
it 'reports profile' do
|
11
|
+
Pilfer::Profiler.new(reporter).
|
12
|
+
profile_files_matching(/integration_spec\.rb/, "testing") do
|
13
|
+
10.times do
|
14
|
+
sleep 0.01
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
lines = output.string.split("\n")
|
19
|
+
lines[0].should =~ %r{Profile start="[\d-]{10} [\d:]{8} UTC" description="testing"$}
|
20
|
+
|
21
|
+
wall_time = lines[1].match(/wall_time=([\d\.]+)/)[1].to_f
|
22
|
+
wall_time.should be_within(10).of(105)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'ignores optional reporter args' do
|
26
|
+
Pilfer::Profiler.new(reporter).
|
27
|
+
profile_files_matching(/integration_spec\.rb/, "testing",
|
28
|
+
:report => :async) do
|
29
|
+
10.times do
|
30
|
+
sleep 0.01
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
lines = output.string.split("\n")
|
35
|
+
lines[0].should =~ %r{Profile start="[\d-]{10} [\d:]{8} UTC" description="testing"$}
|
36
|
+
|
37
|
+
wall_time = lines[1].match(/wall_time=([\d\.]+)/)[1].to_f
|
38
|
+
wall_time.should be_within(10).of(105)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'reporting to a Pilfer::Server' do
|
43
|
+
it 'reports profile'
|
44
|
+
end
|
45
|
+
end
|
data/spec/pilfer/logger_spec.rb
CHANGED
@@ -10,9 +10,10 @@ describe Pilfer::Logger do
|
|
10
10
|
profile_content = File.read(profile_file).gsub('SPEC_ROOT', spec_root)
|
11
11
|
JSON.parse(profile_content)
|
12
12
|
}
|
13
|
-
let(:
|
14
|
-
let(:
|
15
|
-
let(:
|
13
|
+
let(:description) { "GET /" }
|
14
|
+
let(:reporter) { StringIO.new }
|
15
|
+
let(:start) { Time.at(42) }
|
16
|
+
let(:output) {
|
16
17
|
reporter.string.each_line.map {|line|
|
17
18
|
line.sub(/I, \[[^\]]+\] INFO -- : /, '')
|
18
19
|
}.join
|
@@ -27,7 +28,7 @@ describe Pilfer::Logger do
|
|
27
28
|
describe '#write' do
|
28
29
|
it 'writes profile to reporter' do
|
29
30
|
expected = <<-EOS
|
30
|
-
Profile start=1970-01-01 00:00:42 UTC
|
31
|
+
Profile start="1970-01-01 00:00:42 UTC" description="GET /"
|
31
32
|
#{spec_root}/files/hello.rb wall_time=0.0ms cpu_time=0.0ms
|
32
33
|
0.0ms ( 2) | print 'Hello '
|
33
34
|
#{spec_root}/files/test.rb wall_time=113.7ms cpu_time=5.3ms
|
@@ -49,19 +50,19 @@ Profile start=1970-01-01 00:00:42 UTC
|
|
49
50
|
| end
|
50
51
|
| end
|
51
52
|
EOS
|
52
|
-
Pilfer::Logger.new(reporter).write(profile, start)
|
53
|
+
Pilfer::Logger.new(reporter).write(profile, start, description)
|
53
54
|
output.should eq(expected)
|
54
55
|
end
|
55
56
|
|
56
57
|
it 'omits app root' do
|
57
58
|
Pilfer::Logger.new(reporter, :app_root => spec_root).
|
58
|
-
write(profile, start)
|
59
|
+
write(profile, start, description)
|
59
60
|
first_file.should eq('files/hello.rb')
|
60
61
|
end
|
61
62
|
|
62
63
|
it 'omits app root with trailing separator' do
|
63
64
|
Pilfer::Logger.new(reporter, :app_root => spec_root + '/').
|
64
|
-
write(profile, start)
|
65
|
+
write(profile, start, description)
|
65
66
|
first_file.should eq('files/hello.rb')
|
66
67
|
end
|
67
68
|
|
@@ -72,17 +73,25 @@ EOS
|
|
72
73
|
|
73
74
|
it 'omits the source of the nonexistent file' do
|
74
75
|
expected = <<-EOS
|
75
|
-
Profile start=1970-01-01 00:00:42 UTC
|
76
|
+
Profile start="1970-01-01 00:00:42 UTC" description="GET /"
|
76
77
|
(eval) wall_time=113.7ms cpu_time=5.3ms
|
77
78
|
EOS
|
78
|
-
Pilfer::Logger.new(reporter).write(profile, start)
|
79
|
+
Pilfer::Logger.new(reporter).write(profile, start, description)
|
79
80
|
output.should eq(expected)
|
80
81
|
end
|
81
82
|
end
|
82
83
|
|
83
84
|
it 'appends to the log file' do
|
84
|
-
3.times {
|
85
|
+
3.times {
|
86
|
+
Pilfer::Logger.new(reporter).write(profile, start, description)
|
87
|
+
}
|
85
88
|
output.scan('Profile start=').size.should eq(3)
|
86
89
|
end
|
90
|
+
|
91
|
+
it 'ignores optional options' do
|
92
|
+
Pilfer::Logger.new(reporter, :app_root => spec_root).
|
93
|
+
write(profile, start, description, :async => false)
|
94
|
+
output.should_not be_nil
|
95
|
+
end
|
87
96
|
end
|
88
97
|
end
|
@@ -12,7 +12,8 @@ describe Pilfer::Middleware do
|
|
12
12
|
|
13
13
|
it 'profiles and calls the downstream app' do
|
14
14
|
app.should_receive(:call).with(env).once
|
15
|
-
profiler.should_receive(:profile).
|
15
|
+
profiler.should_receive(:profile).
|
16
|
+
with("GET /", :submit => :async).and_yield
|
16
17
|
subject.call(env)
|
17
18
|
end
|
18
19
|
|
@@ -33,8 +34,8 @@ describe Pilfer::Middleware do
|
|
33
34
|
|
34
35
|
it 'passes file matcher to profiler and calls the downstream app' do
|
35
36
|
app.should_receive(:call).with(env).once
|
36
|
-
profiler.should_receive(:profile_files_matching).
|
37
|
-
and_yield
|
37
|
+
profiler.should_receive(:profile_files_matching).
|
38
|
+
with(file_matcher, "GET /", :submit => :async).and_yield
|
38
39
|
subject.call(env)
|
39
40
|
end
|
40
41
|
end
|
@@ -2,14 +2,17 @@ require 'helper'
|
|
2
2
|
require 'pilfer/profiler'
|
3
3
|
|
4
4
|
describe Pilfer::Profiler do
|
5
|
-
let(:reporter)
|
6
|
-
let(:
|
5
|
+
let(:reporter) { stub(:reporter, :write => nil) }
|
6
|
+
let(:reporter_options) { stub(:reporter_options) }
|
7
|
+
let(:description) { stub(:description) }
|
8
|
+
let(:profiler) { stub(:profiler, :call => :profiler_response) }
|
9
|
+
let(:start) { stub(:start) }
|
7
10
|
|
8
11
|
describe '#profile' do
|
9
12
|
it 'profiles all files by default' do
|
10
|
-
profiler = stub(:profiler)
|
11
13
|
profiler.should_receive(:call).with(/./)
|
12
|
-
Pilfer::Profiler.new(reporter).
|
14
|
+
Pilfer::Profiler.new(reporter).
|
15
|
+
profile(description, reporter_options, profiler) { }
|
13
16
|
end
|
14
17
|
|
15
18
|
it 'returns value of app' do
|
@@ -18,17 +21,17 @@ describe Pilfer::Profiler do
|
|
18
21
|
:profiler_response
|
19
22
|
}
|
20
23
|
|
21
|
-
response = Pilfer::Profiler.new(reporter).
|
22
|
-
:app_response
|
23
|
-
}
|
24
|
+
response = Pilfer::Profiler.new(reporter).
|
25
|
+
profile(description, reporter_options, profiler) { :app_response }
|
24
26
|
|
25
27
|
response.should eq(:app_response)
|
26
28
|
end
|
27
29
|
|
28
30
|
it 'writes profile to reporter' do
|
29
|
-
|
30
|
-
|
31
|
-
Pilfer::Profiler.new(reporter).
|
31
|
+
reporter.should_receive(:write).
|
32
|
+
with(:profiler_response, start, description, reporter_options)
|
33
|
+
Pilfer::Profiler.new(reporter).
|
34
|
+
profile(description, reporter_options, profiler, start) { }
|
32
35
|
end
|
33
36
|
end
|
34
37
|
|
@@ -36,17 +39,18 @@ describe Pilfer::Profiler do
|
|
36
39
|
let(:matcher) { stub(:matcher) }
|
37
40
|
|
38
41
|
it 'passes file matcher to profiler' do
|
39
|
-
profiler = stub(:profiler)
|
40
42
|
profiler.should_receive(:call).with(matcher)
|
41
43
|
Pilfer::Profiler.new(reporter).
|
42
|
-
profile_files_matching(matcher,
|
44
|
+
profile_files_matching(matcher, description, reporter_options,
|
45
|
+
profiler) { }
|
43
46
|
end
|
44
47
|
|
45
48
|
it 'writes profile to reporter' do
|
46
|
-
|
47
|
-
|
49
|
+
reporter.should_receive(:write).
|
50
|
+
with(:profiler_response, start, description, reporter_options)
|
48
51
|
Pilfer::Profiler.new(reporter).
|
49
|
-
profile_files_matching(matcher,
|
52
|
+
profile_files_matching(matcher, description, reporter_options,
|
53
|
+
profiler, start) { }
|
50
54
|
end
|
51
55
|
end
|
52
56
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pilfer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Lindvall
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rblineprof
|
@@ -39,7 +39,8 @@ dependencies:
|
|
39
39
|
- - ~>
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '1.0'
|
42
|
-
description:
|
42
|
+
description: pilfer uses rblineprof to measure how long each line of code takes to
|
43
|
+
execute and the number of times it was called.
|
43
44
|
email:
|
44
45
|
- eric@sevenscale.com
|
45
46
|
- larry@marburger.cc
|
@@ -55,10 +56,13 @@ files:
|
|
55
56
|
- lib/pilfer/middleware.rb
|
56
57
|
- lib/pilfer/profile.rb
|
57
58
|
- lib/pilfer/profiler.rb
|
59
|
+
- lib/pilfer/server.rb
|
58
60
|
- lib/pilfer/version.rb
|
61
|
+
- lib/pilfer.rb
|
59
62
|
- spec/files/hello.rb
|
60
63
|
- spec/files/test.rb
|
61
64
|
- spec/helper.rb
|
65
|
+
- spec/integration_spec.rb
|
62
66
|
- spec/pilfer/logger_spec.rb
|
63
67
|
- spec/pilfer/middleware_spec.rb
|
64
68
|
- spec/pilfer/profile_spec.rb
|
@@ -88,11 +92,12 @@ rubyforge_project:
|
|
88
92
|
rubygems_version: 2.0.3
|
89
93
|
signing_key:
|
90
94
|
specification_version: 4
|
91
|
-
summary:
|
95
|
+
summary: line-profiler for ruby and rack
|
92
96
|
test_files:
|
93
97
|
- spec/files/hello.rb
|
94
98
|
- spec/files/test.rb
|
95
99
|
- spec/helper.rb
|
100
|
+
- spec/integration_spec.rb
|
96
101
|
- spec/pilfer/logger_spec.rb
|
97
102
|
- spec/pilfer/middleware_spec.rb
|
98
103
|
- spec/pilfer/profile_spec.rb
|