pilfer 0.0.1.pre4 → 1.0.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 +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
|
+

|
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
|